sommark 5.0.2 → 5.0.4
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/core/evaluator.js +37 -10
- package/core/evaluator.stub.js +1 -0
- package/core/helpers/lib.js +1 -1
- package/core/lexer.js +0 -7
- package/core/modules.js +64 -4
- package/core/parser.js +43 -2
- package/core/transpiler.js +81 -82
- package/dist/sommark.browser.js +225 -104
- package/dist/sommark.browser.lite.js +187 -93
- package/dist/sommark.lexer.js +0 -7
- package/dist/sommark.parser.js +42 -9
- package/index.shared.js +2 -1
- package/package.json +1 -1
package/dist/sommark.browser.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
|
|
1
3
|
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)})();
|
|
2
4
|
return __lib__.default || __lib__; };
|
|
3
5
|
let _match;
|
|
@@ -801,13 +803,6 @@ function lexer(src, filename = "anonymous") {
|
|
|
801
803
|
// LOGIC BLOCKS (${ ... }$) — explicit: static/runtime ${ }$ shorthand: ${ }$ = static ${ }$
|
|
802
804
|
if (char === "$" && next === "{") {
|
|
803
805
|
{
|
|
804
|
-
const hasExplicitKeyword = last_non_junk_type === TOKEN_TYPES.STATIC_KEYWORD || last_non_junk_type === TOKEN_TYPES.RUNTIME_KEYWORD;
|
|
805
|
-
if (!hasExplicitKeyword) {
|
|
806
|
-
// Zero-width: synthetic token has no source presence, must not shift position
|
|
807
|
-
tokens.push({ type: TOKEN_TYPES.STATIC_KEYWORD, value: "static", source: filename, range: { start: { line, character }, end: { line, character } } });
|
|
808
|
-
TOKEN_TYPES.STATIC_KEYWORD;
|
|
809
|
-
last_non_junk_type = TOKEN_TYPES.STATIC_KEYWORD;
|
|
810
|
-
}
|
|
811
806
|
addToken(TOKEN_TYPES.LOGIC_OPEN, "${");
|
|
812
807
|
i += 2;
|
|
813
808
|
|
|
@@ -1761,6 +1756,22 @@ function parseValue(tokens, i, placeholders = {}, variables = {}, allowLogic = t
|
|
|
1761
1756
|
nextI++;
|
|
1762
1757
|
}
|
|
1763
1758
|
|
|
1759
|
+
return [node, nextI, false];
|
|
1760
|
+
} else if (current_token(tokens, i).type === TOKEN_TYPES.LOGIC_OPEN) {
|
|
1761
|
+
if (!allowLogic) {
|
|
1762
|
+
parserError(errorMessage(tokens, i, "literal value", "", "Logic blocks are not allowed in this context."));
|
|
1763
|
+
}
|
|
1764
|
+
let nextI = i + 1;
|
|
1765
|
+
const logicToken = current_token(tokens, nextI);
|
|
1766
|
+
const node = makeLogicNode(STATIC_LOGIC);
|
|
1767
|
+
node.code = logicToken ? logicToken.value : "";
|
|
1768
|
+
node.range = logicToken ? logicToken.range : current_token(tokens, i).range;
|
|
1769
|
+
nextI++;
|
|
1770
|
+
|
|
1771
|
+
if (current_token(tokens, nextI) && current_token(tokens, nextI).type === TOKEN_TYPES.LOGIC_CLOSE) {
|
|
1772
|
+
nextI++;
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1764
1775
|
return [node, nextI, false];
|
|
1765
1776
|
} else if (current_token(tokens, i).type === TOKEN_TYPES.PREFIX_V) {
|
|
1766
1777
|
i++; // consume PREFIX_V keyword
|
|
@@ -1777,7 +1788,9 @@ function parseValue(tokens, i, placeholders = {}, variables = {}, allowLogic = t
|
|
|
1777
1788
|
}
|
|
1778
1789
|
variables.__consumed__.add(vKey);
|
|
1779
1790
|
} else {
|
|
1780
|
-
|
|
1791
|
+
// Encode fallback in the envelope key so resolveAstVariables can apply it
|
|
1792
|
+
// at instantiation time instead of baking it in now.
|
|
1793
|
+
val = getPrefixValue('v', vFallback !== undefined ? `${vKey}|${vFallback}` : vKey);
|
|
1781
1794
|
}
|
|
1782
1795
|
return [val, i, false];
|
|
1783
1796
|
} else if (current_token(tokens, i).type === TOKEN_TYPES.PREFIX_P) {
|
|
@@ -2171,7 +2184,8 @@ function parseText(tokens, i, placeholders = {}, variables = {}, depth = 0, opti
|
|
|
2171
2184
|
}
|
|
2172
2185
|
variables.__consumed__.add(tvKey);
|
|
2173
2186
|
} else {
|
|
2174
|
-
|
|
2187
|
+
// Encode fallback in envelope so resolveAstVariables can apply it later.
|
|
2188
|
+
textNode.text += getPrefixValue('v', tvFallback !== undefined ? `${tvKey}|${tvFallback}` : tvKey);
|
|
2175
2189
|
}
|
|
2176
2190
|
} else {
|
|
2177
2191
|
break;
|
|
@@ -2269,6 +2283,27 @@ function parseNode(tokens, i, filename = null, placeholders = {}, variables = {}
|
|
|
2269
2283
|
return [node, nextI];
|
|
2270
2284
|
}
|
|
2271
2285
|
// ========================================================================== //
|
|
2286
|
+
// Bare Logic Block (${ }$ without explicit static/runtime — defaults to static)
|
|
2287
|
+
// ========================================================================== //
|
|
2288
|
+
else if (current_token(tokens, i) && current_token(tokens, i).type === TOKEN_TYPES.LOGIC_OPEN) {
|
|
2289
|
+
let nextI = i + 1;
|
|
2290
|
+
const logicToken = current_token(tokens, nextI);
|
|
2291
|
+
const node = makeLogicNode(STATIC_LOGIC);
|
|
2292
|
+
node.code = logicToken ? logicToken.value : "";
|
|
2293
|
+
node.depth = depth;
|
|
2294
|
+
node.range = {
|
|
2295
|
+
start: current_token(tokens, i).range.start,
|
|
2296
|
+
end: logicToken ? logicToken.range.end : current_token(tokens, i).range.end
|
|
2297
|
+
};
|
|
2298
|
+
nextI++;
|
|
2299
|
+
|
|
2300
|
+
if (current_token(tokens, nextI) && current_token(tokens, nextI).type === TOKEN_TYPES.LOGIC_CLOSE) {
|
|
2301
|
+
nextI++;
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
return [node, nextI];
|
|
2305
|
+
}
|
|
2306
|
+
// ========================================================================== //
|
|
2272
2307
|
// Text or Placeholder //
|
|
2273
2308
|
// ========================================================================== //
|
|
2274
2309
|
else if (
|
|
@@ -8592,7 +8627,7 @@ function registerHostSettings(settings) {
|
|
|
8592
8627
|
hostSettings = settings || {};
|
|
8593
8628
|
}
|
|
8594
8629
|
|
|
8595
|
-
const version = "5.0.
|
|
8630
|
+
const version = "5.0.4";
|
|
8596
8631
|
|
|
8597
8632
|
const SomMark$1 = {
|
|
8598
8633
|
version,
|
|
@@ -8643,6 +8678,17 @@ const SomMark$1 = {
|
|
|
8643
8678
|
// Freeze the entire Standard Library to make it completely immutable and tamper-proof
|
|
8644
8679
|
Object.freeze(SomMark$1);
|
|
8645
8680
|
|
|
8681
|
+
// Each transpile() call gets its own isolated EvaluatorState stack via async context.
|
|
8682
|
+
const evaluatorStorage = new AsyncLocalStorage();
|
|
8683
|
+
|
|
8684
|
+
/**
|
|
8685
|
+
* Runs fn inside an isolated evaluator context.
|
|
8686
|
+
* Concurrent transpile() calls each get their own stack — no cross-contamination.
|
|
8687
|
+
*/
|
|
8688
|
+
function withEvaluator(fn) {
|
|
8689
|
+
return evaluatorStorage.run([], fn);
|
|
8690
|
+
}
|
|
8691
|
+
|
|
8646
8692
|
// Global tracker to ensure deep recursive Smark compilation never exceeds safe boundaries
|
|
8647
8693
|
let globalCompilationDepth = 0;
|
|
8648
8694
|
|
|
@@ -9369,8 +9415,10 @@ class EvaluatorState {
|
|
|
9369
9415
|
this.expose(vars);
|
|
9370
9416
|
}
|
|
9371
9417
|
|
|
9372
|
-
async execute(code) {
|
|
9418
|
+
async execute(code, baseDir = null) {
|
|
9373
9419
|
if (!this.context) throw new Error("Evaluator not initialized");
|
|
9420
|
+
const prevBaseDir = this.baseDir;
|
|
9421
|
+
if (baseDir) this.baseDir = baseDir;
|
|
9374
9422
|
|
|
9375
9423
|
const timeout = this.security?.timeout ?? 5000;
|
|
9376
9424
|
this.deadline = Date.now() + timeout;
|
|
@@ -9604,6 +9652,7 @@ class EvaluatorState {
|
|
|
9604
9652
|
} finally {
|
|
9605
9653
|
this.deadline = 0;
|
|
9606
9654
|
clearInterval(interval);
|
|
9655
|
+
if (baseDir) this.baseDir = prevBaseDir;
|
|
9607
9656
|
}
|
|
9608
9657
|
}
|
|
9609
9658
|
|
|
@@ -9640,30 +9689,42 @@ class EvaluatorState {
|
|
|
9640
9689
|
|
|
9641
9690
|
class Evaluator {
|
|
9642
9691
|
constructor() {
|
|
9643
|
-
|
|
9692
|
+
// Fallback stack for callers that use init() outside withEvaluator() (e.g. tests).
|
|
9693
|
+
this._fallbackStack = [];
|
|
9694
|
+
}
|
|
9695
|
+
|
|
9696
|
+
_getStack() {
|
|
9697
|
+
return evaluatorStorage.getStore() ?? this._fallbackStack;
|
|
9644
9698
|
}
|
|
9645
9699
|
|
|
9700
|
+
// Expose the active stack so tests can check .instances.length
|
|
9701
|
+
get instances() { return this._getStack(); }
|
|
9702
|
+
|
|
9646
9703
|
setDefaultFs(fs) {
|
|
9647
9704
|
defaultFs$1 = fs;
|
|
9648
9705
|
}
|
|
9649
9706
|
|
|
9650
9707
|
get active() {
|
|
9651
|
-
|
|
9708
|
+
const stack = this._getStack();
|
|
9709
|
+
if (stack.length === 0) {
|
|
9652
9710
|
throw new Error("No active EvaluatorState instance. Did you call init()?");
|
|
9653
9711
|
}
|
|
9654
|
-
return
|
|
9712
|
+
return stack[stack.length - 1];
|
|
9655
9713
|
}
|
|
9656
9714
|
|
|
9715
|
+
// Forward .runtime to the active state so tests can assert on it
|
|
9716
|
+
get runtime() { return this.active?.runtime ?? null; }
|
|
9717
|
+
|
|
9657
9718
|
async init(baseDir = null, security = {}, settings = {}, mapperFile = null) {
|
|
9658
9719
|
const state = new EvaluatorState();
|
|
9659
9720
|
await state.init(baseDir, security, settings, mapperFile);
|
|
9660
|
-
this.
|
|
9721
|
+
this._getStack().push(state);
|
|
9661
9722
|
}
|
|
9662
9723
|
|
|
9663
9724
|
destroy() {
|
|
9664
|
-
|
|
9665
|
-
|
|
9666
|
-
|
|
9725
|
+
const stack = this._getStack();
|
|
9726
|
+
if (stack.length > 0) {
|
|
9727
|
+
stack.pop().destroy();
|
|
9667
9728
|
}
|
|
9668
9729
|
}
|
|
9669
9730
|
|
|
@@ -9679,8 +9740,8 @@ class Evaluator {
|
|
|
9679
9740
|
this.active.inject(vars);
|
|
9680
9741
|
}
|
|
9681
9742
|
|
|
9682
|
-
async execute(code) {
|
|
9683
|
-
return await this.active.execute(code);
|
|
9743
|
+
async execute(code, baseDir = null) {
|
|
9744
|
+
return await this.active.execute(code, baseDir);
|
|
9684
9745
|
}
|
|
9685
9746
|
|
|
9686
9747
|
hasDynamicTag(id) {
|
|
@@ -10045,7 +10106,7 @@ async function generateOutput(ast, i, format, mapper_file, security = {}, parent
|
|
|
10045
10106
|
|
|
10046
10107
|
if (node.type === STATIC_LOGIC) {
|
|
10047
10108
|
try {
|
|
10048
|
-
const result = await Evaluator$1.execute(node.code);
|
|
10109
|
+
const result = await Evaluator$1.execute(node.code, node.baseDir || null);
|
|
10049
10110
|
if (generateRuntimeOutput) return "";
|
|
10050
10111
|
if (result && typeof result === "object" && result.__raw !== undefined) {
|
|
10051
10112
|
if (security?.allowRaw === false) {
|
|
@@ -10251,7 +10312,7 @@ async function generateOutput(ast, i, format, mapper_file, security = {}, parent
|
|
|
10251
10312
|
}
|
|
10252
10313
|
} else if (child.type === STATIC_LOGIC) {
|
|
10253
10314
|
try {
|
|
10254
|
-
const val = await Evaluator$1.execute(child.code);
|
|
10315
|
+
const val = await Evaluator$1.execute(child.code, child.baseDir || null);
|
|
10255
10316
|
if (val !== undefined && typeof val !== "object") richText += String(val);
|
|
10256
10317
|
} catch (err) {
|
|
10257
10318
|
transpilerError([
|
|
@@ -10368,7 +10429,7 @@ async function generateOutput(ast, i, format, mapper_file, security = {}, parent
|
|
|
10368
10429
|
|
|
10369
10430
|
case STATIC_LOGIC:
|
|
10370
10431
|
try {
|
|
10371
|
-
const result = await Evaluator$1.execute(body_node.code);
|
|
10432
|
+
const result = await Evaluator$1.execute(body_node.code, body_node.baseDir || null);
|
|
10372
10433
|
if (result && typeof result === "object" && result.__raw !== undefined) {
|
|
10373
10434
|
if (security?.allowRaw === false) {
|
|
10374
10435
|
bodyOutput = mapper_file ? mapper_file.text(String(result.__raw)) : String(result.__raw);
|
|
@@ -10482,109 +10543,108 @@ async function transpiler(optionsOrAst, format, mapperFile) {
|
|
|
10482
10543
|
})();
|
|
10483
10544
|
|
|
10484
10545
|
const dualOutput = optionsOrAst?.dualOutput || false;
|
|
10485
|
-
|
|
10486
|
-
// Initialize Logic Sandbox
|
|
10487
|
-
await Evaluator$1.init(fileBaseDir, security, settings, targetMapper);
|
|
10488
|
-
// Inject global data
|
|
10489
10546
|
const placeholders = optionsOrAst?.placeholders || settings?.placeholders || {};
|
|
10490
10547
|
const variables = optionsOrAst?.variables || settings?.variables || {};
|
|
10491
10548
|
warnDroppedVariables(variables);
|
|
10492
|
-
Evaluator$1.inject(placeholders);
|
|
10493
|
-
Evaluator$1.inject(variables);
|
|
10494
10549
|
|
|
10495
|
-
|
|
10496
|
-
|
|
10497
|
-
|
|
10550
|
+
return withEvaluator(async () => {
|
|
10551
|
+
// Initialize Logic Sandbox inside isolated async context
|
|
10552
|
+
await Evaluator$1.init(fileBaseDir, security, settings, targetMapper);
|
|
10553
|
+
Evaluator$1.inject(placeholders);
|
|
10554
|
+
Evaluator$1.inject(variables);
|
|
10555
|
+
|
|
10556
|
+
let output = "";
|
|
10557
|
+
let prev_body_node = null;
|
|
10558
|
+
let prev_was_silent = false;
|
|
10498
10559
|
|
|
10499
|
-
|
|
10500
|
-
|
|
10560
|
+
if (dualOutput) {
|
|
10561
|
+
const idState = { mode: 'record', ids: [], idx: 0 };
|
|
10501
10562
|
|
|
10502
|
-
|
|
10503
|
-
|
|
10504
|
-
|
|
10505
|
-
|
|
10506
|
-
|
|
10507
|
-
|
|
10508
|
-
|
|
10509
|
-
|
|
10510
|
-
|
|
10511
|
-
|
|
10512
|
-
|
|
10513
|
-
|
|
10514
|
-
|
|
10515
|
-
|
|
10516
|
-
|
|
10517
|
-
|
|
10563
|
+
// HTML pass — generate HTML, record element IDs for runtime blocks
|
|
10564
|
+
let htmlOutput = "";
|
|
10565
|
+
try {
|
|
10566
|
+
for (let i = 0; i < body.length; i++) {
|
|
10567
|
+
const node = body[i];
|
|
10568
|
+
const blockOutput = await generateOutput(body, i, targetFormat, targetMapper, security, null, false, true, instance, idState);
|
|
10569
|
+
let finalBlockOutput = blockOutput;
|
|
10570
|
+
if (prev_was_silent && node.type === TEXT$1) finalBlockOutput = finalBlockOutput.replace(/^\n/, "");
|
|
10571
|
+
if (finalBlockOutput) {
|
|
10572
|
+
htmlOutput += finalBlockOutput;
|
|
10573
|
+
prev_was_silent = false;
|
|
10574
|
+
} else {
|
|
10575
|
+
prev_was_silent = true;
|
|
10576
|
+
if ((node.type === COMMENT || node.type === COMMENT_BLOCK) && targetMapper?.options?.removeComments) {
|
|
10577
|
+
const nextNode = body[i + 1];
|
|
10578
|
+
if (nextNode && nextNode.type === TEXT$1 && (nextNode.text === "\n" || nextNode.text === "\r\n")) i++;
|
|
10579
|
+
}
|
|
10518
10580
|
}
|
|
10519
10581
|
}
|
|
10582
|
+
} finally {
|
|
10583
|
+
Evaluator$1.destroy();
|
|
10520
10584
|
}
|
|
10521
|
-
} finally {
|
|
10522
|
-
Evaluator$1.destroy();
|
|
10523
|
-
}
|
|
10524
10585
|
|
|
10525
|
-
|
|
10526
|
-
|
|
10527
|
-
|
|
10528
|
-
|
|
10586
|
+
// JS pass — replay the same IDs so querySelector targets match HTML
|
|
10587
|
+
idState.mode = 'replay';
|
|
10588
|
+
idState.idx = 0;
|
|
10589
|
+
prev_was_silent = false;
|
|
10529
10590
|
|
|
10530
|
-
|
|
10531
|
-
|
|
10532
|
-
|
|
10591
|
+
await Evaluator$1.init(fileBaseDir, security, settings, targetMapper);
|
|
10592
|
+
Evaluator$1.inject(placeholders);
|
|
10593
|
+
Evaluator$1.inject(variables);
|
|
10594
|
+
|
|
10595
|
+
let jsOutput = "";
|
|
10596
|
+
try {
|
|
10597
|
+
for (let i = 0; i < body.length; i++) {
|
|
10598
|
+
const node = body[i];
|
|
10599
|
+
const blockOutput = await generateOutput(body, i, targetFormat, targetMapper, security, null, true, false, instance, idState);
|
|
10600
|
+
let finalBlockOutput = blockOutput;
|
|
10601
|
+
if (prev_was_silent && node.type === TEXT$1) finalBlockOutput = finalBlockOutput.replace(/^\n/, "");
|
|
10602
|
+
if (finalBlockOutput) {
|
|
10603
|
+
jsOutput += finalBlockOutput;
|
|
10604
|
+
prev_was_silent = false;
|
|
10605
|
+
} else {
|
|
10606
|
+
prev_was_silent = true;
|
|
10607
|
+
}
|
|
10608
|
+
}
|
|
10609
|
+
} finally {
|
|
10610
|
+
Evaluator$1.destroy();
|
|
10611
|
+
}
|
|
10612
|
+
|
|
10613
|
+
return [htmlOutput.trim(), jsOutput.trim()];
|
|
10614
|
+
}
|
|
10533
10615
|
|
|
10534
|
-
let jsOutput = "";
|
|
10535
10616
|
try {
|
|
10536
10617
|
for (let i = 0; i < body.length; i++) {
|
|
10537
10618
|
const node = body[i];
|
|
10538
|
-
const blockOutput = await generateOutput(body, i, targetFormat, targetMapper, security, null,
|
|
10619
|
+
const blockOutput = await generateOutput(body, i, targetFormat, targetMapper, security, null, false, false, instance);
|
|
10620
|
+
|
|
10539
10621
|
let finalBlockOutput = blockOutput;
|
|
10540
|
-
if (prev_was_silent && node.type === TEXT$1)
|
|
10622
|
+
if (prev_was_silent && node.type === TEXT$1) {
|
|
10623
|
+
finalBlockOutput = finalBlockOutput.replace(/^\n/, "");
|
|
10624
|
+
}
|
|
10625
|
+
|
|
10541
10626
|
if (finalBlockOutput) {
|
|
10542
|
-
|
|
10627
|
+
output += finalBlockOutput;
|
|
10543
10628
|
prev_was_silent = false;
|
|
10629
|
+
if (node.type !== TEXT$1 || node.text.trim().length > 0) {
|
|
10630
|
+
prev_body_node = node;
|
|
10631
|
+
}
|
|
10544
10632
|
} else {
|
|
10545
10633
|
prev_was_silent = true;
|
|
10634
|
+
if ((node.type === COMMENT || node.type === COMMENT_BLOCK) && targetMapper?.options?.removeComments) {
|
|
10635
|
+
const nextNode = body[i + 1];
|
|
10636
|
+
if (nextNode && nextNode.type === TEXT$1 && (nextNode.text === "\n" || nextNode.text === "\r\n")) {
|
|
10637
|
+
i++;
|
|
10638
|
+
}
|
|
10639
|
+
}
|
|
10546
10640
|
}
|
|
10547
10641
|
}
|
|
10548
10642
|
} finally {
|
|
10549
10643
|
Evaluator$1.destroy();
|
|
10550
10644
|
}
|
|
10551
10645
|
|
|
10552
|
-
return
|
|
10553
|
-
}
|
|
10554
|
-
|
|
10555
|
-
try {
|
|
10556
|
-
for (let i = 0; i < body.length; i++) {
|
|
10557
|
-
const node = body[i];
|
|
10558
|
-
const blockOutput = await generateOutput(body, i, targetFormat, targetMapper, security, null, false, false, instance);
|
|
10559
|
-
|
|
10560
|
-
let finalBlockOutput = blockOutput;
|
|
10561
|
-
if (prev_was_silent && node.type === TEXT$1) {
|
|
10562
|
-
finalBlockOutput = finalBlockOutput.replace(/^\n/, "");
|
|
10563
|
-
}
|
|
10564
|
-
|
|
10565
|
-
if (finalBlockOutput) {
|
|
10566
|
-
output += finalBlockOutput;
|
|
10567
|
-
prev_was_silent = false;
|
|
10568
|
-
if (node.type !== TEXT$1 || node.text.trim().length > 0) {
|
|
10569
|
-
prev_body_node = node;
|
|
10570
|
-
}
|
|
10571
|
-
} else {
|
|
10572
|
-
prev_was_silent = true;
|
|
10573
|
-
if ((node.type === COMMENT || node.type === COMMENT_BLOCK) && targetMapper?.options?.removeComments) {
|
|
10574
|
-
// If a comment is removed, check the next node.
|
|
10575
|
-
// If it's just a blank line, skip it so we don't have extra gaps in the output.
|
|
10576
|
-
const nextNode = body[i + 1];
|
|
10577
|
-
if (nextNode && nextNode.type === TEXT$1 && (nextNode.text === "\n" || nextNode.text === "\r\n")) {
|
|
10578
|
-
i++; // Skip the next newline node
|
|
10579
|
-
}
|
|
10580
|
-
}
|
|
10581
|
-
}
|
|
10582
|
-
}
|
|
10583
|
-
} finally {
|
|
10584
|
-
Evaluator$1.destroy();
|
|
10585
|
-
}
|
|
10586
|
-
|
|
10587
|
-
return output.trim();
|
|
10646
|
+
return output.trim();
|
|
10647
|
+
});
|
|
10588
10648
|
}
|
|
10589
10649
|
|
|
10590
10650
|
/**
|
|
@@ -10607,7 +10667,7 @@ async function transpileArgs(props) {
|
|
|
10607
10667
|
result[key] = "";
|
|
10608
10668
|
} else if (value.type === STATIC_LOGIC) {
|
|
10609
10669
|
try {
|
|
10610
|
-
result[key] = await Evaluator$1.execute(value.code);
|
|
10670
|
+
result[key] = await Evaluator$1.execute(value.code, value.baseDir || null);
|
|
10611
10671
|
} catch (err) {
|
|
10612
10672
|
transpilerError([
|
|
10613
10673
|
`<$red:Logic Error (Argument):$> ${err.message}{line}`,
|
|
@@ -13245,7 +13305,10 @@ const resolveAstVariables = (nodes, variables) => {
|
|
|
13245
13305
|
for (const node of nodes) {
|
|
13246
13306
|
if (node.type === TEXT$1) {
|
|
13247
13307
|
if (node.text.includes(VAR_PREFIX)) {
|
|
13248
|
-
node.text = node.text.replace(VAR_PATTERN, (match,
|
|
13308
|
+
node.text = node.text.replace(VAR_PATTERN, (match, keyAndFallback) => {
|
|
13309
|
+
const pipeIdx = keyAndFallback.indexOf('|');
|
|
13310
|
+
const key = pipeIdx >= 0 ? keyAndFallback.slice(0, pipeIdx) : keyAndFallback;
|
|
13311
|
+
const fallback = pipeIdx >= 0 ? keyAndFallback.slice(pipeIdx + 1) : undefined;
|
|
13249
13312
|
if (variables[key] !== undefined) {
|
|
13250
13313
|
if (!variables.__consumed__) {
|
|
13251
13314
|
Object.defineProperty(variables, "__consumed__", {
|
|
@@ -13258,14 +13321,21 @@ const resolveAstVariables = (nodes, variables) => {
|
|
|
13258
13321
|
variables.__consumed__.add(key);
|
|
13259
13322
|
return String(variables[key]);
|
|
13260
13323
|
}
|
|
13324
|
+
if (fallback !== undefined) return fallback;
|
|
13261
13325
|
return match;
|
|
13262
13326
|
});
|
|
13263
13327
|
}
|
|
13264
13328
|
} else if (node.type === BLOCK) {
|
|
13265
13329
|
// Resolve any unresolved variables in block arguments
|
|
13266
13330
|
for (const [argKey, argVal] of Object.entries(node.props)) {
|
|
13267
|
-
if (typeof argVal
|
|
13268
|
-
|
|
13331
|
+
if (typeof argVal !== "string" || !argVal.includes(VAR_PREFIX)) continue;
|
|
13332
|
+
|
|
13333
|
+
if (argVal.startsWith(VAR_PREFIX) && argVal.endsWith(VAR_SUFFIX)) {
|
|
13334
|
+
// Entire value is an envelope — resolve to scalar or fallback
|
|
13335
|
+
const keyAndFallback = argVal.slice(VAR_PREFIX.length, -VAR_SUFFIX.length);
|
|
13336
|
+
const pipeIdx = keyAndFallback.indexOf('|');
|
|
13337
|
+
const varKey = pipeIdx >= 0 ? keyAndFallback.slice(0, pipeIdx) : keyAndFallback;
|
|
13338
|
+
const fallback = pipeIdx >= 0 ? keyAndFallback.slice(pipeIdx + 1) : undefined;
|
|
13269
13339
|
if (variables[varKey] !== undefined) {
|
|
13270
13340
|
node.props[argKey] = variables[varKey];
|
|
13271
13341
|
if (!variables.__consumed__) {
|
|
@@ -13277,7 +13347,31 @@ const resolveAstVariables = (nodes, variables) => {
|
|
|
13277
13347
|
});
|
|
13278
13348
|
}
|
|
13279
13349
|
variables.__consumed__.add(varKey);
|
|
13350
|
+
} else if (fallback !== undefined) {
|
|
13351
|
+
node.props[argKey] = fallback;
|
|
13280
13352
|
}
|
|
13353
|
+
} else {
|
|
13354
|
+
// Envelope embedded inside a larger string — replace in-place.
|
|
13355
|
+
// Unresolved envelopes become "" so they don't pollute class names etc.
|
|
13356
|
+
node.props[argKey] = argVal.replace(VAR_PATTERN, (match, keyAndFallback) => {
|
|
13357
|
+
const pipeIdx = keyAndFallback.indexOf('|');
|
|
13358
|
+
const key = pipeIdx >= 0 ? keyAndFallback.slice(0, pipeIdx) : keyAndFallback;
|
|
13359
|
+
const fallback = pipeIdx >= 0 ? keyAndFallback.slice(pipeIdx + 1) : undefined;
|
|
13360
|
+
if (variables[key] !== undefined) {
|
|
13361
|
+
if (!variables.__consumed__) {
|
|
13362
|
+
Object.defineProperty(variables, "__consumed__", {
|
|
13363
|
+
value: new Set(),
|
|
13364
|
+
writable: true,
|
|
13365
|
+
enumerable: false,
|
|
13366
|
+
configurable: true
|
|
13367
|
+
});
|
|
13368
|
+
}
|
|
13369
|
+
variables.__consumed__.add(key);
|
|
13370
|
+
return String(variables[key]);
|
|
13371
|
+
}
|
|
13372
|
+
if (fallback !== undefined) return fallback;
|
|
13373
|
+
return "";
|
|
13374
|
+
});
|
|
13281
13375
|
}
|
|
13282
13376
|
}
|
|
13283
13377
|
if (node.body) {
|
|
@@ -13307,6 +13401,7 @@ const cloneAst = (nodes) => {
|
|
|
13307
13401
|
if (node.id !== undefined) nodeCopy.id = node.id;
|
|
13308
13402
|
if (node.code !== undefined) nodeCopy.code = node.code;
|
|
13309
13403
|
if (node.isSelfClosing !== undefined) nodeCopy.isSelfClosing = node.isSelfClosing;
|
|
13404
|
+
if (node.baseDir !== undefined) nodeCopy.baseDir = node.baseDir;
|
|
13310
13405
|
if (node.props !== undefined) {
|
|
13311
13406
|
nodeCopy.props = { ...node.props };
|
|
13312
13407
|
}
|
|
@@ -13318,6 +13413,20 @@ const cloneAst = (nodes) => {
|
|
|
13318
13413
|
return copy;
|
|
13319
13414
|
};
|
|
13320
13415
|
|
|
13416
|
+
/**
|
|
13417
|
+
* Tags all STATIC_LOGIC and RUNTIME_LOGIC nodes in a subtree with their
|
|
13418
|
+
* source module's baseDir so the evaluator can resolve imports correctly.
|
|
13419
|
+
*/
|
|
13420
|
+
const tagLogicNodes = (nodes, baseDir) => {
|
|
13421
|
+
if (!nodes) return;
|
|
13422
|
+
for (const node of nodes) {
|
|
13423
|
+
if ((node.type === STATIC_LOGIC || node.type === RUNTIME_LOGIC) && !node.baseDir) {
|
|
13424
|
+
node.baseDir = baseDir;
|
|
13425
|
+
}
|
|
13426
|
+
if (node.body) tagLogicNodes(node.body, baseDir);
|
|
13427
|
+
}
|
|
13428
|
+
};
|
|
13429
|
+
|
|
13321
13430
|
/**
|
|
13322
13431
|
* Handles all [import] and [$use-module] blocks in your code.
|
|
13323
13432
|
* It loads the requested files, checks for errors, and puts the content into the main document.
|
|
@@ -13491,6 +13600,7 @@ async function resolveModules(ast, context) {
|
|
|
13491
13600
|
format: context.format,
|
|
13492
13601
|
filename: mod.path,
|
|
13493
13602
|
baseDir: posix.dirname(mod.localPath),
|
|
13603
|
+
fs: context.instance.fs,
|
|
13494
13604
|
mapperFile: context.instance.mapperFile,
|
|
13495
13605
|
placeholders: context.instance.placeholders,
|
|
13496
13606
|
variables: {},
|
|
@@ -13506,6 +13616,7 @@ async function resolveModules(ast, context) {
|
|
|
13506
13616
|
});
|
|
13507
13617
|
|
|
13508
13618
|
const subAst = await subSmark.parse();
|
|
13619
|
+
tagLogicNodes(subAst, posix.dirname(mod.localPath));
|
|
13509
13620
|
context.instance.moduleCache.set(mod.localPath, subAst);
|
|
13510
13621
|
expandedNodes = trimAst(subAst);
|
|
13511
13622
|
}
|
|
@@ -13549,6 +13660,7 @@ async function resolveModules(ast, context) {
|
|
|
13549
13660
|
format: context.format,
|
|
13550
13661
|
filename: mod.path,
|
|
13551
13662
|
baseDir: posix.dirname(mod.localPath),
|
|
13663
|
+
fs: context.instance.fs,
|
|
13552
13664
|
mapperFile: context.instance.mapperFile,
|
|
13553
13665
|
placeholders: context.instance.placeholders,
|
|
13554
13666
|
variables: {}, // Parse without variables to keep the cached AST pure
|
|
@@ -13564,6 +13676,7 @@ async function resolveModules(ast, context) {
|
|
|
13564
13676
|
});
|
|
13565
13677
|
|
|
13566
13678
|
subAst = await subSmark.parse();
|
|
13679
|
+
tagLogicNodes(subAst, posix.dirname(mod.localPath));
|
|
13567
13680
|
context.instance.moduleCache.set(mod.localPath, subAst);
|
|
13568
13681
|
subAst = cloneAst(subAst);
|
|
13569
13682
|
}
|
|
@@ -13633,6 +13746,13 @@ async function resolveModules(ast, context) {
|
|
|
13633
13746
|
return ast;
|
|
13634
13747
|
}
|
|
13635
13748
|
|
|
13749
|
+
/**
|
|
13750
|
+
* After full transpilation of the top-level file, apply any v{} fallbacks that
|
|
13751
|
+
* remain unresolved. Envelopes with no fallback are kept as-is (debugging signal).
|
|
13752
|
+
* Must NOT be called on sub-module ASTs — only on the final top-level AST.
|
|
13753
|
+
*/
|
|
13754
|
+
const applyVariableFallbacks = (ast) => resolveAstVariables(ast, {});
|
|
13755
|
+
|
|
13636
13756
|
/**
|
|
13637
13757
|
* SomMark Rules Validator
|
|
13638
13758
|
*
|
|
@@ -14127,6 +14247,7 @@ class SomMark {
|
|
|
14127
14247
|
if (this.showSpinner) startSpinner();
|
|
14128
14248
|
try {
|
|
14129
14249
|
const ast = this.ast || await this.parse(src);
|
|
14250
|
+
applyVariableFallbacks(ast);
|
|
14130
14251
|
let result = await transpiler({
|
|
14131
14252
|
ast,
|
|
14132
14253
|
format: this.targetFormat,
|