sommark 5.0.5 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/async-hooks.js +19 -0
- package/core/evaluator.js +212 -22
- package/core/helpers/config-loader.js +29 -9
- package/core/helpers/lib.js +1 -1
- package/core/helpers/preprocessor.js +19 -0
- package/core/modules.js +59 -21
- package/core/parser.js +6 -3
- package/core/pathe-bundle.js +1 -0
- package/core/transpiler.js +130 -15
- package/core/validator.js +17 -4
- package/dist/sommark.browser.js +669 -214
- package/dist/sommark.browser.lite.js +458 -191
- package/dist/sommark.parser.js +6 -3
- package/esbuild.js +64 -0
- package/index.browser.js +4 -1
- package/index.js +102 -1
- package/index.shared.js +13 -2
- package/mappers/languages/markdown.js +99 -81
- package/mappers/languages/xml.js +76 -61
- package/mappers/shared/index.js +10 -1
- package/package.json +11 -3
- package/rollup.js +79 -0
- package/vite.js +20 -0
package/dist/sommark.browser.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
-
|
|
3
1
|
let _lazyMatch = () => { var __lib__=(()=>{var m=Object.defineProperty,V=Object.getOwnPropertyDescriptor,G=Object.getOwnPropertyNames,T=Object.prototype.hasOwnProperty,q=(r,e)=>{for(var n in e)m(r,n,{get:e[n],enumerable:true});},H=(r,e,n,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of G(e))!T.call(r,t)&&t!==n&&m(r,t,{get:()=>e[t],enumerable:!(a=V(e,t))||a.enumerable});return r},J=r=>H(m({},"__esModule",{value:true}),r),w={};q(w,{default:()=>re});var A=r=>Array.isArray(r),d=r=>typeof r=="function",Q=r=>r.length===0,W=r=>typeof r=="number",K=r=>typeof r=="object"&&r!==null,X=r=>r instanceof RegExp,b=r=>typeof r=="string",h=r=>r===void 0,Y=r=>{const e=new Map;return n=>{const a=e.get(n);if(a)return a;const t=r(n);return e.set(n,t),t}},rr=(r,e,n={})=>{const a={cache:{},input:r,index:0,indexMax:0,options:n,output:[]};if(v(e)(a)&&a.index===r.length)return a.output;throw new Error(`Failed to parse at index ${a.indexMax}`)},i=(r,e)=>A(r)?er(r,e):b(r)?ar(r,e):nr(r,e),er=(r,e)=>{const n={};for(const a of r){if(a.length!==1)throw new Error(`Invalid character: "${a}"`);const t=a.charCodeAt(0);n[t]=true;}return a=>{const t=a.index,o=a.input;for(;a.index<o.length&&o.charCodeAt(a.index)in n;)a.index+=1;const u=a.index;if(u>t){if(!h(e)&&!a.options.silent){const s=a.input.slice(t,u),c=d(e)?e(s,o,String(t)):e;h(c)||a.output.push(c);}a.indexMax=Math.max(a.indexMax,a.index);}return true}},nr=(r,e)=>{const n=r.source,a=r.flags.replace(/y|$/,"y"),t=new RegExp(n,a);return g(o=>{t.lastIndex=o.index;const u=t.exec(o.input);if(u){if(!h(e)&&!o.options.silent){const s=d(e)?e(...u,o.input,String(o.index)):e;h(s)||o.output.push(s);}return o.index+=u[0].length,o.indexMax=Math.max(o.indexMax,o.index),true}else return false})},ar=(r,e)=>n=>{if(n.input.startsWith(r,n.index)){if(!h(e)&&!n.options.silent){const t=d(e)?e(r,n.input,String(n.index)):e;h(t)||n.output.push(t);}return n.index+=r.length,n.indexMax=Math.max(n.indexMax,n.index),true}else return false},C=(r,e,n,a)=>{const t=v(r);return g(_(M(o=>{let u=0;for(;u<n;){const s=o.index;if(!t(o)||(u+=1,o.index===s))break}return u>=e})))},tr=(r,e)=>C(r,0,1),f=(r,e)=>C(r,0,1/0),x=(r,e)=>{const n=r.map(v);return g(_(M(a=>{for(let t=0,o=n.length;t<o;t++)if(!n[t](a))return false;return true})))},l=(r,e)=>{const n=r.map(v);return g(_(a=>{for(let t=0,o=n.length;t<o;t++)if(n[t](a))return true;return false}))},M=(r,e=false)=>{const n=v(r);return a=>{const t=a.index,o=a.output.length,u=n(a);return (!u||e)&&(a.index=t,a.output.length!==o&&(a.output.length=o)),u}},_=(r,e)=>{const n=v(r);return n},g=(()=>{let r=0;return e=>{const n=v(e),a=r+=1;return t=>{var o;if(t.options.memoization===false)return n(t);const u=t.index,s=(o=t.cache)[a]||(o[a]=new Map),c=s.get(u);if(c===false)return false;if(W(c))return t.index=c,true;if(c)return t.index=c.index,c.output?.length&&t.output.push(...c.output),true;{const Z=t.output.length;if(n(t)){const D=t.index,U=t.output.length;if(U>Z){const ee=t.output.slice(Z,U);s.set(u,{index:D,output:ee});}else s.set(u,D);return true}else return s.set(u,false),false}}}})(),E=r=>{let e;return n=>(e||(e=v(r())),e(n))},v=Y(r=>{if(d(r))return Q(r)?E(r):r;if(b(r)||X(r))return i(r);if(A(r))return x(r);if(K(r))return l(Object.values(r));throw new Error("Invalid rule")}),P="abcdefghijklmnopqrstuvwxyz",ir=r=>{let e="";for(;r>0;){const n=(r-1)%26;e=P[n]+e,r=Math.floor((r-1)/26);}return e},O=r=>{let e=0;for(let n=0,a=r.length;n<a;n++)e=e*26+P.indexOf(r[n])+1;return e},S=(r,e)=>{if(e<r)return S(e,r);const n=[];for(;r<=e;)n.push(r++);return n},or=(r,e,n)=>S(r,e).map(a=>String(a).padStart(n,"0")),R=(r,e)=>S(O(r),O(e)).map(ir),p=r=>r,z=r=>ur(e=>rr(e,r,{memoization:false}).join("")),ur=r=>{const e={};return n=>e[n]??(e[n]=r(n))},sr=i(/^\*\*\/\*$/,".*"),cr=i(/^\*\*\/(\*)?([ a-zA-Z0-9._-]+)$/,(r,e,n)=>`.*${e?"":"(?:^|/)"}${n.replaceAll(".","\\.")}`),lr=i(/^\*\*\/(\*)?([ a-zA-Z0-9._-]*)\{([ a-zA-Z0-9._-]+(?:,[ a-zA-Z0-9._-]+)*)\}$/,(r,e,n,a)=>`.*${e?"":"(?:^|/)"}${n.replaceAll(".","\\.")}(?:${a.replaceAll(",","|").replaceAll(".","\\.")})`),y=i(/\\./,p),pr=i(/[$.*+?^(){}[\]\|]/,r=>`\\${r}`),vr=i(/./,p),hr=i(/^(?:!!)*!(.*)$/,(r,e)=>`(?!^${L(e)}$).*?`),dr=i(/^(!!)+/,""),fr=l([hr,dr]),xr=i(/\/(\*\*\/)+/,"(?:/.+/|/)"),gr=i(/^(\*\*\/)+/,"(?:^|.*/)"),mr=i(/\/(\*\*)$/,"(?:/.*|$)"),_r=i(/\*\*/,".*"),j=l([xr,gr,mr,_r]),Sr=i(/\*\/(?!\*\*\/)/,"[^/]*/"),yr=i(/\*/,"[^/]*"),N=l([Sr,yr]),k=i("?","[^/]"),$r=i("[",p),wr=i("]",p),Ar=i(/[!^]/,"^/"),br=i(/[a-z]-[a-z]|[0-9]-[0-9]/i,p),Cr=i(/[$.*+?^(){}[\|]/,r=>`\\${r}`),Mr=i(/[^\]]/,p),Er=l([y,Cr,br,Mr]),B=x([$r,tr(Ar),f(Er),wr]),Pr=i("{","(?:"),Or=i("}",")"),Rr=i(/(\d+)\.\.(\d+)/,(r,e,n)=>or(+e,+n,Math.min(e.length,n.length)).join("|")),zr=i(/([a-z]+)\.\.([a-z]+)/,(r,e,n)=>R(e,n).join("|")),jr=i(/([A-Z]+)\.\.([A-Z]+)/,(r,e,n)=>R(e.toLowerCase(),n.toLowerCase()).join("|").toUpperCase()),Nr=l([Rr,zr,jr]),I=x([Pr,Nr,Or]),kr=i("{","(?:"),Br=i("}",")"),Ir=i(",","|"),Fr=i(/[$.*+?^(){[\]\|]/,r=>`\\${r}`),Lr=i(/[^}]/,p),Zr=E(()=>F),Dr=l([j,N,k,B,I,Zr,y,Fr,Ir,Lr]),F=x([kr,f(Dr),Br]),Ur=f(l([sr,cr,lr,fr,j,N,k,B,I,F,y,pr,vr])),Vr=Ur,Gr=z(Vr),L=Gr,Tr=i(/\\./,p),qr=i(/./,p),Hr=i(/\*\*\*+/,"*"),Jr=i(/([^/{[(!])\*\*/,(r,e)=>`${e}*`),Qr=i(/(^|.)\*\*(?=[^*/)\]}])/,(r,e)=>`${e}*`),Wr=f(l([Tr,Hr,Jr,Qr,qr])),Kr=Wr,Xr=z(Kr),Yr=Xr,$=(r,e)=>{const n=Array.isArray(r)?r:[r];if(!n.length)return false;const a=n.map($.compile),t=n.every(s=>/(\/(?:\*\*)?|\[\/\])$/.test(s)),o=e.replace(/[\\\/]+/g,"/").replace(/\/$/,t?"/":"");return a.some(s=>s.test(o))};$.compile=r=>new RegExp(`^${L(Yr(r))}$`,"s");var re=$;return J(w)})();
|
|
4
2
|
return __lib__.default || __lib__; };
|
|
5
3
|
let _match;
|
|
@@ -1476,6 +1474,7 @@ function makeBlockNode() {
|
|
|
1476
1474
|
structure: "Block",
|
|
1477
1475
|
id: "",
|
|
1478
1476
|
props: {},
|
|
1477
|
+
directives: {},
|
|
1479
1478
|
body: [],
|
|
1480
1479
|
depth: 0,
|
|
1481
1480
|
range: {
|
|
@@ -1961,9 +1960,11 @@ function parseBlock(tokens, i, filename = null, placeholders = {}, variables = {
|
|
|
1961
1960
|
i = valueIndex;
|
|
1962
1961
|
|
|
1963
1962
|
// Store Argument
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1963
|
+
if (k && k.startsWith("smark-")) {
|
|
1964
|
+
blockNode.directives[k.slice(6)] = v; // strip "smark-" prefix
|
|
1965
|
+
} else {
|
|
1966
|
+
blockNode.props[String(argIndex++)] = v;
|
|
1967
|
+
if (k) blockNode.props[k] = v;
|
|
1967
1968
|
}
|
|
1968
1969
|
k = "";
|
|
1969
1970
|
v = "";
|
|
@@ -8627,7 +8628,7 @@ function registerHostSettings(settings) {
|
|
|
8627
8628
|
hostSettings = settings || {};
|
|
8628
8629
|
}
|
|
8629
8630
|
|
|
8630
|
-
const version = "5.0
|
|
8631
|
+
const version = "5.2.0";
|
|
8631
8632
|
|
|
8632
8633
|
const SomMark$1 = {
|
|
8633
8634
|
version,
|
|
@@ -8678,14 +8679,21 @@ const SomMark$1 = {
|
|
|
8678
8679
|
// Freeze the entire Standard Library to make it completely immutable and tamper-proof
|
|
8679
8680
|
Object.freeze(SomMark$1);
|
|
8680
8681
|
|
|
8681
|
-
// Each transpile() call gets its own isolated EvaluatorState stack via async context.
|
|
8682
|
-
|
|
8682
|
+
const patheBundleCode = "let _lazyMatch = () => { var __lib__=(()=>{var m=Object.defineProperty,V=Object.getOwnPropertyDescriptor,G=Object.getOwnPropertyNames,T=Object.prototype.hasOwnProperty,q=(r,e)=>{for(var n in e)m(r,n,{get:e[n],enumerable:true});},H=(r,e,n,a)=>{if(e&&typeof e==\"object\"||typeof e==\"function\")for(let t of G(e))!T.call(r,t)&&t!==n&&m(r,t,{get:()=>e[t],enumerable:!(a=V(e,t))||a.enumerable});return r},J=r=>H(m({},\"__esModule\",{value:true}),r),w={};q(w,{default:()=>re});var A=r=>Array.isArray(r),d=r=>typeof r==\"function\",Q=r=>r.length===0,W=r=>typeof r==\"number\",K=r=>typeof r==\"object\"&&r!==null,X=r=>r instanceof RegExp,b=r=>typeof r==\"string\",h=r=>r===void 0,Y=r=>{const e=new Map;return n=>{const a=e.get(n);if(a)return a;const t=r(n);return e.set(n,t),t}},rr=(r,e,n={})=>{const a={cache:{},input:r,index:0,indexMax:0,options:n,output:[]};if(v(e)(a)&&a.index===r.length)return a.output;throw new Error(`Failed to parse at index ${a.indexMax}`)},i=(r,e)=>A(r)?er(r,e):b(r)?ar(r,e):nr(r,e),er=(r,e)=>{const n={};for(const a of r){if(a.length!==1)throw new Error(`Invalid character: \"${a}\"`);const t=a.charCodeAt(0);n[t]=true;}return a=>{const t=a.index,o=a.input;for(;a.index<o.length&&o.charCodeAt(a.index)in n;)a.index+=1;const u=a.index;if(u>t){if(!h(e)&&!a.options.silent){const s=a.input.slice(t,u),c=d(e)?e(s,o,String(t)):e;h(c)||a.output.push(c);}a.indexMax=Math.max(a.indexMax,a.index);}return true}},nr=(r,e)=>{const n=r.source,a=r.flags.replace(/y|$/,\"y\"),t=new RegExp(n,a);return g(o=>{t.lastIndex=o.index;const u=t.exec(o.input);if(u){if(!h(e)&&!o.options.silent){const s=d(e)?e(...u,o.input,String(o.index)):e;h(s)||o.output.push(s);}return o.index+=u[0].length,o.indexMax=Math.max(o.indexMax,o.index),true}else return false})},ar=(r,e)=>n=>{if(n.input.startsWith(r,n.index)){if(!h(e)&&!n.options.silent){const t=d(e)?e(r,n.input,String(n.index)):e;h(t)||n.output.push(t);}return n.index+=r.length,n.indexMax=Math.max(n.indexMax,n.index),true}else return false},C=(r,e,n,a)=>{const t=v(r);return g(_(M(o=>{let u=0;for(;u<n;){const s=o.index;if(!t(o)||(u+=1,o.index===s))break}return u>=e})))},tr=(r,e)=>C(r,0,1),f=(r,e)=>C(r,0,1/0),x=(r,e)=>{const n=r.map(v);return g(_(M(a=>{for(let t=0,o=n.length;t<o;t++)if(!n[t](a))return false;return true})))},l=(r,e)=>{const n=r.map(v);return g(_(a=>{for(let t=0,o=n.length;t<o;t++)if(n[t](a))return true;return false}))},M=(r,e=false)=>{const n=v(r);return a=>{const t=a.index,o=a.output.length,u=n(a);return (!u||e)&&(a.index=t,a.output.length!==o&&(a.output.length=o)),u}},_=(r,e)=>{const n=v(r);return n},g=(()=>{let r=0;return e=>{const n=v(e),a=r+=1;return t=>{var o;if(t.options.memoization===false)return n(t);const u=t.index,s=(o=t.cache)[a]||(o[a]=new Map),c=s.get(u);if(c===false)return false;if(W(c))return t.index=c,true;if(c)return t.index=c.index,c.output?.length&&t.output.push(...c.output),true;{const Z=t.output.length;if(n(t)){const D=t.index,U=t.output.length;if(U>Z){const ee=t.output.slice(Z,U);s.set(u,{index:D,output:ee});}else s.set(u,D);return true}else return s.set(u,false),false}}}})(),E=r=>{let e;return n=>(e||(e=v(r())),e(n))},v=Y(r=>{if(d(r))return Q(r)?E(r):r;if(b(r)||X(r))return i(r);if(A(r))return x(r);if(K(r))return l(Object.values(r));throw new Error(\"Invalid rule\")}),P=\"abcdefghijklmnopqrstuvwxyz\",ir=r=>{let e=\"\";for(;r>0;){const n=(r-1)%26;e=P[n]+e,r=Math.floor((r-1)/26);}return e},O=r=>{let e=0;for(let n=0,a=r.length;n<a;n++)e=e*26+P.indexOf(r[n])+1;return e},S=(r,e)=>{if(e<r)return S(e,r);const n=[];for(;r<=e;)n.push(r++);return n},or=(r,e,n)=>S(r,e).map(a=>String(a).padStart(n,\"0\")),R=(r,e)=>S(O(r),O(e)).map(ir),p=r=>r,z=r=>ur(e=>rr(e,r,{memoization:false}).join(\"\")),ur=r=>{const e={};return n=>e[n]??(e[n]=r(n))},sr=i(/^\\*\\*\\/\\*$/,\".*\"),cr=i(/^\\*\\*\\/(\\*)?([ a-zA-Z0-9._-]+)$/,(r,e,n)=>`.*${e?\"\":\"(?:^|/)\"}${n.replaceAll(\".\",\"\\\\.\")}`),lr=i(/^\\*\\*\\/(\\*)?([ a-zA-Z0-9._-]*)\\{([ a-zA-Z0-9._-]+(?:,[ a-zA-Z0-9._-]+)*)\\}$/,(r,e,n,a)=>`.*${e?\"\":\"(?:^|/)\"}${n.replaceAll(\".\",\"\\\\.\")}(?:${a.replaceAll(\",\",\"|\").replaceAll(\".\",\"\\\\.\")})`),y=i(/\\\\./,p),pr=i(/[$.*+?^(){}[\\]\\|]/,r=>`\\\\${r}`),vr=i(/./,p),hr=i(/^(?:!!)*!(.*)$/,(r,e)=>`(?!^${L(e)}$).*?`),dr=i(/^(!!)+/,\"\"),fr=l([hr,dr]),xr=i(/\\/(\\*\\*\\/)+/,\"(?:/.+/|/)\"),gr=i(/^(\\*\\*\\/)+/,\"(?:^|.*/)\"),mr=i(/\\/(\\*\\*)$/,\"(?:/.*|$)\"),_r=i(/\\*\\*/,\".*\"),j=l([xr,gr,mr,_r]),Sr=i(/\\*\\/(?!\\*\\*\\/)/,\"[^/]*/\"),yr=i(/\\*/,\"[^/]*\"),N=l([Sr,yr]),k=i(\"?\",\"[^/]\"),$r=i(\"[\",p),wr=i(\"]\",p),Ar=i(/[!^]/,\"^/\"),br=i(/[a-z]-[a-z]|[0-9]-[0-9]/i,p),Cr=i(/[$.*+?^(){}[\\|]/,r=>`\\\\${r}`),Mr=i(/[^\\]]/,p),Er=l([y,Cr,br,Mr]),B=x([$r,tr(Ar),f(Er),wr]),Pr=i(\"{\",\"(?:\"),Or=i(\"}\",\")\"),Rr=i(/(\\d+)\\.\\.(\\d+)/,(r,e,n)=>or(+e,+n,Math.min(e.length,n.length)).join(\"|\")),zr=i(/([a-z]+)\\.\\.([a-z]+)/,(r,e,n)=>R(e,n).join(\"|\")),jr=i(/([A-Z]+)\\.\\.([A-Z]+)/,(r,e,n)=>R(e.toLowerCase(),n.toLowerCase()).join(\"|\").toUpperCase()),Nr=l([Rr,zr,jr]),I=x([Pr,Nr,Or]),kr=i(\"{\",\"(?:\"),Br=i(\"}\",\")\"),Ir=i(\",\",\"|\"),Fr=i(/[$.*+?^(){[\\]\\|]/,r=>`\\\\${r}`),Lr=i(/[^}]/,p),Zr=E(()=>F),Dr=l([j,N,k,B,I,Zr,y,Fr,Ir,Lr]),F=x([kr,f(Dr),Br]),Ur=f(l([sr,cr,lr,fr,j,N,k,B,I,F,y,pr,vr])),Vr=Ur,Gr=z(Vr),L=Gr,Tr=i(/\\\\./,p),qr=i(/./,p),Hr=i(/\\*\\*\\*+/,\"*\"),Jr=i(/([^/{[(!])\\*\\*/,(r,e)=>`${e}*`),Qr=i(/(^|.)\\*\\*(?=[^*/)\\]}])/,(r,e)=>`${e}*`),Wr=f(l([Tr,Hr,Jr,Qr,qr])),Kr=Wr,Xr=z(Kr),Yr=Xr,$=(r,e)=>{const n=Array.isArray(r)?r:[r];if(!n.length)return false;const a=n.map($.compile),t=n.every(s=>/(\\/(?:\\*\\*)?|\\[\\/\\])$/.test(s)),o=e.replace(/[\\\\\\/]+/g,\"/\").replace(/\\/$/,t?\"/\":\"\");return a.some(s=>s.test(o))};$.compile=r=>new RegExp(`^${L(Yr(r))}$`,\"s\");var re=$;return J(w)})();\n return __lib__.default || __lib__; };\nlet _match;\nconst zeptomatch = (path, pattern) => {\n if (!_match) {\n _match = _lazyMatch();\n _lazyMatch = null;\n }\n return _match(path, pattern);\n};\n\nconst _DRIVE_LETTER_START_RE = /^[A-Za-z]:\\//;\nfunction normalizeWindowsPath(input = \"\") {\n if (!input) {\n return input;\n }\n return input.replace(/\\\\/g, \"/\").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());\n}\n\nconst _UNC_REGEX = /^[/\\\\]{2}/;\nconst _IS_ABSOLUTE_RE = /^[/\\\\](?![/\\\\])|^[/\\\\]{2}(?!\\.)|^[A-Za-z]:[/\\\\]/;\nconst _DRIVE_LETTER_RE = /^[A-Za-z]:$/;\nconst _ROOT_FOLDER_RE = /^\\/([A-Za-z]:)?$/;\nconst _EXTNAME_RE = /.(\\.[^./]+|\\.)$/;\nconst _PATH_ROOT_RE = /^[/\\\\]|^[a-zA-Z]:[/\\\\]/;\nconst sep = \"/\";\nconst normalize = function(path) {\n if (path.length === 0) {\n return \".\";\n }\n path = normalizeWindowsPath(path);\n const isUNCPath = path.match(_UNC_REGEX);\n const isPathAbsolute = isAbsolute(path);\n const trailingSeparator = path[path.length - 1] === \"/\";\n path = normalizeString(path, !isPathAbsolute);\n if (path.length === 0) {\n if (isPathAbsolute) {\n return \"/\";\n }\n return trailingSeparator ? \"./\" : \".\";\n }\n if (trailingSeparator) {\n path += \"/\";\n }\n if (_DRIVE_LETTER_RE.test(path)) {\n path += \"/\";\n }\n if (isUNCPath) {\n if (!isPathAbsolute) {\n return `//./${path}`;\n }\n return `//${path}`;\n }\n return isPathAbsolute && !isAbsolute(path) ? `/${path}` : path;\n};\nconst join = function(...segments) {\n let path = \"\";\n for (const seg of segments) {\n if (!seg) {\n continue;\n }\n if (path.length > 0) {\n const pathTrailing = path[path.length - 1] === \"/\";\n const segLeading = seg[0] === \"/\";\n const both = pathTrailing && segLeading;\n if (both) {\n path += seg.slice(1);\n } else {\n path += pathTrailing || segLeading ? seg : `/${seg}`;\n }\n } else {\n path += seg;\n }\n }\n return normalize(path);\n};\nfunction cwd() {\n if (typeof process !== \"undefined\" && typeof process.cwd === \"function\") {\n return process.cwd().replace(/\\\\/g, \"/\");\n }\n return \"/\";\n}\nconst resolve = function(...arguments_) {\n arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));\n let resolvedPath = \"\";\n let resolvedAbsolute = false;\n for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {\n const path = index >= 0 ? arguments_[index] : cwd();\n if (!path || path.length === 0) {\n continue;\n }\n resolvedPath = `${path}/${resolvedPath}`;\n resolvedAbsolute = isAbsolute(path);\n }\n resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);\n if (resolvedAbsolute && !isAbsolute(resolvedPath)) {\n return `/${resolvedPath}`;\n }\n return resolvedPath.length > 0 ? resolvedPath : \".\";\n};\nfunction normalizeString(path, allowAboveRoot) {\n let res = \"\";\n let lastSegmentLength = 0;\n let lastSlash = -1;\n let dots = 0;\n let char = null;\n for (let index = 0; index <= path.length; ++index) {\n if (index < path.length) {\n char = path[index];\n } else if (char === \"/\") {\n break;\n } else {\n char = \"/\";\n }\n if (char === \"/\") {\n if (lastSlash === index - 1 || dots === 1) ; else if (dots === 2) {\n if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== \".\" || res[res.length - 2] !== \".\") {\n if (res.length > 2) {\n const lastSlashIndex = res.lastIndexOf(\"/\");\n if (lastSlashIndex === -1) {\n res = \"\";\n lastSegmentLength = 0;\n } else {\n res = res.slice(0, lastSlashIndex);\n lastSegmentLength = res.length - 1 - res.lastIndexOf(\"/\");\n }\n lastSlash = index;\n dots = 0;\n continue;\n } else if (res.length > 0) {\n res = \"\";\n lastSegmentLength = 0;\n lastSlash = index;\n dots = 0;\n continue;\n }\n }\n if (allowAboveRoot) {\n res += res.length > 0 ? \"/..\" : \"..\";\n lastSegmentLength = 2;\n }\n } else {\n if (res.length > 0) {\n res += `/${path.slice(lastSlash + 1, index)}`;\n } else {\n res = path.slice(lastSlash + 1, index);\n }\n lastSegmentLength = index - lastSlash - 1;\n }\n lastSlash = index;\n dots = 0;\n } else if (char === \".\" && dots !== -1) {\n ++dots;\n } else {\n dots = -1;\n }\n }\n return res;\n}\nconst isAbsolute = function(p) {\n return _IS_ABSOLUTE_RE.test(p);\n};\nconst toNamespacedPath = function(p) {\n return normalizeWindowsPath(p);\n};\nconst extname = function(p) {\n if (p === \"..\") return \"\";\n const match = _EXTNAME_RE.exec(normalizeWindowsPath(p));\n return match && match[1] || \"\";\n};\nconst relative = function(from, to) {\n const _from = resolve(from).replace(_ROOT_FOLDER_RE, \"$1\").split(\"/\");\n const _to = resolve(to).replace(_ROOT_FOLDER_RE, \"$1\").split(\"/\");\n if (_to[0][1] === \":\" && _from[0][1] === \":\" && _from[0] !== _to[0]) {\n return _to.join(\"/\");\n }\n const _fromCopy = [..._from];\n for (const segment of _fromCopy) {\n if (_to[0] !== segment) {\n break;\n }\n _from.shift();\n _to.shift();\n }\n return [..._from.map(() => \"..\"), ..._to].join(\"/\");\n};\nconst dirname = function(p) {\n const segments = normalizeWindowsPath(p).replace(/\\/$/, \"\").split(\"/\").slice(0, -1);\n if (segments.length === 1 && _DRIVE_LETTER_RE.test(segments[0])) {\n segments[0] += \"/\";\n }\n return segments.join(\"/\") || (isAbsolute(p) ? \"/\" : \".\");\n};\nconst format = function(p) {\n const ext = p.ext ? p.ext.startsWith(\".\") ? p.ext : `.${p.ext}` : \"\";\n const segments = [p.root, p.dir, p.base ?? (p.name ?? \"\") + ext].filter(\n Boolean\n );\n return normalizeWindowsPath(\n p.root ? resolve(...segments) : segments.join(\"/\")\n );\n};\nconst basename = function(p, extension) {\n const segments = normalizeWindowsPath(p).split(\"/\");\n let lastSegment = \"\";\n for (let i = segments.length - 1; i >= 0; i--) {\n const val = segments[i];\n if (val) {\n lastSegment = val;\n break;\n }\n }\n return extension && lastSegment.endsWith(extension) ? lastSegment.slice(0, -extension.length) : lastSegment;\n};\nconst parse = function(p) {\n const root = _PATH_ROOT_RE.exec(p)?.[0]?.replace(/\\\\/g, \"/\") || \"\";\n const base = basename(p);\n const extension = extname(base);\n return {\n root,\n dir: dirname(p),\n base,\n ext: extension,\n name: base.slice(0, base.length - extension.length)\n };\n};\nconst matchesGlob = (path, pattern) => {\n return zeptomatch(pattern, normalize(path));\n};\n\nconst _path = {\n __proto__: null,\n basename: basename,\n dirname: dirname,\n extname: extname,\n format: format,\n isAbsolute: isAbsolute,\n join: join,\n matchesGlob: matchesGlob,\n normalize: normalize,\n normalizeString: normalizeString,\n parse: parse,\n relative: relative,\n resolve: resolve,\n sep: sep,\n toNamespacedPath: toNamespacedPath\n};\n\nconst delimiter = /* @__PURE__ */ (() => globalThis.process?.platform === \"win32\" ? \";\" : \":\")();\nconst _platforms = { posix: void 0, win32: void 0 };\nconst mix = (del = delimiter) => {\n return new Proxy(_path, {\n get(_, prop) {\n if (prop === \"delimiter\") return del;\n if (prop === \"posix\") return posix;\n if (prop === \"win32\") return win32;\n return _platforms[prop] || _path[prop];\n }\n });\n};\nconst posix = /* @__PURE__ */ mix(\":\");\nconst win32 = /* @__PURE__ */ mix(\";\");\n\nvar pathe = /*#__PURE__*/Object.freeze({\n __proto__: null,\n basename: basename,\n default: posix,\n delimiter: delimiter,\n dirname: dirname,\n extname: extname,\n format: format,\n isAbsolute: isAbsolute,\n join: join,\n matchesGlob: matchesGlob,\n normalize: normalize,\n normalizeString: normalizeString,\n parse: parse,\n posix: posix,\n relative: relative,\n resolve: resolve,\n sep: sep,\n toNamespacedPath: toNamespacedPath,\n win32: win32\n});\n\nglobalThis.pathHandler = pathe;\n";
|
|
8683
|
+
|
|
8684
|
+
// Set by index.js (Node.js) or index.browser.js (shim) — never imported directly.
|
|
8685
|
+
let evaluatorStorage = null;
|
|
8686
|
+
|
|
8687
|
+
function setDefaultAsyncLocalStorage$1(cls) {
|
|
8688
|
+
evaluatorStorage = cls ? new cls() : null;
|
|
8689
|
+
}
|
|
8683
8690
|
|
|
8684
8691
|
/**
|
|
8685
8692
|
* Runs fn inside an isolated evaluator context.
|
|
8686
8693
|
* Concurrent transpile() calls each get their own stack — no cross-contamination.
|
|
8687
8694
|
*/
|
|
8688
8695
|
function withEvaluator(fn) {
|
|
8696
|
+
if (!evaluatorStorage) return fn();
|
|
8689
8697
|
return evaluatorStorage.run([], fn);
|
|
8690
8698
|
}
|
|
8691
8699
|
|
|
@@ -8814,7 +8822,7 @@ const customFetchAdapter = async (input, init, security = {}) => {
|
|
|
8814
8822
|
};
|
|
8815
8823
|
};
|
|
8816
8824
|
|
|
8817
|
-
const customCompileAdapter = async (src, options, parentSecurity = {}) => {
|
|
8825
|
+
const customCompileAdapter = async (src, options, parentSecurity = {}, parentFs = null, parentBaseDir = null) => {
|
|
8818
8826
|
const maxDepth = parentSecurity?.maxDepth ?? 5;
|
|
8819
8827
|
if (globalCompilationDepth >= maxDepth) {
|
|
8820
8828
|
throw new Error(`Recursion Guard: Maximum Smark compilation depth exceeded (limit is ${maxDepth}).`);
|
|
@@ -8830,7 +8838,9 @@ const customCompileAdapter = async (src, options, parentSecurity = {}) => {
|
|
|
8830
8838
|
...cleanOptions,
|
|
8831
8839
|
src,
|
|
8832
8840
|
format: cleanOptions.format || "html",
|
|
8833
|
-
security: parentSecurity
|
|
8841
|
+
security: parentSecurity,
|
|
8842
|
+
fs: parentFs ?? undefined,
|
|
8843
|
+
baseDir: cleanOptions.baseDir || parentBaseDir || undefined,
|
|
8834
8844
|
};
|
|
8835
8845
|
const sm = new compilerClass(compilerOptions);
|
|
8836
8846
|
return await sm.transpile();
|
|
@@ -8843,6 +8853,7 @@ const customCompileAdapter = async (src, options, parentSecurity = {}) => {
|
|
|
8843
8853
|
registerHostCompile(customCompileAdapter);
|
|
8844
8854
|
|
|
8845
8855
|
let defaultFs$1 = null;
|
|
8856
|
+
let defaultEnv = null;
|
|
8846
8857
|
let quickJSInstance = null;
|
|
8847
8858
|
async function getQuickJSModule() {
|
|
8848
8859
|
if (!quickJSInstance) {
|
|
@@ -8866,6 +8877,16 @@ function objectToHandle(context, obj) {
|
|
|
8866
8877
|
return result.unwrap();
|
|
8867
8878
|
}
|
|
8868
8879
|
|
|
8880
|
+
function isPlainData(value, seen = new Set()) {
|
|
8881
|
+
if (value === null || value === undefined) return true;
|
|
8882
|
+
if (typeof value === "function") return false;
|
|
8883
|
+
if (typeof value !== "object") return true;
|
|
8884
|
+
if (seen.has(value)) return false;
|
|
8885
|
+
seen.add(value);
|
|
8886
|
+
if (Array.isArray(value)) return value.every(v => isPlainData(v, seen));
|
|
8887
|
+
return Object.values(value).every(v => isPlainData(v, seen));
|
|
8888
|
+
}
|
|
8889
|
+
|
|
8869
8890
|
function expose(context, vars, pendingDeferreds) {
|
|
8870
8891
|
for (const [key, value] of Object.entries(vars)) {
|
|
8871
8892
|
let handle;
|
|
@@ -8960,6 +8981,7 @@ class EvaluatorState {
|
|
|
8960
8981
|
} else {
|
|
8961
8982
|
this.baseDir = "/";
|
|
8962
8983
|
}
|
|
8984
|
+
this.rootDir = settings?.instance?.cwd || this.baseDir;
|
|
8963
8985
|
this.scopes = [{}];
|
|
8964
8986
|
this.dynamicTagsStack = [new Map()];
|
|
8965
8987
|
this.security = security;
|
|
@@ -8986,15 +9008,39 @@ class EvaluatorState {
|
|
|
8986
9008
|
});
|
|
8987
9009
|
|
|
8988
9010
|
this.expose({
|
|
9011
|
+
__hostEnv: (key) => {
|
|
9012
|
+
if (defaultEnv === null) {
|
|
9013
|
+
throw new Error(
|
|
9014
|
+
"[SomMark] SomMark.env() is not available in browser mode.\n" +
|
|
9015
|
+
"Environment variables are a server-side concept.\n" +
|
|
9016
|
+
"Read env values at build time and pass them as placeholders instead."
|
|
9017
|
+
);
|
|
9018
|
+
}
|
|
9019
|
+
const allowlist = this.security?.env;
|
|
9020
|
+
if (!Array.isArray(allowlist) || !allowlist.includes(key)) return undefined;
|
|
9021
|
+
return defaultEnv[key] ?? undefined;
|
|
9022
|
+
},
|
|
8989
9023
|
__hostSomMarkVersion: SomMark$1.version,
|
|
8990
9024
|
__hostSomMarkSettings: () => {
|
|
8991
|
-
const
|
|
8992
|
-
|
|
8993
|
-
|
|
8994
|
-
|
|
8995
|
-
|
|
9025
|
+
const s = SomMark$1.settings;
|
|
9026
|
+
return JSON.stringify({
|
|
9027
|
+
format: s.format ?? null,
|
|
9028
|
+
dev: s.dev ?? false,
|
|
9029
|
+
removeComments:s.removeComments ?? false,
|
|
9030
|
+
allowRaw: s.allowRaw ?? true,
|
|
9031
|
+
dualOutput: s.dualOutput ?? false,
|
|
9032
|
+
webOutputs: s.webOutputs ?? false,
|
|
9033
|
+
});
|
|
9034
|
+
},
|
|
8996
9035
|
__hostCompile: async (src, options) => {
|
|
8997
|
-
return await customCompileAdapter(src, options, this.security);
|
|
9036
|
+
return await customCompileAdapter(src, options, this.security, this.nodeFs, this.baseDir);
|
|
9037
|
+
},
|
|
9038
|
+
__hostLexer: (src, filename) => {
|
|
9039
|
+
return JSON.stringify(lexer(src, filename || "anonymous"));
|
|
9040
|
+
},
|
|
9041
|
+
__hostParser: (src, filename) => {
|
|
9042
|
+
const tokens = lexer(src, filename || "anonymous");
|
|
9043
|
+
return JSON.stringify(parser(tokens, filename || "anonymous"));
|
|
8998
9044
|
},
|
|
8999
9045
|
__hostFetch: async (input, initStr) => {
|
|
9000
9046
|
const init = initStr ? JSON.parse(initStr) : undefined;
|
|
@@ -9022,6 +9068,59 @@ class EvaluatorState {
|
|
|
9022
9068
|
const payload = JSON.parse(payloadStr);
|
|
9023
9069
|
return await target.render.call(this.mapperFile, payload);
|
|
9024
9070
|
},
|
|
9071
|
+
__hostFileRead: async (filePath) => {
|
|
9072
|
+
if (!this.nodeFs) {
|
|
9073
|
+
throw new Error(
|
|
9074
|
+
"[SomMark] fileHandler is not available in browser mode.\n" +
|
|
9075
|
+
"File access is a server-side concept."
|
|
9076
|
+
);
|
|
9077
|
+
}
|
|
9078
|
+
const abs = posix.resolve(this.rootDir, filePath);
|
|
9079
|
+
if (!abs.startsWith(this.rootDir)) {
|
|
9080
|
+
throw new Error(
|
|
9081
|
+
`[SomMark] fileHandler.read: path traversal outside project root is not allowed.\n` +
|
|
9082
|
+
`Attempted path: ${abs}`
|
|
9083
|
+
);
|
|
9084
|
+
}
|
|
9085
|
+
return this.nodeFs.readFile(abs, "utf-8");
|
|
9086
|
+
},
|
|
9087
|
+
__hostFileExists: async (filePath) => {
|
|
9088
|
+
if (!this.nodeFs) return false;
|
|
9089
|
+
const abs = posix.resolve(this.rootDir, filePath);
|
|
9090
|
+
if (!abs.startsWith(this.rootDir)) return false;
|
|
9091
|
+
return this.nodeFs.exists(abs);
|
|
9092
|
+
},
|
|
9093
|
+
__hostFileGlob: async (pattern) => {
|
|
9094
|
+
if (!this.nodeFs) throw new Error("[SomMark] fileHandler.glob is not available in browser mode.\nFile access is a server-side concept.");
|
|
9095
|
+
if (!this.nodeFs.glob) throw new Error("[SomMark] fileHandler.glob requires Node.js 22 or later.");
|
|
9096
|
+
const files = await this.nodeFs.glob(pattern, { cwd: this.rootDir });
|
|
9097
|
+
return JSON.stringify(files);
|
|
9098
|
+
},
|
|
9099
|
+
__hostFileLastModified: async (filePath) => {
|
|
9100
|
+
if (!this.nodeFs) throw new Error("[SomMark] fileHandler.lastModified is not available in browser mode.");
|
|
9101
|
+
const abs = posix.resolve(this.rootDir, filePath);
|
|
9102
|
+
if (!abs.startsWith(this.rootDir)) throw new Error("[SomMark] fileHandler.lastModified: path traversal outside project root is not allowed.");
|
|
9103
|
+
const stat = await this.nodeFs.stat(abs);
|
|
9104
|
+
return stat.mtimeMs;
|
|
9105
|
+
},
|
|
9106
|
+
__hostFileStat: async (filePath) => {
|
|
9107
|
+
if (!this.nodeFs) throw new Error("[SomMark] fileHandler.stat is not available in browser mode.\nFile access is a server-side concept.");
|
|
9108
|
+
const abs = posix.resolve(this.rootDir, filePath);
|
|
9109
|
+
if (!abs.startsWith(this.rootDir)) throw new Error(`[SomMark] fileHandler.stat: path traversal outside project root is not allowed.\nAttempted path: ${abs}`);
|
|
9110
|
+
try {
|
|
9111
|
+
const s = await this.nodeFs.stat(abs);
|
|
9112
|
+
return JSON.stringify({
|
|
9113
|
+
size: s.size,
|
|
9114
|
+
mtime: s.mtimeMs,
|
|
9115
|
+
ctime: s.ctimeMs,
|
|
9116
|
+
atime: s.atimeMs,
|
|
9117
|
+
isFile: s.isFile(),
|
|
9118
|
+
isDirectory: s.isDirectory(),
|
|
9119
|
+
});
|
|
9120
|
+
} catch {
|
|
9121
|
+
return null;
|
|
9122
|
+
}
|
|
9123
|
+
},
|
|
9025
9124
|
__allowRaw: this.security.allowRaw !== false
|
|
9026
9125
|
});
|
|
9027
9126
|
|
|
@@ -9174,6 +9273,18 @@ class EvaluatorState {
|
|
|
9174
9273
|
}
|
|
9175
9274
|
return await __hostCompile(src, options);
|
|
9176
9275
|
},
|
|
9276
|
+
lexer: (src, filename) => {
|
|
9277
|
+
if (typeof src !== "string") {
|
|
9278
|
+
throw new Error("SomMark.lexer Error: Source must be a string.");
|
|
9279
|
+
}
|
|
9280
|
+
return JSON.parse(__hostLexer(src, filename));
|
|
9281
|
+
},
|
|
9282
|
+
parser: (src, filename) => {
|
|
9283
|
+
if (typeof src !== "string") {
|
|
9284
|
+
throw new Error("SomMark.parser Error: Source must be a string.");
|
|
9285
|
+
}
|
|
9286
|
+
return JSON.parse(__hostParser(src, filename));
|
|
9287
|
+
},
|
|
9177
9288
|
raw: (html) => {
|
|
9178
9289
|
if (typeof __allowRaw !== "undefined" && !__allowRaw) {
|
|
9179
9290
|
throw new Error("Security Error: SomMark.raw is disabled in this environment.");
|
|
@@ -9197,6 +9308,12 @@ class EvaluatorState {
|
|
|
9197
9308
|
throw new Error("SomMark.static Error: Argument must be a string.");
|
|
9198
9309
|
}
|
|
9199
9310
|
return globalThis.eval(expr);
|
|
9311
|
+
},
|
|
9312
|
+
env: (key) => {
|
|
9313
|
+
if (typeof key !== "string" || !key) {
|
|
9314
|
+
throw new Error("SomMark.env Error: Key must be a non-empty string.");
|
|
9315
|
+
}
|
|
9316
|
+
return __hostEnv(key);
|
|
9200
9317
|
}
|
|
9201
9318
|
};
|
|
9202
9319
|
|
|
@@ -9208,6 +9325,20 @@ class EvaluatorState {
|
|
|
9208
9325
|
configurable: false
|
|
9209
9326
|
});
|
|
9210
9327
|
|
|
9328
|
+
Object.defineProperty(globalThis, "Smark", {
|
|
9329
|
+
value: SomMark,
|
|
9330
|
+
writable: false,
|
|
9331
|
+
configurable: false
|
|
9332
|
+
});
|
|
9333
|
+
|
|
9334
|
+
globalThis.fileHandler = Object.freeze({
|
|
9335
|
+
read: async (path) => await __hostFileRead(path),
|
|
9336
|
+
exists: async (path) => await __hostFileExists(path),
|
|
9337
|
+
glob: async (pattern) => JSON.parse(await __hostFileGlob(pattern)),
|
|
9338
|
+
lastModified: async (path) => await __hostFileLastModified(path),
|
|
9339
|
+
stat: async (path) => { const r = await __hostFileStat(path); return r ? JSON.parse(r) : null; },
|
|
9340
|
+
});
|
|
9341
|
+
|
|
9211
9342
|
delete globalThis.fetch;
|
|
9212
9343
|
delete globalThis.process;
|
|
9213
9344
|
`);
|
|
@@ -9219,15 +9350,27 @@ class EvaluatorState {
|
|
|
9219
9350
|
}
|
|
9220
9351
|
setupRes.value.dispose();
|
|
9221
9352
|
|
|
9222
|
-
|
|
9353
|
+
const patheRes = this.context.evalCode(patheBundleCode);
|
|
9354
|
+
if (patheRes.error) {
|
|
9355
|
+
patheRes.error.dispose();
|
|
9356
|
+
} else {
|
|
9357
|
+
patheRes.value.dispose();
|
|
9358
|
+
}
|
|
9359
|
+
|
|
9360
|
+
// Configure module loader using virtual FS implementation.
|
|
9361
|
+
// The normalizer resolves every import to an absolute path so the module
|
|
9362
|
+
// cache key is always absolute — <smark> (the eval module name) can never
|
|
9363
|
+
// be reached by any user import regardless of what the file is named.
|
|
9223
9364
|
this.runtime.setModuleLoader((moduleName) => {
|
|
9224
9365
|
try {
|
|
9225
9366
|
const isRaw = moduleName.endsWith("?raw");
|
|
9226
9367
|
const cleanModuleName = isRaw ? moduleName.slice(0, -4) : moduleName;
|
|
9227
|
-
|
|
9228
|
-
|
|
9368
|
+
// moduleName is already an absolute path (supplied by the normalizer below),
|
|
9369
|
+
// so resolve() is a no-op for absolute paths and a safe fallback for URLs.
|
|
9370
|
+
const resolvedPath = /^https?:\/\//.test(cleanModuleName)
|
|
9371
|
+
? cleanModuleName
|
|
9229
9372
|
: posix.resolve(this.baseDir, cleanModuleName);
|
|
9230
|
-
|
|
9373
|
+
|
|
9231
9374
|
const fsImpl = this.settings?.fs || this.settings?.instance?.fs || this.nodeFs;
|
|
9232
9375
|
if (!fsImpl) {
|
|
9233
9376
|
throw new Error("No filesystem implementation available.");
|
|
@@ -9260,6 +9403,22 @@ class EvaluatorState {
|
|
|
9260
9403
|
} catch (err) {
|
|
9261
9404
|
throw err;
|
|
9262
9405
|
}
|
|
9406
|
+
}, (baseName, moduleName) => {
|
|
9407
|
+
// Resolve every import to an absolute path so no user import can ever
|
|
9408
|
+
// normalize to <smark> (or any other virtual eval module name).
|
|
9409
|
+
const isRaw = moduleName.endsWith("?raw");
|
|
9410
|
+
const clean = isRaw ? moduleName.slice(0, -4) : moduleName;
|
|
9411
|
+
if (/^https?:\/\//.test(clean)) return moduleName;
|
|
9412
|
+
const baseDir = (baseName === "<smark>" || !posix.isAbsolute(baseName))
|
|
9413
|
+
? this.baseDir
|
|
9414
|
+
: (/^https?:\/\//.test(baseName) ? baseName : posix.dirname(baseName));
|
|
9415
|
+
let resolved;
|
|
9416
|
+
if (/^https?:\/\//.test(baseDir)) {
|
|
9417
|
+
resolved = new URL(clean, baseDir).href;
|
|
9418
|
+
} else {
|
|
9419
|
+
resolved = posix.resolve(baseDir, clean);
|
|
9420
|
+
}
|
|
9421
|
+
return isRaw ? resolved + "?raw" : resolved;
|
|
9263
9422
|
});
|
|
9264
9423
|
}
|
|
9265
9424
|
|
|
@@ -9410,9 +9569,24 @@ class EvaluatorState {
|
|
|
9410
9569
|
|
|
9411
9570
|
inject(vars) {
|
|
9412
9571
|
if (!this.context) return;
|
|
9572
|
+
const safe = {};
|
|
9573
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
9574
|
+
if (typeof value === "function") {
|
|
9575
|
+
const src = value.toString();
|
|
9576
|
+
if (src.includes("SomMark.")) {
|
|
9577
|
+
console.warn(`[SomMark] variables.${key}: references 'SomMark' which bundlers may rename. Use 'Smark' instead.`);
|
|
9578
|
+
}
|
|
9579
|
+
const res = this.context.evalCode(`globalThis[${JSON.stringify(key)}] = ${src}`);
|
|
9580
|
+
if (res.error) res.error.dispose();
|
|
9581
|
+
else res.value.dispose();
|
|
9582
|
+
continue;
|
|
9583
|
+
}
|
|
9584
|
+
if (!isPlainData(value)) continue;
|
|
9585
|
+
safe[key] = value;
|
|
9586
|
+
}
|
|
9413
9587
|
const currentScope = this.scopes[this.scopes.length - 1];
|
|
9414
|
-
Object.assign(currentScope,
|
|
9415
|
-
this.expose(
|
|
9588
|
+
Object.assign(currentScope, safe);
|
|
9589
|
+
this.expose(safe);
|
|
9416
9590
|
}
|
|
9417
9591
|
|
|
9418
9592
|
async execute(code, baseDir = null) {
|
|
@@ -9482,7 +9656,16 @@ class EvaluatorState {
|
|
|
9482
9656
|
}
|
|
9483
9657
|
}
|
|
9484
9658
|
} catch (err) {
|
|
9485
|
-
//
|
|
9659
|
+
// Parse failed as a statement — try as a parenthesised expression.
|
|
9660
|
+
// This handles object/array literals like {a: 1} or [1, 2] which are
|
|
9661
|
+
// ambiguous in statement context but valid when wrapped in parens.
|
|
9662
|
+
try {
|
|
9663
|
+
const trimmed = code.trim();
|
|
9664
|
+
parse$1(`(${trimmed})`, { ecmaVersion: 'latest', sourceType: 'module' });
|
|
9665
|
+
finalCode = `export default (${trimmed});`;
|
|
9666
|
+
} catch {
|
|
9667
|
+
// Give up — let QuickJS surface the error.
|
|
9668
|
+
}
|
|
9486
9669
|
}
|
|
9487
9670
|
|
|
9488
9671
|
if (autoExportedNames.length > 0 && !hasExplicitExports) {
|
|
@@ -9496,7 +9679,7 @@ class EvaluatorState {
|
|
|
9496
9679
|
|
|
9497
9680
|
let result;
|
|
9498
9681
|
if (isModule) {
|
|
9499
|
-
const evalRes = this.context.evalCode(finalCode, "
|
|
9682
|
+
const evalRes = this.context.evalCode(finalCode, "<smark>", { type: 'module' });
|
|
9500
9683
|
if (evalRes.error) {
|
|
9501
9684
|
const err = this.context.dump(evalRes.error);
|
|
9502
9685
|
evalRes.error.dispose();
|
|
@@ -9565,7 +9748,7 @@ class EvaluatorState {
|
|
|
9565
9748
|
}
|
|
9566
9749
|
|
|
9567
9750
|
const defaultValue = this.context.dump(resolvedDefaultHandle);
|
|
9568
|
-
|
|
9751
|
+
|
|
9569
9752
|
if (isPromise) {
|
|
9570
9753
|
resolvedDefaultHandle.dispose();
|
|
9571
9754
|
}
|
|
@@ -9605,7 +9788,7 @@ class EvaluatorState {
|
|
|
9605
9788
|
result = res;
|
|
9606
9789
|
}
|
|
9607
9790
|
} else {
|
|
9608
|
-
const evalRes = this.context.evalCode(code, "
|
|
9791
|
+
const evalRes = this.context.evalCode(code, "<smark>");
|
|
9609
9792
|
if (evalRes.error) {
|
|
9610
9793
|
const err = this.context.dump(evalRes.error);
|
|
9611
9794
|
evalRes.error.dispose();
|
|
@@ -9641,7 +9824,7 @@ class EvaluatorState {
|
|
|
9641
9824
|
return result;
|
|
9642
9825
|
} catch (error) {
|
|
9643
9826
|
const stack = error.stack || "";
|
|
9644
|
-
const match = stack.match(/
|
|
9827
|
+
const match = stack.match(/__smark__\.js:(\d+):(\d+)/) || stack.match(/:(\d+):(\d+)/);
|
|
9645
9828
|
|
|
9646
9829
|
const err = new Error(error.message || error);
|
|
9647
9830
|
if (match) {
|
|
@@ -9704,6 +9887,14 @@ class Evaluator {
|
|
|
9704
9887
|
defaultFs$1 = fs;
|
|
9705
9888
|
}
|
|
9706
9889
|
|
|
9890
|
+
setDefaultEnv(env) {
|
|
9891
|
+
defaultEnv = env;
|
|
9892
|
+
}
|
|
9893
|
+
|
|
9894
|
+
setDefaultAsyncLocalStorage(cls) {
|
|
9895
|
+
setDefaultAsyncLocalStorage$1(cls);
|
|
9896
|
+
}
|
|
9897
|
+
|
|
9707
9898
|
get active() {
|
|
9708
9899
|
const stack = this._getStack();
|
|
9709
9900
|
if (stack.length === 0) {
|
|
@@ -9916,8 +10107,27 @@ async function preprocessRuntimeLogic(code, filename = null, security = {}, inst
|
|
|
9916
10107
|
if (filename && filename !== "anonymous") {
|
|
9917
10108
|
baseDir = posix.dirname(posix.resolve(filename));
|
|
9918
10109
|
}
|
|
10110
|
+
|
|
10111
|
+
// Block absolute paths — path.resolve would ignore baseDir entirely
|
|
10112
|
+
if (posix.isAbsolute(argValue)) {
|
|
10113
|
+
transpilerError([
|
|
10114
|
+
`<$red:Security Error:$> Absolute import paths are not allowed: <$magenta:${argValue}$>{line}`,
|
|
10115
|
+
`<$yellow:Use a path relative to the template file, e.g.$> <$green:SomMark.import("./data.json")$> <$yellow:or$> <$green:SomMark.import("../shared/data.json")$><$yellow:.$>{line}`,
|
|
10116
|
+
`<$yellow:Base directory:$> <$blue:${baseDir}$>{line}`
|
|
10117
|
+
]);
|
|
10118
|
+
}
|
|
10119
|
+
|
|
9919
10120
|
const resolvedPath = posix.resolve(baseDir, argValue);
|
|
9920
10121
|
|
|
10122
|
+
// Block path traversal — resolved path must stay inside baseDir
|
|
10123
|
+
const safeBases = baseDir.endsWith(posix.sep) ? baseDir : baseDir + posix.sep;
|
|
10124
|
+
if (!resolvedPath.startsWith(safeBases) && resolvedPath !== baseDir) {
|
|
10125
|
+
transpilerError([
|
|
10126
|
+
`<$red:Security Error:$> Import path escapes the project directory: <$magenta:${argValue}$>{line}`,
|
|
10127
|
+
`<$yellow:Resolved Path:$> <$blue:${resolvedPath}$>{line}`
|
|
10128
|
+
]);
|
|
10129
|
+
}
|
|
10130
|
+
|
|
9921
10131
|
const fsImpl = instance?.fs || await getNodeFs();
|
|
9922
10132
|
|
|
9923
10133
|
// File presence validation
|
|
@@ -10013,7 +10223,7 @@ function warnDroppedVariables(variables) {
|
|
|
10013
10223
|
} else if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
10014
10224
|
for (const [nestedKey, nestedVal] of Object.entries(value)) {
|
|
10015
10225
|
if (typeof nestedVal === "function") {
|
|
10016
|
-
console.warn(`[SomMark] variables.${key}.${nestedKey}
|
|
10226
|
+
console.warn(`[SomMark] variables.${key}.${nestedKey}: nested functions inside objects are not supported. Define it as a top-level function instead: variables.${nestedKey}`);
|
|
10017
10227
|
} else if (nestedVal === undefined) {
|
|
10018
10228
|
console.warn(`[SomMark] variables.${key}.${nestedKey} is undefined and will be ignored.`);
|
|
10019
10229
|
}
|
|
@@ -10030,6 +10240,7 @@ const randomBytesHex = (size) => {
|
|
|
10030
10240
|
|
|
10031
10241
|
const BODY_PLACEHOLDER = `SOMMARKBODYPLACEHOLDER${randomBytesHex(8)}SOMMARK`;
|
|
10032
10242
|
|
|
10243
|
+
|
|
10033
10244
|
/**
|
|
10034
10245
|
* Extracts all plain text from a node and its children.
|
|
10035
10246
|
*
|
|
@@ -10119,15 +10330,28 @@ async function generateOutput(ast, i, format, mapper_file, security = {}, parent
|
|
|
10119
10330
|
const out = (result !== undefined && typeof result !== "object") ? String(result) : "";
|
|
10120
10331
|
return mapper_file ? mapper_file.text(out) : out;
|
|
10121
10332
|
} catch (err) {
|
|
10333
|
+
const line = node.range?.start?.line + 1 || 1;
|
|
10122
10334
|
transpilerError([
|
|
10123
10335
|
`<$red:Logic Error:$> ${err.message}{line}`,
|
|
10124
|
-
`<$yellow:Code:$> <$blue:${node.code}$>{line}
|
|
10336
|
+
`<$yellow:Code:$> <$blue:${node.code}$>{line}`,
|
|
10337
|
+
`at line <$yellow:${line}$>{line}`
|
|
10125
10338
|
]);
|
|
10126
10339
|
}
|
|
10127
10340
|
}
|
|
10128
10341
|
|
|
10129
10342
|
if (node.type === FOR_EACH) {
|
|
10130
10343
|
const transpiledArgs = await transpileArgs(node.props);
|
|
10344
|
+
|
|
10345
|
+
if (!node.props || (node.props[0] === undefined && node.props["items"] === undefined)) {
|
|
10346
|
+
const line = node.range?.start?.line + 1 || 1;
|
|
10347
|
+
transpilerError([
|
|
10348
|
+
`<$red:Missing Prop Error in [for-each]:$>{line}`,
|
|
10349
|
+
`[for-each] requires an array as its first prop, e.g. [for-each = \${ array }\$]{line}`,
|
|
10350
|
+
`at line <$yellow:${line}$>{line}`
|
|
10351
|
+
]);
|
|
10352
|
+
return "";
|
|
10353
|
+
}
|
|
10354
|
+
|
|
10131
10355
|
const items = mapper_file ? mapper_file.safeArg({ props: transpiledArgs, index: 0, key: "items", fallBack: [] }) : [];
|
|
10132
10356
|
|
|
10133
10357
|
if (!Array.isArray(items)) {
|
|
@@ -10141,11 +10365,11 @@ async function generateOutput(ast, i, format, mapper_file, security = {}, parent
|
|
|
10141
10365
|
}
|
|
10142
10366
|
|
|
10143
10367
|
const asVar = transpiledArgs.as || "value";
|
|
10144
|
-
if (asVar === "i") {
|
|
10368
|
+
if (asVar === "i" || asVar === "length") {
|
|
10145
10369
|
const line = node.range?.start?.line + 1 || 1;
|
|
10146
10370
|
transpilerError([
|
|
10147
10371
|
`<$red:Reserved Variable Error in [for-each]:$>{line}`,
|
|
10148
|
-
`'
|
|
10372
|
+
`'${asVar}' is a reserved variable name.{N}Use a different name for the 'as' prop, e.g. as: "item"{line}`,
|
|
10149
10373
|
`at line <$yellow:${line}$>{line}`
|
|
10150
10374
|
]);
|
|
10151
10375
|
return "";
|
|
@@ -10175,22 +10399,28 @@ async function generateOutput(ast, i, format, mapper_file, security = {}, parent
|
|
|
10175
10399
|
}
|
|
10176
10400
|
}
|
|
10177
10401
|
|
|
10178
|
-
|
|
10402
|
+
const rawJoin = transpiledArgs.join ?? null;
|
|
10403
|
+
const join = rawJoin !== null ? rawJoin.replace(/\\n/g, "\n").replace(/\\t/g, "\t").replace(/\\r/g, "\r") : null;
|
|
10404
|
+
const parts = [];
|
|
10179
10405
|
let idx = 0;
|
|
10406
|
+
const length = items.length;
|
|
10180
10407
|
for (const item of items) {
|
|
10181
10408
|
Evaluator$1.pushScope();
|
|
10182
10409
|
Evaluator$1.inject({
|
|
10183
10410
|
[asVar]: item,
|
|
10184
|
-
i: idx
|
|
10411
|
+
i: idx++,
|
|
10412
|
+
length
|
|
10185
10413
|
});
|
|
10186
10414
|
|
|
10415
|
+
let iterOutput = "";
|
|
10187
10416
|
for (let j = 0; j < cleanedBody.length; j++) {
|
|
10188
|
-
|
|
10417
|
+
iterOutput += await generateOutput(cleanedBody, j, format, mapper_file, security, parentId, generateRuntimeOutput, hideRuntimeOutput, instance, idState, extraCtx);
|
|
10189
10418
|
}
|
|
10190
10419
|
|
|
10191
10420
|
await Evaluator$1.popScope();
|
|
10421
|
+
parts.push(iterOutput);
|
|
10192
10422
|
}
|
|
10193
|
-
return
|
|
10423
|
+
return join !== null ? parts.join(join) : parts.join("");
|
|
10194
10424
|
}
|
|
10195
10425
|
|
|
10196
10426
|
let secretId = null;
|
|
@@ -10218,13 +10448,12 @@ async function generateOutput(ast, i, format, mapper_file, security = {}, parent
|
|
|
10218
10448
|
}
|
|
10219
10449
|
|
|
10220
10450
|
// smark-raw block — body collected verbatim by lexer, bypasses normal body processing pipeline
|
|
10221
|
-
if (node.type === BLOCK && (node.
|
|
10451
|
+
if (node.type === BLOCK && (node.directives?.raw === "true" || node.directives?.raw === true)) {
|
|
10222
10452
|
if (generateRuntimeOutput) return "";
|
|
10223
10453
|
const rawContent = node.body?.map(n => String(n.text || "")).join("") || "";
|
|
10224
|
-
const
|
|
10225
|
-
const transpiledArgs = await transpileArgs(cleanArgs);
|
|
10454
|
+
const transpiledArgs = await transpileArgs(node.props);
|
|
10226
10455
|
if (Evaluator$1.active?.hasDynamicTag?.(node.id)) {
|
|
10227
|
-
return await Evaluator$1.active.executeDynamicTag(node.id, { props: transpiledArgs, content: rawContent, textContent: rawContent });
|
|
10456
|
+
return await Evaluator$1.active.executeDynamicTag(node.id, { props: transpiledArgs, directives: node.directives, content: rawContent, textContent: rawContent });
|
|
10228
10457
|
}
|
|
10229
10458
|
let rawTarget = mapper_file ? matchedValue(mapper_file.outputs, node.id) : null;
|
|
10230
10459
|
if (!rawTarget && mapper_file) rawTarget = mapper_file.getUnknownTag(node);
|
|
@@ -10232,6 +10461,7 @@ async function generateOutput(ast, i, format, mapper_file, security = {}, parent
|
|
|
10232
10461
|
const isManualMode = !!rawTarget.options?.handleAst;
|
|
10233
10462
|
return await rawTarget.render.call(mapper_file, {
|
|
10234
10463
|
props: transpiledArgs,
|
|
10464
|
+
directives: node.directives,
|
|
10235
10465
|
content: rawContent,
|
|
10236
10466
|
textContent: rawContent,
|
|
10237
10467
|
ast: isManualMode ? node : undefined,
|
|
@@ -10315,9 +10545,11 @@ async function generateOutput(ast, i, format, mapper_file, security = {}, parent
|
|
|
10315
10545
|
const val = await Evaluator$1.execute(child.code, child.baseDir || null);
|
|
10316
10546
|
if (val !== undefined && typeof val !== "object") richText += String(val);
|
|
10317
10547
|
} catch (err) {
|
|
10548
|
+
const line = child.range?.start?.line + 1 || 1;
|
|
10318
10549
|
transpilerError([
|
|
10319
10550
|
`<$red:Logic Error:$> ${err.message}{line}`,
|
|
10320
|
-
`<$yellow:Code:$> <$blue:${child.code}$>{line}
|
|
10551
|
+
`<$yellow:Code:$> <$blue:${child.code}$>{line}`,
|
|
10552
|
+
`at line <$yellow:${line}$>{line}`
|
|
10321
10553
|
]);
|
|
10322
10554
|
}
|
|
10323
10555
|
} else if (child.type === COMMENT) {
|
|
@@ -10343,6 +10575,7 @@ async function generateOutput(ast, i, format, mapper_file, security = {}, parent
|
|
|
10343
10575
|
|
|
10344
10576
|
return await target.render.call(mapper_file, {
|
|
10345
10577
|
props: transpiledArgs,
|
|
10578
|
+
directives: node.directives,
|
|
10346
10579
|
content: "",
|
|
10347
10580
|
textContent: richText || textContent,
|
|
10348
10581
|
ast: cleanAst,
|
|
@@ -10361,6 +10594,7 @@ async function generateOutput(ast, i, format, mapper_file, security = {}, parent
|
|
|
10361
10594
|
}
|
|
10362
10595
|
result += await target.render.call(mapper_file, {
|
|
10363
10596
|
props: transpiledArgs,
|
|
10597
|
+
directives: node.directives,
|
|
10364
10598
|
content,
|
|
10365
10599
|
textContent,
|
|
10366
10600
|
ast: new Proxy({}, {
|
|
@@ -10442,9 +10676,11 @@ async function generateOutput(ast, i, format, mapper_file, security = {}, parent
|
|
|
10442
10676
|
bodyOutput = mapper_file ? mapper_file.text(out, { ...target?.options, escape: parentEscape }) : out;
|
|
10443
10677
|
}
|
|
10444
10678
|
} catch (err) {
|
|
10679
|
+
const line = body_node.range?.start?.line + 1 || 1;
|
|
10445
10680
|
transpilerError([
|
|
10446
10681
|
`<$red:Logic Error:$> ${err.message}{line}`,
|
|
10447
|
-
`<$yellow:Code:$> <$blue:${body_node.code}$>{line}
|
|
10682
|
+
`<$yellow:Code:$> <$blue:${body_node.code}$>{line}`,
|
|
10683
|
+
`at line <$yellow:${line}$>{line}`
|
|
10448
10684
|
]);
|
|
10449
10685
|
}
|
|
10450
10686
|
break;
|
|
@@ -10543,6 +10779,10 @@ async function transpiler(optionsOrAst, format, mapperFile) {
|
|
|
10543
10779
|
})();
|
|
10544
10780
|
|
|
10545
10781
|
const dualOutput = optionsOrAst?.dualOutput || false;
|
|
10782
|
+
const webOutputs = optionsOrAst?.webOutputs || false;
|
|
10783
|
+
if (webOutputs && dualOutput) {
|
|
10784
|
+
throw new Error("[SomMark] Cannot use both 'webOutputs' and 'dualOutput' at the same time. Use 'webOutputs' (returns [html, css, js]) or 'dualOutput' (returns [html, js]).");
|
|
10785
|
+
}
|
|
10546
10786
|
const placeholders = optionsOrAst?.placeholders || settings?.placeholders || {};
|
|
10547
10787
|
const variables = optionsOrAst?.variables || settings?.variables || {};
|
|
10548
10788
|
warnDroppedVariables(variables);
|
|
@@ -10557,6 +10797,89 @@ async function transpiler(optionsOrAst, format, mapperFile) {
|
|
|
10557
10797
|
let prev_body_node = null;
|
|
10558
10798
|
let prev_was_silent = false;
|
|
10559
10799
|
|
|
10800
|
+
if (webOutputs) {
|
|
10801
|
+
// Use unique markers so [style] content is extracted precisely —
|
|
10802
|
+
// no <style> regex on the final HTML, works with static logic inside [style].
|
|
10803
|
+
const CSS_OPEN = `SOMMARKCSSOPEN${randomBytesHex(8)}SOMMARK`;
|
|
10804
|
+
const CSS_CLOSE = `SOMMARKCSSCLOSE${randomBytesHex(8)}SOMMARK`;
|
|
10805
|
+
|
|
10806
|
+
const webMapper = targetMapper.clone();
|
|
10807
|
+
webMapper.register("style", function ({ content }) {
|
|
10808
|
+
return `${CSS_OPEN}${content}${CSS_CLOSE}`;
|
|
10809
|
+
}, { escape: false });
|
|
10810
|
+
// [head] injects CSS variables as a raw <style> string via this.cssVariables —
|
|
10811
|
+
// override it so those variables go through markers too.
|
|
10812
|
+
webMapper.register("head", function ({ content }) {
|
|
10813
|
+
const varsMarker = this.cssVariables
|
|
10814
|
+
? `${CSS_OPEN}:root { ${this.cssVariables} }${CSS_CLOSE}\n`
|
|
10815
|
+
: "";
|
|
10816
|
+
return this.tag("head").body(`${varsMarker}${content}`);
|
|
10817
|
+
}, { escape: false });
|
|
10818
|
+
|
|
10819
|
+
const idState = { mode: 'record', ids: [], idx: 0 };
|
|
10820
|
+
|
|
10821
|
+
// HTML pass — [style] blocks emit markers instead of <style> tags
|
|
10822
|
+
let htmlOutput = "";
|
|
10823
|
+
try {
|
|
10824
|
+
for (let i = 0; i < body.length; i++) {
|
|
10825
|
+
const node = body[i];
|
|
10826
|
+
const blockOutput = await generateOutput(body, i, targetFormat, webMapper, security, null, false, true, instance, idState);
|
|
10827
|
+
let finalBlockOutput = blockOutput;
|
|
10828
|
+
if (prev_was_silent && node.type === TEXT$1) finalBlockOutput = finalBlockOutput.replace(/^\n/, "");
|
|
10829
|
+
if (finalBlockOutput) {
|
|
10830
|
+
htmlOutput += finalBlockOutput;
|
|
10831
|
+
prev_was_silent = false;
|
|
10832
|
+
} else {
|
|
10833
|
+
prev_was_silent = true;
|
|
10834
|
+
if ((node.type === COMMENT || node.type === COMMENT_BLOCK) && targetMapper?.options?.removeComments) {
|
|
10835
|
+
const nextNode = body[i + 1];
|
|
10836
|
+
if (nextNode && nextNode.type === TEXT$1 && (nextNode.text === "\n" || nextNode.text === "\r\n")) i++;
|
|
10837
|
+
}
|
|
10838
|
+
}
|
|
10839
|
+
}
|
|
10840
|
+
} finally {
|
|
10841
|
+
Evaluator$1.destroy();
|
|
10842
|
+
}
|
|
10843
|
+
|
|
10844
|
+
// Extract CSS from markers — exact, no HTML regex
|
|
10845
|
+
const cssChunks = [];
|
|
10846
|
+
const markerRe = new RegExp(`${CSS_OPEN}([\\s\\S]*?)${CSS_CLOSE}`, "g");
|
|
10847
|
+
htmlOutput = htmlOutput.replace(markerRe, (_, chunk) => {
|
|
10848
|
+
cssChunks.push(chunk.trim());
|
|
10849
|
+
return "";
|
|
10850
|
+
});
|
|
10851
|
+
const css = cssChunks.join("\n").trim();
|
|
10852
|
+
|
|
10853
|
+
// JS pass — replay IDs so querySelector targets match HTML
|
|
10854
|
+
idState.mode = 'replay';
|
|
10855
|
+
idState.idx = 0;
|
|
10856
|
+
prev_was_silent = false;
|
|
10857
|
+
|
|
10858
|
+
await Evaluator$1.init(fileBaseDir, security, settings, targetMapper);
|
|
10859
|
+
Evaluator$1.inject(placeholders);
|
|
10860
|
+
Evaluator$1.inject(variables);
|
|
10861
|
+
|
|
10862
|
+
let jsOutput = "";
|
|
10863
|
+
try {
|
|
10864
|
+
for (let i = 0; i < body.length; i++) {
|
|
10865
|
+
const node = body[i];
|
|
10866
|
+
const blockOutput = await generateOutput(body, i, targetFormat, targetMapper, security, null, true, false, instance, idState);
|
|
10867
|
+
let finalBlockOutput = blockOutput;
|
|
10868
|
+
if (prev_was_silent && node.type === TEXT$1) finalBlockOutput = finalBlockOutput.replace(/^\n/, "");
|
|
10869
|
+
if (finalBlockOutput) {
|
|
10870
|
+
jsOutput += finalBlockOutput;
|
|
10871
|
+
prev_was_silent = false;
|
|
10872
|
+
} else {
|
|
10873
|
+
prev_was_silent = true;
|
|
10874
|
+
}
|
|
10875
|
+
}
|
|
10876
|
+
} finally {
|
|
10877
|
+
Evaluator$1.destroy();
|
|
10878
|
+
}
|
|
10879
|
+
|
|
10880
|
+
return [htmlOutput.trim(), css, jsOutput.trim()];
|
|
10881
|
+
}
|
|
10882
|
+
|
|
10560
10883
|
if (dualOutput) {
|
|
10561
10884
|
const idState = { mode: 'record', ids: [], idx: 0 };
|
|
10562
10885
|
|
|
@@ -10669,9 +10992,11 @@ async function transpileArgs(props) {
|
|
|
10669
10992
|
try {
|
|
10670
10993
|
result[key] = await Evaluator$1.execute(value.code, value.baseDir || null);
|
|
10671
10994
|
} catch (err) {
|
|
10995
|
+
const line = value.range?.start?.line + 1 || 1;
|
|
10672
10996
|
transpilerError([
|
|
10673
10997
|
`<$red:Logic Error (Argument):$> ${err.message}{line}`,
|
|
10674
|
-
`<$yellow:Code:$> <$blue:${value.code}$>{line}
|
|
10998
|
+
`<$yellow:Code:$> <$blue:${value.code}$>{line}`,
|
|
10999
|
+
`at line <$yellow:${line}$>{line}`
|
|
10675
11000
|
]);
|
|
10676
11001
|
}
|
|
10677
11002
|
} else {
|
|
@@ -11784,11 +12109,20 @@ class Mapper {
|
|
|
11784
12109
|
|
|
11785
12110
|
/**
|
|
11786
12111
|
* Registers universal utility blocks shared across all SomMark mappers.
|
|
11787
|
-
* These blocks are considered "Format Agnostic."
|
|
11788
12112
|
*
|
|
11789
12113
|
* @param {Mapper} mapper - The mapper instance to register tags on.
|
|
11790
12114
|
*/
|
|
11791
12115
|
function registerSharedOutputs(mapper) {
|
|
12116
|
+
mapper.register(
|
|
12117
|
+
["raw", "Raw"],
|
|
12118
|
+
({ content }) => {
|
|
12119
|
+
return content;
|
|
12120
|
+
},
|
|
12121
|
+
{
|
|
12122
|
+
escape: false, rules: {
|
|
12123
|
+
required_directives: ["raw"]
|
|
12124
|
+
} }
|
|
12125
|
+
);
|
|
11792
12126
|
}
|
|
11793
12127
|
|
|
11794
12128
|
const SVG_ELEMENTS = new Set([
|
|
@@ -11941,6 +12275,7 @@ HTML.register(
|
|
|
11941
12275
|
return "";
|
|
11942
12276
|
},
|
|
11943
12277
|
);
|
|
12278
|
+
registerSharedOutputs(HTML);
|
|
11944
12279
|
|
|
11945
12280
|
/**
|
|
11946
12281
|
* The Markdown Mapper used for generating Markdown text.
|
|
@@ -11967,42 +12302,37 @@ const MARKDOWN = Mapper.define({
|
|
|
11967
12302
|
},
|
|
11968
12303
|
|
|
11969
12304
|
/**
|
|
11970
|
-
* Provides a fallback for unknown tags by
|
|
11971
|
-
|
|
12305
|
+
* Provides a fallback for unknown tags by rendering them as HTML elements.
|
|
12306
|
+
* Passes child nodes to the transpiler, which handles all node types (such as ForEach).
|
|
12307
|
+
**/
|
|
11972
12308
|
getUnknownTag(node) {
|
|
11973
|
-
const id = node.id
|
|
11974
|
-
|
|
12309
|
+
const id = node.id;
|
|
11975
12310
|
return {
|
|
11976
|
-
|
|
12311
|
+
options: { trimAndWrapBlocks: true },
|
|
12312
|
+
render: ({ props, content, isSelfClosing }) => {
|
|
11977
12313
|
const element = this.tag(id).smartAttributes(props, this.customProps, this.options);
|
|
11978
12314
|
if (isSelfClosing || VOID_ELEMENTS.has(id)) return element.selfClose();
|
|
11979
|
-
|
|
11980
|
-
|
|
11981
|
-
for (const child of (ast.body || [])) {
|
|
11982
|
-
if (child.type === TEXT$1) rawContent += this.text(child.text);
|
|
11983
|
-
else if (child.type === BLOCK) rawContent += await renderChild(child);
|
|
11984
|
-
}
|
|
11985
|
-
rawContent = rawContent.trim();
|
|
11986
|
-
|
|
11987
|
-
const meaningful = (ast.body || []).filter(c => c.type !== TEXT$1 || c.text.trim());
|
|
11988
|
-
const finalContent = meaningful.length <= 1 ? rawContent : `\n${rawContent}\n`;
|
|
11989
|
-
return element.body(finalContent);
|
|
11990
|
-
},
|
|
11991
|
-
options: { handleAst: true }
|
|
12315
|
+
return element.body(content);
|
|
12316
|
+
}
|
|
11992
12317
|
};
|
|
11993
12318
|
}
|
|
11994
12319
|
});
|
|
11995
12320
|
|
|
11996
12321
|
MARKDOWN.inherit(HTML);
|
|
11997
12322
|
const { md, safeArg } = MARKDOWN;
|
|
12323
|
+
registerSharedOutputs(MARKDOWN);
|
|
11998
12324
|
|
|
11999
12325
|
/**
|
|
12000
12326
|
* Quote - Renders blockquote content or GFM alerts.
|
|
12001
12327
|
*/
|
|
12002
|
-
MARKDOWN.register(
|
|
12003
|
-
|
|
12004
|
-
|
|
12005
|
-
|
|
12328
|
+
MARKDOWN.register(
|
|
12329
|
+
"quote",
|
|
12330
|
+
({ props, content }) => {
|
|
12331
|
+
const type = safeArg({ props, index: 0, key: "type", fallBack: "" });
|
|
12332
|
+
return md.quote(content, type);
|
|
12333
|
+
},
|
|
12334
|
+
{ resolve: true }
|
|
12335
|
+
);
|
|
12006
12336
|
|
|
12007
12337
|
/**
|
|
12008
12338
|
* Headings - Renders H1-H6 block headings.
|
|
@@ -12082,12 +12412,12 @@ MARKDOWN.register(
|
|
|
12082
12412
|
"link",
|
|
12083
12413
|
({ props, content, isSelfClosing }) => {
|
|
12084
12414
|
if (isSelfClosing) {
|
|
12085
|
-
const text
|
|
12086
|
-
const src
|
|
12415
|
+
const text = safeArg({ props, index: 0, key: "text", fallBack: "" });
|
|
12416
|
+
const src = safeArg({ props, index: 1, key: "src", fallBack: "" });
|
|
12087
12417
|
const title = safeArg({ props, index: 2, key: "title", fallBack: "" });
|
|
12088
12418
|
return md.url("link", text, src, title);
|
|
12089
12419
|
}
|
|
12090
|
-
const src
|
|
12420
|
+
const src = safeArg({ props, index: 0, key: "src", fallBack: "" });
|
|
12091
12421
|
const title = safeArg({ props, index: 1, key: "title", fallBack: "" });
|
|
12092
12422
|
return md.url("link", content, src, title);
|
|
12093
12423
|
},
|
|
@@ -12125,10 +12455,14 @@ MARKDOWN.register(
|
|
|
12125
12455
|
* Escape - Escapes special Markdown characters.
|
|
12126
12456
|
* Self-closing: [escape = "text" !] or [escape = text: "text" !]
|
|
12127
12457
|
*/
|
|
12128
|
-
MARKDOWN.register(
|
|
12129
|
-
|
|
12130
|
-
|
|
12131
|
-
|
|
12458
|
+
MARKDOWN.register(
|
|
12459
|
+
["escape", "e"],
|
|
12460
|
+
function ({ props, content, isSelfClosing }) {
|
|
12461
|
+
const text = isSelfClosing ? safeArg({ props, index: 0, key: "text", fallBack: "" }) : content;
|
|
12462
|
+
return this.md.escape(text);
|
|
12463
|
+
},
|
|
12464
|
+
{ resolve: true }
|
|
12465
|
+
);
|
|
12132
12466
|
|
|
12133
12467
|
const ROW_SEP = "\x1E";
|
|
12134
12468
|
const CELL_SEP = "\x1F";
|
|
@@ -12144,12 +12478,16 @@ MARKDOWN.register(
|
|
|
12144
12478
|
const headers = [];
|
|
12145
12479
|
const rows = [];
|
|
12146
12480
|
|
|
12147
|
-
const extractRows = async
|
|
12481
|
+
const extractRows = async sectionNode => {
|
|
12148
12482
|
const sectionRows = [];
|
|
12149
|
-
for (const child of
|
|
12483
|
+
for (const child of sectionNode.body || []) {
|
|
12150
12484
|
if (child.type === BLOCK && child.id?.toLowerCase() === "row") {
|
|
12151
12485
|
const rendered = await renderChild(child, { inTable: true });
|
|
12152
|
-
const cells =
|
|
12486
|
+
const cells =
|
|
12487
|
+
rendered
|
|
12488
|
+
.split(ROW_SEP)[0]
|
|
12489
|
+
?.split(CELL_SEP)
|
|
12490
|
+
.filter(c => c !== "") ?? [];
|
|
12153
12491
|
if (cells.length > 0) sectionRows.push(cells);
|
|
12154
12492
|
} else if (child.type === FOR_EACH) {
|
|
12155
12493
|
const rendered = await renderChild(child, { inTable: true });
|
|
@@ -12183,25 +12521,29 @@ MARKDOWN.register(
|
|
|
12183
12521
|
*/
|
|
12184
12522
|
MARKDOWN.register(["header", "body"], ({ content }) => content);
|
|
12185
12523
|
|
|
12186
|
-
MARKDOWN.register(
|
|
12187
|
-
|
|
12188
|
-
|
|
12189
|
-
|
|
12190
|
-
|
|
12191
|
-
|
|
12524
|
+
MARKDOWN.register(
|
|
12525
|
+
"row",
|
|
12526
|
+
async function ({ ast, renderChild, inTable }) {
|
|
12527
|
+
if (!inTable) {
|
|
12528
|
+
let result = "";
|
|
12529
|
+
for (const child of ast.body) {
|
|
12530
|
+
if (child.type === TEXT$1) result += this.text(child.text);
|
|
12531
|
+
else if (child.type === BLOCK) result += await renderChild(child);
|
|
12532
|
+
}
|
|
12533
|
+
return result;
|
|
12192
12534
|
}
|
|
12193
|
-
|
|
12194
|
-
|
|
12195
|
-
|
|
12196
|
-
|
|
12197
|
-
|
|
12198
|
-
|
|
12199
|
-
|
|
12200
|
-
cells += await renderChild(child, { inTable: true });
|
|
12535
|
+
let cells = "";
|
|
12536
|
+
for (const child of ast.body) {
|
|
12537
|
+
if (child.type !== BLOCK) continue;
|
|
12538
|
+
const id = child.id?.toLowerCase();
|
|
12539
|
+
if (id === "cell" || id === "th" || id === "td") {
|
|
12540
|
+
cells += await renderChild(child, { inTable: true });
|
|
12541
|
+
}
|
|
12201
12542
|
}
|
|
12202
|
-
|
|
12203
|
-
|
|
12204
|
-
|
|
12543
|
+
return cells + ROW_SEP;
|
|
12544
|
+
},
|
|
12545
|
+
{ handleAst: true }
|
|
12546
|
+
);
|
|
12205
12547
|
|
|
12206
12548
|
MARKDOWN.register(["cell", "th", "td"], ({ content, inTable }) => {
|
|
12207
12549
|
return inTable ? content.trim() + CELL_SEP : content;
|
|
@@ -12211,34 +12553,42 @@ MARKDOWN.register(["cell", "th", "td"], ({ content, inTable }) => {
|
|
|
12211
12553
|
* Lists - Authoritative Native AST List resolution.
|
|
12212
12554
|
* Supports Ordered (Number) and Unordered (Dotlex) lists with deep nesting.
|
|
12213
12555
|
*/
|
|
12214
|
-
MARKDOWN.register(
|
|
12215
|
-
|
|
12216
|
-
|
|
12217
|
-
|
|
12218
|
-
|
|
12556
|
+
MARKDOWN.register(
|
|
12557
|
+
["list", "List"],
|
|
12558
|
+
async function ({ ast, props, renderChild }) {
|
|
12559
|
+
const indicator = safeArg({ props, index: 0, fallBack: "dot" });
|
|
12560
|
+
const isOrdered = indicator === "number" || indicator === "ol";
|
|
12561
|
+
const marker = isOrdered ? "" : indicator === "dot" ? "-" : indicator;
|
|
12562
|
+
const items = [];
|
|
12219
12563
|
|
|
12220
|
-
|
|
12221
|
-
|
|
12222
|
-
|
|
12223
|
-
|
|
12224
|
-
|
|
12564
|
+
for (const node of ast.body) {
|
|
12565
|
+
if (node.type !== BLOCK) continue;
|
|
12566
|
+
const id = node.id?.toLowerCase();
|
|
12567
|
+
if (id === "item") {
|
|
12568
|
+
items.push((await renderChild(node)).trim());
|
|
12569
|
+
}
|
|
12225
12570
|
}
|
|
12226
|
-
}
|
|
12227
12571
|
|
|
12228
|
-
|
|
12229
|
-
},
|
|
12572
|
+
return isOrdered ? md.orderedList(items, 0) : md.unorderedList(items, 0, marker);
|
|
12573
|
+
},
|
|
12574
|
+
{ handleAst: true, trimAndWrapBlocks: false }
|
|
12575
|
+
);
|
|
12230
12576
|
|
|
12231
12577
|
/**
|
|
12232
12578
|
* List Helpers - Internal tags for list structural organization.
|
|
12233
12579
|
*/
|
|
12234
|
-
MARKDOWN.register(
|
|
12235
|
-
|
|
12236
|
-
|
|
12237
|
-
|
|
12238
|
-
|
|
12239
|
-
|
|
12240
|
-
|
|
12241
|
-
}
|
|
12580
|
+
MARKDOWN.register(
|
|
12581
|
+
["item", "Item"],
|
|
12582
|
+
async function ({ ast, renderChild }) {
|
|
12583
|
+
let result = "";
|
|
12584
|
+
for (const child of ast.body) {
|
|
12585
|
+
if (child.type === TEXT$1) result += this.text(child.text);
|
|
12586
|
+
else if (child.type === BLOCK) result += await renderChild(child);
|
|
12587
|
+
}
|
|
12588
|
+
return result.trim();
|
|
12589
|
+
},
|
|
12590
|
+
{ handleAst: true, trimAndWrapBlocks: false }
|
|
12591
|
+
);
|
|
12242
12592
|
|
|
12243
12593
|
/**
|
|
12244
12594
|
* Todo - Renders task list items with status markers.
|
|
@@ -12248,19 +12598,23 @@ MARKDOWN.register(["item", "Item"], async function ({ ast, renderChild }) {
|
|
|
12248
12598
|
* [todo = "Add feature", "x" !] positional self-closing (task, status)
|
|
12249
12599
|
* [todo = "x"]Add feature[end] status in prop, task in body
|
|
12250
12600
|
*/
|
|
12251
|
-
MARKDOWN.register(
|
|
12252
|
-
|
|
12601
|
+
MARKDOWN.register(
|
|
12602
|
+
"todo",
|
|
12603
|
+
({ props, content, isSelfClosing }) => {
|
|
12604
|
+
let status, task;
|
|
12253
12605
|
|
|
12254
|
-
|
|
12255
|
-
|
|
12256
|
-
|
|
12257
|
-
|
|
12258
|
-
|
|
12259
|
-
|
|
12260
|
-
|
|
12606
|
+
if (isSelfClosing) {
|
|
12607
|
+
task = safeArg({ props, index: 0, key: "task", fallBack: "" });
|
|
12608
|
+
status = safeArg({ props, index: 1, key: "status", fallBack: "" });
|
|
12609
|
+
} else {
|
|
12610
|
+
status = safeArg({ props, index: 0, fallBack: "" });
|
|
12611
|
+
task = content;
|
|
12612
|
+
}
|
|
12261
12613
|
|
|
12262
|
-
|
|
12263
|
-
},
|
|
12614
|
+
return md.todo(status, task);
|
|
12615
|
+
},
|
|
12616
|
+
{ trimAndWrapBlocks: false }
|
|
12617
|
+
);
|
|
12264
12618
|
|
|
12265
12619
|
/**
|
|
12266
12620
|
* The MDX Mapper used for generating Markdown with JSX.
|
|
@@ -12470,100 +12824,115 @@ Jsonc.register(["Array", "array"], async function ({ props, ast, depth = 0, inAr
|
|
|
12470
12824
|
/**
|
|
12471
12825
|
* Renders a standard XML tag based on the provided identifier and arguments.
|
|
12472
12826
|
* Ensures strict attribute quoting and handles self-closing tags for empty bodies.
|
|
12473
|
-
*
|
|
12827
|
+
*
|
|
12474
12828
|
* @param {string} id - The XML tag identifier (case-sensitive).
|
|
12475
12829
|
* @param {Object} props - Key-value pairs to be rendered as XML attributes.
|
|
12476
12830
|
* @param {string} content - The rendered inner content of the tag.
|
|
12477
12831
|
* @returns {string} The fully rendered XML tag string.
|
|
12478
12832
|
*/
|
|
12479
12833
|
const renderXmlTag = function (id, props, content, isSelfClosing) {
|
|
12480
|
-
|
|
12481
|
-
|
|
12482
|
-
|
|
12483
|
-
// Filter out positional indices (numeric keys) for XML attributes
|
|
12484
|
-
const namedArgs = {};
|
|
12485
|
-
Object.keys(props).forEach(key => {
|
|
12486
|
-
if (isNaN(parseInt(key))) {
|
|
12487
|
-
namedArgs[key] = props[key];
|
|
12488
|
-
}
|
|
12489
|
-
});
|
|
12834
|
+
// XML is case-sensitive, so we use the exact id provided
|
|
12835
|
+
const element = this.tag(id);
|
|
12490
12836
|
|
|
12491
|
-
|
|
12492
|
-
|
|
12837
|
+
// Filter out positional indices (numeric keys) for XML attributes
|
|
12838
|
+
const namedArgs = {};
|
|
12839
|
+
Object.keys(props).forEach(key => {
|
|
12840
|
+
if (isNaN(parseInt(key))) {
|
|
12841
|
+
namedArgs[key] = props[key];
|
|
12842
|
+
}
|
|
12843
|
+
});
|
|
12493
12844
|
|
|
12494
|
-
|
|
12845
|
+
// In XML, attributes must always have values (strict = true)
|
|
12846
|
+
element.attributes(namedArgs, true);
|
|
12495
12847
|
|
|
12496
|
-
|
|
12497
|
-
|
|
12498
|
-
|
|
12848
|
+
const hasBody = typeof content === "string" && content.trim().length > 0;
|
|
12849
|
+
|
|
12850
|
+
if (isSelfClosing || !hasBody) {
|
|
12851
|
+
return element.selfClose();
|
|
12852
|
+
}
|
|
12499
12853
|
|
|
12500
|
-
|
|
12854
|
+
return element.body(content);
|
|
12501
12855
|
};
|
|
12502
12856
|
|
|
12503
12857
|
/**
|
|
12504
12858
|
* The XML Mapper used for creating XML pages.
|
|
12505
12859
|
*/
|
|
12506
12860
|
const XML = Mapper.define({
|
|
12507
|
-
|
|
12508
|
-
|
|
12509
|
-
|
|
12510
|
-
|
|
12511
|
-
|
|
12512
|
-
|
|
12513
|
-
|
|
12514
|
-
|
|
12861
|
+
/**
|
|
12862
|
+
* Renders a comment in XML format.
|
|
12863
|
+
* @param {string} text - The comment content.
|
|
12864
|
+
* @returns {string}
|
|
12865
|
+
*/
|
|
12866
|
+
comment(text) {
|
|
12867
|
+
return `<!-- ${text} -->`;
|
|
12868
|
+
},
|
|
12515
12869
|
|
|
12516
|
-
|
|
12517
|
-
|
|
12518
|
-
|
|
12519
|
-
|
|
12520
|
-
|
|
12521
|
-
|
|
12522
|
-
|
|
12523
|
-
|
|
12524
|
-
|
|
12525
|
-
|
|
12526
|
-
|
|
12527
|
-
|
|
12870
|
+
/**
|
|
12871
|
+
* Resolves unknown tags by preserving their original case and applying XML rules.
|
|
12872
|
+
* @param {Object} node - The AST node representing the unknown tag.
|
|
12873
|
+
* @returns {Object} Renderer definition for the tag.
|
|
12874
|
+
*/
|
|
12875
|
+
getUnknownTag(node) {
|
|
12876
|
+
const id = node.id;
|
|
12877
|
+
return {
|
|
12878
|
+
render: ({ props, content, isSelfClosing }) => renderXmlTag.call(this, id, props, content, isSelfClosing),
|
|
12879
|
+
options: {}
|
|
12880
|
+
};
|
|
12881
|
+
},
|
|
12882
|
+
options: {
|
|
12883
|
+
trimAndWrapBlocks: true
|
|
12884
|
+
}
|
|
12528
12885
|
});
|
|
12529
12886
|
|
|
12530
12887
|
/**
|
|
12531
12888
|
* Registers the XML declaration as a self-closing block.
|
|
12532
12889
|
* Usage: [xml = version: "1.0", encoding: "UTF-8"]
|
|
12533
12890
|
*/
|
|
12534
|
-
XML.register(
|
|
12535
|
-
|
|
12536
|
-
|
|
12537
|
-
|
|
12538
|
-
|
|
12891
|
+
XML.register(
|
|
12892
|
+
"xml",
|
|
12893
|
+
({ props }) => {
|
|
12894
|
+
const version = props.version || "1.0";
|
|
12895
|
+
const encoding = props.encoding || "UTF-8";
|
|
12896
|
+
return `<?xml version="${version}" encoding="${encoding}"?>`;
|
|
12897
|
+
},
|
|
12898
|
+
{ rules: { is_empty_body: true } }
|
|
12899
|
+
);
|
|
12539
12900
|
|
|
12540
12901
|
/**
|
|
12541
12902
|
* Registers the DOCTYPE declaration.
|
|
12542
12903
|
* Usage: [doctype = root: "note", system: "note.dtd"]
|
|
12543
12904
|
*/
|
|
12544
|
-
XML.register(
|
|
12545
|
-
|
|
12546
|
-
|
|
12547
|
-
|
|
12905
|
+
XML.register(
|
|
12906
|
+
["DOCTYPE", "doctype"],
|
|
12907
|
+
({ props }) => {
|
|
12908
|
+
const root = props.root || "root";
|
|
12909
|
+
const system = props.system;
|
|
12910
|
+
const pub = props.public || props.fpi;
|
|
12548
12911
|
|
|
12549
|
-
|
|
12550
|
-
|
|
12551
|
-
|
|
12552
|
-
|
|
12553
|
-
|
|
12554
|
-
|
|
12555
|
-
},
|
|
12912
|
+
if (pub && system) {
|
|
12913
|
+
return `<!DOCTYPE ${root} PUBLIC "${pub}" "${system}">`;
|
|
12914
|
+
} else if (system) {
|
|
12915
|
+
return `<!DOCTYPE ${root} SYSTEM "${system}">`;
|
|
12916
|
+
}
|
|
12917
|
+
return `<!DOCTYPE ${root}>`;
|
|
12918
|
+
},
|
|
12919
|
+
{ rules: { is_empty_body: true } }
|
|
12920
|
+
);
|
|
12556
12921
|
|
|
12557
12922
|
/**
|
|
12558
12923
|
* Registers the XML stylesheet processing instruction.
|
|
12559
12924
|
* Usage: [xml-stylesheet = href: "style.xsl"]
|
|
12560
12925
|
*/
|
|
12561
|
-
XML.register(
|
|
12562
|
-
|
|
12563
|
-
|
|
12564
|
-
|
|
12565
|
-
|
|
12566
|
-
|
|
12926
|
+
XML.register(
|
|
12927
|
+
"xml-stylesheet",
|
|
12928
|
+
({ props }) => {
|
|
12929
|
+
const type = props.type || "text/xsl";
|
|
12930
|
+
const href = props.href;
|
|
12931
|
+
if (!href) return "";
|
|
12932
|
+
return `<?xml-stylesheet type="${type}" href="${href}"?>`;
|
|
12933
|
+
},
|
|
12934
|
+
{ rules: { is_empty_body: true } }
|
|
12935
|
+
);
|
|
12567
12936
|
|
|
12568
12937
|
/**
|
|
12569
12938
|
* Registers CDATA sections.
|
|
@@ -12571,10 +12940,12 @@ XML.register("xml-stylesheet", ({ props }) => {
|
|
|
12571
12940
|
* Self-closing: [cdata = "raw content" !] or [cdata = text: "raw content" !]
|
|
12572
12941
|
*/
|
|
12573
12942
|
XML.register("cdata", ({ props, content, isSelfClosing }) => {
|
|
12574
|
-
|
|
12575
|
-
|
|
12943
|
+
const text = isSelfClosing ? (props[0] ?? props.text ?? "") : content;
|
|
12944
|
+
return `<![CDATA[${text}]]>`;
|
|
12576
12945
|
});
|
|
12577
12946
|
|
|
12947
|
+
registerSharedOutputs(XML);
|
|
12948
|
+
|
|
12578
12949
|
const csvEscape = (value) => {
|
|
12579
12950
|
const str = String(value ?? "").trim();
|
|
12580
12951
|
if (str.includes(",") || str.includes('"') || str.includes("\n") || str.includes("\r")) {
|
|
@@ -12631,6 +13002,8 @@ CSV.register(["col", "cell", "td"], ({ textContent }) => {
|
|
|
12631
13002
|
return csvEscape(textContent);
|
|
12632
13003
|
}, { handleAst: true, trimAndWrapBlocks: false });
|
|
12633
13004
|
|
|
13005
|
+
registerSharedOutputs(CSV);
|
|
13006
|
+
|
|
12634
13007
|
const isValidInt$1 = (v) => v !== "" && !isNaN(Number(v)) && !v.includes(".");
|
|
12635
13008
|
const isValidFloat$1 = (v) => v !== "" && !isNaN(Number(v)) && v.includes(".");
|
|
12636
13009
|
const isValidNumber$1 = (v) => v !== "" && !isNaN(Number(v));
|
|
@@ -12854,6 +13227,8 @@ TOML.register("array", async ({ props, ast, renderChild }) => {
|
|
|
12854
13227
|
return `${tomlKey(key)} = [${vals.join(", ")}]\n`;
|
|
12855
13228
|
}, { handleAst: true });
|
|
12856
13229
|
|
|
13230
|
+
registerSharedOutputs(TOML);
|
|
13231
|
+
|
|
12857
13232
|
const isValidInt = (v) => v !== "" && !isNaN(Number(v)) && !v.includes(".");
|
|
12858
13233
|
const isValidFloat = (v) => v !== "" && !isNaN(Number(v)) && v.includes(".");
|
|
12859
13234
|
const isValidNumber = (v) => v !== "" && !isNaN(Number(v));
|
|
@@ -13169,6 +13544,8 @@ YAML.register("doc-start", () => "---\n", { rules: { is_empty_body: true } });
|
|
|
13169
13544
|
*/
|
|
13170
13545
|
YAML.register("doc-end", () => "...\n", { rules: { is_empty_body: true } });
|
|
13171
13546
|
|
|
13547
|
+
registerSharedOutputs(YAML);
|
|
13548
|
+
|
|
13172
13549
|
/**
|
|
13173
13550
|
* The Text Mapper used for plain-text extraction.
|
|
13174
13551
|
*/
|
|
@@ -13445,34 +13822,48 @@ async function resolveModules(ast, context) {
|
|
|
13445
13822
|
const baseDir = context.instance.baseDir || ((filename === "anonymous") ? absFilename : posix.dirname(absFilename));
|
|
13446
13823
|
|
|
13447
13824
|
// 1. Helper: Trim AST to remove file-boundary whitespace and "ghost" newlines
|
|
13448
|
-
const trimAst = (nodes) => {
|
|
13825
|
+
const trimAst = (nodes, trimBoundaries = true) => {
|
|
13449
13826
|
if (!nodes) return [];
|
|
13450
13827
|
|
|
13451
|
-
// 1. Filter out
|
|
13452
|
-
// (Comments, Imports,
|
|
13828
|
+
// 1. Filter out whitespace-only text nodes adjacent (directly or through other whitespace)
|
|
13829
|
+
// to non-rendering nodes (Comments, Imports, USE_MODULE).
|
|
13453
13830
|
const nonRenderingTypes = [COMMENT, IMPORT, USE_MODULE];
|
|
13454
13831
|
let res = nodes.filter((node, idx) => {
|
|
13455
13832
|
if (node.type !== TEXT$1 || node.text.trim() !== "") return true;
|
|
13456
13833
|
|
|
13457
|
-
|
|
13458
|
-
|
|
13459
|
-
|
|
13460
|
-
(
|
|
13461
|
-
|
|
13834
|
+
// Walk backwards through consecutive whitespace nodes to find prev non-whitespace
|
|
13835
|
+
let prevIsNonRendering = false;
|
|
13836
|
+
for (let j = idx - 1; j >= 0; j--) {
|
|
13837
|
+
if (nodes[j].type === TEXT$1 && nodes[j].text.trim() === "") continue;
|
|
13838
|
+
prevIsNonRendering = nonRenderingTypes.includes(nodes[j].type);
|
|
13839
|
+
break;
|
|
13840
|
+
}
|
|
13462
13841
|
|
|
13463
|
-
|
|
13842
|
+
// Walk forwards through consecutive whitespace nodes to find next non-whitespace
|
|
13843
|
+
let nextIsNonRendering = false;
|
|
13844
|
+
for (let j = idx + 1; j < nodes.length; j++) {
|
|
13845
|
+
if (nodes[j].type === TEXT$1 && nodes[j].text.trim() === "") continue;
|
|
13846
|
+
nextIsNonRendering = nonRenderingTypes.includes(nodes[j].type);
|
|
13847
|
+
break;
|
|
13848
|
+
}
|
|
13849
|
+
|
|
13850
|
+
return !(prevIsNonRendering || nextIsNonRendering);
|
|
13464
13851
|
});
|
|
13465
13852
|
|
|
13466
|
-
|
|
13467
|
-
|
|
13468
|
-
res
|
|
13469
|
-
|
|
13470
|
-
|
|
13471
|
-
res
|
|
13853
|
+
if (trimBoundaries) {
|
|
13854
|
+
// 2. Final pass: trim leading/trailing newlines from the remaining boundary text nodes
|
|
13855
|
+
if (res.length > 0 && res[0].type === TEXT$1) {
|
|
13856
|
+
res[0].text = res[0].text.replace(/^[\r\n]+/, "");
|
|
13857
|
+
}
|
|
13858
|
+
if (res.length > 0 && res[res.length - 1].type === TEXT$1) {
|
|
13859
|
+
res[res.length - 1].text = res[res.length - 1].text.replace(/[\r\n]+\s*$/, "");
|
|
13860
|
+
}
|
|
13861
|
+
|
|
13862
|
+
// 3. Remove any nodes that became purely empty after trimming
|
|
13863
|
+
res = res.filter(node => node.type !== TEXT$1 || node.text !== "");
|
|
13472
13864
|
}
|
|
13473
13865
|
|
|
13474
|
-
|
|
13475
|
-
return res.filter(node => node.type !== TEXT$1 || node.text !== "");
|
|
13866
|
+
return res;
|
|
13476
13867
|
};
|
|
13477
13868
|
|
|
13478
13869
|
// 2. Helper: Inject Slots with Indentation Propagation
|
|
@@ -13535,13 +13926,38 @@ async function resolveModules(ast, context) {
|
|
|
13535
13926
|
let resolvedPath = filePath;
|
|
13536
13927
|
for (const [prefix, replacement] of Object.entries(importAliases)) {
|
|
13537
13928
|
if (filePath.startsWith(prefix)) {
|
|
13538
|
-
|
|
13929
|
+
const replaced = filePath.replace(prefix, replacement);
|
|
13930
|
+
// Preserve scheme prefixes (pkg:, http:, etc.) — don't path.resolve them
|
|
13931
|
+
resolvedPath = replaced.startsWith("pkg:") || replaced.startsWith("http://") || replaced.startsWith("https://")
|
|
13932
|
+
? replaced
|
|
13933
|
+
: posix.resolve(context.instance.cwd || "/", replaced);
|
|
13539
13934
|
break;
|
|
13540
13935
|
}
|
|
13541
13936
|
}
|
|
13542
13937
|
|
|
13543
|
-
// 1b.
|
|
13544
|
-
|
|
13938
|
+
// 1b. pkg: — resolve from node_modules at project root
|
|
13939
|
+
let absolutePath;
|
|
13940
|
+
if (resolvedPath.startsWith("pkg:")) {
|
|
13941
|
+
if (!context.instance.fs?.__isNodeFs) {
|
|
13942
|
+
runtimeError([`<$red:Module Error:$> <$cyan:pkg:$> imports are not supported in browser or virtual filesystem mode at line <$yellow:${node.range.start.line + 1}$>`]);
|
|
13943
|
+
}
|
|
13944
|
+
const pkgPath = resolvedPath.slice(4);
|
|
13945
|
+
if (!pkgPath || pkgPath.trim() === "") {
|
|
13946
|
+
runtimeError([`<$red:Module Error:$> <$cyan:pkg:$> path cannot be empty at line <$yellow:${node.range.start.line + 1}$>`]);
|
|
13947
|
+
}
|
|
13948
|
+
const nodeModulesRoot = posix.resolve(context.instance.cwd || "/", "node_modules");
|
|
13949
|
+
absolutePath = posix.resolve(nodeModulesRoot, pkgPath);
|
|
13950
|
+
if (!absolutePath.startsWith(nodeModulesRoot + posix.sep) && absolutePath !== nodeModulesRoot) {
|
|
13951
|
+
runtimeError([`<$red:Module Security Error:$> <$cyan:pkg:${pkgPath}$> resolves outside node_modules — path traversal is not allowed at line <$yellow:${node.range.start.line + 1}$>`]);
|
|
13952
|
+
}
|
|
13953
|
+
} else {
|
|
13954
|
+
// 1c. Resolve relative to current base (FS)
|
|
13955
|
+
absolutePath = resolveModulePath(resolvedPath, currentBaseDir);
|
|
13956
|
+
}
|
|
13957
|
+
|
|
13958
|
+
if (!context.instance.fs) {
|
|
13959
|
+
runtimeError([`<$red:Module Error:$> Cannot import <$magenta:${filePath}$> — no filesystem is available.{N}In browser mode, pass a URL-based <$cyan:baseDir$> or a <$cyan:files$> map to enable module loading.`]);
|
|
13960
|
+
}
|
|
13545
13961
|
|
|
13546
13962
|
// Local Path Resolution with Auto-Extension
|
|
13547
13963
|
let localPath = absolutePath;
|
|
@@ -13694,7 +14110,6 @@ async function resolveModules(ast, context) {
|
|
|
13694
14110
|
Object.entries(node.props).filter(([key]) => {
|
|
13695
14111
|
if (key === "__consumed__") return false;
|
|
13696
14112
|
if (consumed.has(key)) return false; // THE FIX: Filter if hit by v{}
|
|
13697
|
-
if (key === "smark-raw") return false; // directive — must not leak onto root element
|
|
13698
14113
|
return true;
|
|
13699
14114
|
})
|
|
13700
14115
|
);
|
|
@@ -13809,10 +14224,7 @@ const runValidations = (node, target, instance) => {
|
|
|
13809
14224
|
const isStructural = node.type === "Block";
|
|
13810
14225
|
if (isStructural && rules.required_args && Array.isArray(rules.required_args)) {
|
|
13811
14226
|
const missingArgs = rules.required_args.filter(arg => {
|
|
13812
|
-
|
|
13813
|
-
if (typeof arg === "number") {
|
|
13814
|
-
return node.props[arg] === undefined;
|
|
13815
|
-
}
|
|
14227
|
+
if (typeof arg === "number") return node.props[arg] === undefined;
|
|
13816
14228
|
return node.props[arg] === undefined;
|
|
13817
14229
|
});
|
|
13818
14230
|
|
|
@@ -13827,6 +14239,22 @@ const runValidations = (node, target, instance) => {
|
|
|
13827
14239
|
);
|
|
13828
14240
|
}
|
|
13829
14241
|
}
|
|
14242
|
+
|
|
14243
|
+
// -- Directives Validation (Required Directives) ----------------------- //
|
|
14244
|
+
if (isStructural && rules.required_directives && Array.isArray(rules.required_directives)) {
|
|
14245
|
+
const missingDirectives = rules.required_directives.filter(key => node.directives?.[key] === undefined);
|
|
14246
|
+
|
|
14247
|
+
if (missingDirectives.length > 0) {
|
|
14248
|
+
transpilerError(
|
|
14249
|
+
[
|
|
14250
|
+
"{N}",
|
|
14251
|
+
`<$yellow:Identifier$> <$blue:'${id}'$> <$yellow:is missing required directive props:$> <$red:${missingDirectives.map(k => `smark-${k}`).join(", ")}$>{N}`,
|
|
14252
|
+
`<$blue:Please ensure these directive props are provided in the template usage.$>`
|
|
14253
|
+
],
|
|
14254
|
+
context
|
|
14255
|
+
);
|
|
14256
|
+
}
|
|
14257
|
+
}
|
|
13830
14258
|
};
|
|
13831
14259
|
|
|
13832
14260
|
/**
|
|
@@ -13998,6 +14426,14 @@ function setDefaultFs(fs) {
|
|
|
13998
14426
|
Evaluator$1.setDefaultFs(fs);
|
|
13999
14427
|
}
|
|
14000
14428
|
|
|
14429
|
+
function setDefaultEnv(env) {
|
|
14430
|
+
Evaluator$1.setDefaultEnv(env);
|
|
14431
|
+
}
|
|
14432
|
+
|
|
14433
|
+
function setDefaultAsyncLocalStorage(cls) {
|
|
14434
|
+
Evaluator$1.setDefaultAsyncLocalStorage(cls);
|
|
14435
|
+
}
|
|
14436
|
+
|
|
14001
14437
|
function setDefaultResolvePath(fn) {
|
|
14002
14438
|
defaultResolvePath = fn;
|
|
14003
14439
|
}
|
|
@@ -14030,7 +14466,7 @@ class SomMark {
|
|
|
14030
14466
|
* @param {string} [options.baseDir=null] - The base directory for resolving relative paths.
|
|
14031
14467
|
*/
|
|
14032
14468
|
constructor(options = {}) {
|
|
14033
|
-
const { src, ast = null, format, mapperFile = null, filename = "anonymous", removeComments = true, placeholders = {}, customProps = [], fallbackTarget = true, outputValidator = null, importAliases = {}, importStack = [], baseDir = null, moduleCache = null, showSpinner = true, security = {}, dualOutput = false, moduleIdentityToken = null } = options;
|
|
14469
|
+
const { src, ast = null, format, mapperFile = null, filename = "anonymous", removeComments = true, placeholders = {}, customProps = [], fallbackTarget = true, outputValidator = null, importAliases = {}, importStack = [], baseDir = null, moduleCache = null, showSpinner = true, security = {}, dualOutput = false, webOutputs = false, moduleIdentityToken = null } = options;
|
|
14034
14470
|
this.rawSettings = options;
|
|
14035
14471
|
this.src = src;
|
|
14036
14472
|
this.ast = ast;
|
|
@@ -14041,6 +14477,7 @@ class SomMark {
|
|
|
14041
14477
|
this.placeholders = placeholders;
|
|
14042
14478
|
this.customProps = customProps;
|
|
14043
14479
|
this.dualOutput = dualOutput;
|
|
14480
|
+
this.webOutputs = webOutputs;
|
|
14044
14481
|
this.cwd = options.baseDir || (options.files ? "/" : defaultCwd);
|
|
14045
14482
|
this.fs = options.fs
|
|
14046
14483
|
|| (options.files ? new VirtualFS(options.files) : null)
|
|
@@ -14069,7 +14506,8 @@ class SomMark {
|
|
|
14069
14506
|
allowFetch: security?.allowFetch !== false,
|
|
14070
14507
|
allowHttp: security?.allowHttp === true,
|
|
14071
14508
|
allowedOrigins: Array.isArray(security?.allowedOrigins) ? security.allowedOrigins.map(o => o.toLowerCase()) : null,
|
|
14072
|
-
allowedExtensions: Array.isArray(security?.allowedExtensions) ? security.allowedExtensions.map(e => e.toLowerCase()) : null
|
|
14509
|
+
allowedExtensions: Array.isArray(security?.allowedExtensions) ? security.allowedExtensions.map(e => e.toLowerCase()) : null,
|
|
14510
|
+
env: Array.isArray(security?.env) ? security.env : []
|
|
14073
14511
|
};
|
|
14074
14512
|
this.warnings = [];
|
|
14075
14513
|
this._prepared = false;
|
|
@@ -14256,6 +14694,7 @@ class SomMark {
|
|
|
14256
14694
|
security: this.security,
|
|
14257
14695
|
settings: this.rawSettings,
|
|
14258
14696
|
dualOutput: this.dualOutput,
|
|
14697
|
+
webOutputs: this.webOutputs,
|
|
14259
14698
|
instance: this
|
|
14260
14699
|
});
|
|
14261
14700
|
|
|
@@ -14381,8 +14820,24 @@ async function findAndLoadConfig(targetPath) {
|
|
|
14381
14820
|
}
|
|
14382
14821
|
setCompilerClass(SomMark);
|
|
14383
14822
|
|
|
14823
|
+
class AsyncLocalStorage {
|
|
14824
|
+
#store = undefined;
|
|
14825
|
+
run(store, fn) {
|
|
14826
|
+
const prev = this.#store;
|
|
14827
|
+
this.#store = store;
|
|
14828
|
+
try { return fn(); }
|
|
14829
|
+
finally { this.#store = prev; }
|
|
14830
|
+
}
|
|
14831
|
+
getStore() { return this.#store; }
|
|
14832
|
+
exit(fn) { return fn(); }
|
|
14833
|
+
enterWith(store) { this.#store = store; }
|
|
14834
|
+
disable() {}
|
|
14835
|
+
}
|
|
14836
|
+
|
|
14384
14837
|
setDefaultFs(null);
|
|
14385
14838
|
setDefaultCwd("/");
|
|
14839
|
+
setDefaultEnv(null);
|
|
14840
|
+
setDefaultAsyncLocalStorage(AsyncLocalStorage);
|
|
14386
14841
|
|
|
14387
14842
|
/**
|
|
14388
14843
|
* Resolves a relative path into a full URL using the current document location.
|
|
@@ -14464,4 +14919,4 @@ function renderCompiledHTML(container, html) {
|
|
|
14464
14919
|
}
|
|
14465
14920
|
}
|
|
14466
14921
|
|
|
14467
|
-
export { CSV, Evaluator$1 as Evaluator, formats as FORMATS, HTML, Json, Jsonc, MARKDOWN, MDX, Mapper, TOKEN_TYPES, TOML, XML, YAML, SomMark as default, enableColor, findAndLoadConfig, labels, lex, lexSync, parse, parseSync, preprocessRuntimeLogic, registerSharedOutputs, renderCompiledHTML, resolveBaseDir, safeArg$1 as safeArg, setDefaultCwd, setDefaultFindAndLoadConfig, setDefaultFs, setDefaultResolvePath, transpile };
|
|
14922
|
+
export { CSV, Evaluator$1 as Evaluator, formats as FORMATS, HTML, Json, Jsonc, MARKDOWN, MDX, Mapper, TOKEN_TYPES, TOML, XML, YAML, SomMark as default, enableColor, findAndLoadConfig, labels, lex, lexSync, parse, parseSync, preprocessRuntimeLogic, registerSharedOutputs, renderCompiledHTML, resolveBaseDir, safeArg$1 as safeArg, setDefaultAsyncLocalStorage, setDefaultCwd, setDefaultEnv, setDefaultFindAndLoadConfig, setDefaultFs, setDefaultResolvePath, transpile };
|