@fluixi/compiler 1.0.0-alpha.83 → 1.0.0-alpha.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analyze/expr-shape.cjs +62 -1
- package/dist/analyze/expr-shape.d.cts +14 -0
- package/dist/analyze/expr-shape.d.ts +2 -2
- package/dist/analyze/expr-shape.js +1 -1
- package/dist/analyze/expr-shape.mjs +41 -1
- package/dist/analyze/intrinsics.cjs +80 -1
- package/dist/analyze/intrinsics.d.cts +66 -0
- package/dist/analyze/intrinsics.d.ts +7 -7
- package/dist/analyze/intrinsics.d.ts.map +1 -1
- package/dist/analyze/intrinsics.js +3 -3
- package/dist/analyze/intrinsics.mjs +59 -1
- package/dist/analyze/paren.cjs +134 -0
- package/dist/analyze/paren.d.cts +23 -0
- package/dist/analyze/paren.d.ts +24 -0
- package/dist/analyze/paren.d.ts.map +1 -0
- package/dist/analyze/paren.js +168 -0
- package/dist/analyze/paren.mjs +113 -0
- package/dist/analyze/props-destructure.cjs +226 -1
- package/dist/analyze/props-destructure.d.cts +83 -0
- package/dist/analyze/props-destructure.d.ts +3 -3
- package/dist/analyze/props-destructure.d.ts.map +1 -1
- package/dist/analyze/props-destructure.js +4 -4
- package/dist/analyze/props-destructure.mjs +205 -1
- package/dist/analyze/reactive-bindings.cjs +190 -1
- package/dist/analyze/reactive-bindings.d.cts +42 -0
- package/dist/analyze/reactive-bindings.d.ts +1 -1
- package/dist/analyze/reactive-bindings.js +2 -2
- package/dist/analyze/reactive-bindings.mjs +167 -1
- package/dist/analyze/reactive-expr.cjs +38 -1
- package/dist/analyze/reactive-expr.d.cts +38 -0
- package/dist/analyze/reactive-expr.d.ts +4 -4
- package/dist/analyze/reactive-expr.d.ts.map +1 -1
- package/dist/analyze/reactive-expr.js +2 -2
- package/dist/analyze/reactive-expr.mjs +17 -1
- package/dist/analyze/static-graph.cjs +811 -1
- package/dist/analyze/static-graph.d.cts +102 -0
- package/dist/analyze/static-graph.d.ts +1 -1
- package/dist/analyze/static-graph.d.ts.map +1 -1
- package/dist/analyze/static-graph.js +11 -11
- package/dist/analyze/static-graph.mjs +793 -1
- package/dist/babel-TGY76XJ7.mjs +636 -0
- package/dist/chunk-4PLHS2FP.mjs +158 -0
- package/dist/chunk-6DB5FXS4.mjs +1718 -0
- package/dist/chunk-F74GFKSL.mjs +16 -0
- package/dist/chunk-G6QC2WRJ.mjs +11 -0
- package/dist/chunk-RUDJDZAA.mjs +1078 -0
- package/dist/codegen/backend.cjs +18 -1
- package/dist/codegen/backend.d.cts +35 -0
- package/dist/codegen/backends/imperative.cjs +542 -1
- package/dist/codegen/backends/imperative.d.cts +11 -0
- package/dist/codegen/backends/imperative.d.ts +1 -1
- package/dist/codegen/backends/imperative.d.ts.map +1 -1
- package/dist/codegen/backends/imperative.js +24 -20
- package/dist/codegen/backends/imperative.mjs +519 -1
- package/dist/codegen/contract.cjs +52 -1
- package/dist/codegen/contract.d.cts +24 -0
- package/dist/codegen/contract.d.ts +2 -2
- package/dist/codegen/contract.js +2 -2
- package/dist/codegen/contract.mjs +31 -1
- package/dist/codegen/partial-template.cjs +176 -1
- package/dist/codegen/partial-template.d.cts +61 -0
- package/dist/codegen/partial-template.d.ts +4 -4
- package/dist/codegen/partial-template.js +0 -0
- package/dist/codegen/partial-template.mjs +153 -1
- package/dist/codegen/serialize-static.cjs +125 -1
- package/dist/codegen/serialize-static.d.cts +38 -0
- package/dist/codegen/serialize-static.d.ts +3 -3
- package/dist/codegen/serialize-static.js +6 -6
- package/dist/codegen/serialize-static.mjs +104 -1
- package/dist/frontend/babel/build-ir-lit.cjs +1509 -1
- package/dist/frontend/babel/build-ir-lit.d.cts +26 -0
- package/dist/frontend/babel/build-ir-lit.d.ts +2 -2
- package/dist/frontend/babel/build-ir-lit.mjs +1478 -1
- package/dist/frontend/babel/build-ir.cjs +1524 -1
- package/dist/frontend/babel/build-ir.d.cts +35 -0
- package/dist/frontend/babel/build-ir.d.ts +2 -2
- package/dist/frontend/babel/build-ir.js +2 -2
- package/dist/frontend/babel/build-ir.mjs +1495 -1
- package/dist/frontend/babel/index.cjs +2186 -1
- package/dist/frontend/babel/index.d.cts +33 -0
- package/dist/frontend/babel/index.d.ts +1 -1
- package/dist/frontend/babel/index.js +1 -1
- package/dist/frontend/babel/index.mjs +2156 -1
- package/dist/frontend/babel/lower-template.cjs +1507 -1
- package/dist/frontend/babel/lower-template.d.cts +23 -0
- package/dist/frontend/babel/lower-template.d.ts +1 -1
- package/dist/frontend/babel/lower-template.js +2 -2
- package/dist/frontend/babel/lower-template.mjs +1478 -1
- package/dist/frontend/babel/plugin.cjs +2132 -1
- package/dist/frontend/babel/plugin.d.cts +95 -0
- package/dist/frontend/babel/plugin.d.ts +5 -5
- package/dist/frontend/babel/plugin.d.ts.map +1 -1
- package/dist/frontend/babel/plugin.js +14 -14
- package/dist/frontend/babel/plugin.mjs +2103 -1
- package/dist/frontend/babel/server-functions.cjs +110 -1
- package/dist/frontend/babel/server-functions.d.cts +10 -0
- package/dist/frontend/babel/server-functions.mjs +89 -1
- package/dist/frontend/babel/types.cjs +18 -1
- package/dist/frontend/babel/types.d.cts +33 -0
- package/dist/frontend/types.cjs +18 -1
- package/dist/frontend/types.d.cts +25 -0
- package/dist/frontend/vocabulary.cjs +98 -0
- package/dist/frontend/vocabulary.d.cts +79 -0
- package/dist/frontend/vocabulary.d.ts +80 -0
- package/dist/frontend/vocabulary.d.ts.map +1 -0
- package/dist/frontend/vocabulary.js +124 -0
- package/dist/frontend/vocabulary.mjs +77 -0
- package/dist/index.cjs +1595 -14
- package/dist/index.d.cts +27 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -4
- package/dist/index.mjs +1575 -14
- package/dist/integrations.cjs +4312 -26
- package/dist/integrations.d.cts +215 -0
- package/dist/integrations.d.ts +9 -9
- package/dist/integrations.d.ts.map +1 -1
- package/dist/integrations.js +29 -8
- package/dist/integrations.mjs +536 -18
- package/dist/ir/nodes.cjs +18 -1
- package/dist/ir/nodes.d.cts +168 -0
- package/dist/ir/nodes.d.ts +6 -6
- package/dist/lower/compile-template.cjs +1356 -1
- package/dist/lower/compile-template.d.cts +69 -0
- package/dist/lower/compile-template.d.ts +3 -3
- package/dist/lower/compile-template.js +1 -1
- package/dist/lower/compile-template.mjs +1336 -1
- package/dist/lower/jsx.cjs +529 -1
- package/dist/lower/jsx.d.cts +25 -0
- package/dist/lower/jsx.d.ts +1 -1
- package/dist/lower/jsx.d.ts.map +1 -1
- package/dist/lower/jsx.js +6 -5
- package/dist/lower/jsx.mjs +506 -1
- package/dist/lower/template.cjs +1061 -1
- package/dist/lower/template.d.cts +53 -0
- package/dist/lower/template.d.ts +3 -3
- package/dist/lower/template.d.ts.map +1 -1
- package/dist/lower/template.js +12 -11
- package/dist/lower/template.mjs +1042 -1
- package/dist/lower/text.cjs +42 -1
- package/dist/lower/text.d.cts +10 -0
- package/dist/lower/text.d.ts +2 -2
- package/dist/lower/text.d.ts.map +1 -1
- package/dist/lower/text.js +2 -2
- package/dist/lower/text.mjs +21 -1
- package/dist/optimize/analyze-static.cjs +112 -1
- package/dist/optimize/analyze-static.d.cts +39 -0
- package/dist/optimize/analyze-static.d.ts +2 -2
- package/dist/optimize/analyze-static.js +2 -2
- package/dist/optimize/analyze-static.mjs +91 -1
- package/dist/optimize/collapse-strategies.cjs +90 -1
- package/dist/optimize/collapse-strategies.d.cts +30 -0
- package/dist/optimize/collapse-strategies.d.ts +3 -3
- package/dist/optimize/collapse-strategies.mjs +67 -1
- package/dist/options.cjs +33 -1
- package/dist/options.d.cts +39 -0
- package/dist/options.d.ts +2 -2
- package/dist/options.mjs +12 -1
- package/dist/resolve/builtins.cjs +73 -1
- package/dist/resolve/builtins.d.cts +68 -0
- package/dist/resolve/builtins.d.ts +8 -8
- package/dist/resolve/builtins.d.ts.map +1 -1
- package/dist/resolve/builtins.js +4 -4
- package/dist/resolve/builtins.mjs +53 -1
- package/dist/resolve/component-globals.cjs +234 -14
- package/dist/resolve/component-globals.d.cts +80 -0
- package/dist/resolve/component-globals.d.ts +5 -5
- package/dist/resolve/component-globals.js +10 -10
- package/dist/resolve/component-globals.mjs +211 -14
- package/dist/resolve/component-resolver.cjs +80 -1
- package/dist/resolve/component-resolver.d.cts +68 -0
- package/dist/resolve/component-resolver.d.ts +3 -3
- package/dist/resolve/component-resolver.d.ts.map +1 -1
- package/dist/resolve/component-resolver.js +3 -3
- package/dist/resolve/component-resolver.mjs +59 -1
- package/dist/resolve/load-directive.cjs +93 -1
- package/dist/resolve/load-directive.d.cts +53 -0
- package/dist/resolve/load-directive.d.ts +5 -5
- package/dist/resolve/load-directive.js +6 -6
- package/dist/resolve/load-directive.mjs +72 -1
- package/dist/resolve/resolve-ir.cjs +108 -1
- package/dist/resolve/resolve-ir.d.cts +53 -0
- package/dist/resolve/resolve-ir.d.ts +6 -6
- package/dist/resolve/resolve-ir.d.ts.map +1 -1
- package/dist/resolve/resolve-ir.js +1 -1
- package/dist/resolve/resolve-ir.mjs +85 -1
- package/dist/resolve/write-globals.cjs +319 -14
- package/dist/resolve/write-globals.d.cts +30 -0
- package/dist/resolve/write-globals.js +2 -2
- package/dist/resolve/write-globals.mjs +298 -14
- package/dist/server-fns-ZYWQTSZU.mjs +78 -0
- package/dist/server-functions-PCERFA7A.mjs +82 -0
- package/dist/transform/bindings.cjs +71 -1
- package/dist/transform/bindings.d.cts +16 -0
- package/dist/transform/bindings.d.ts +1 -1
- package/dist/transform/bindings.js +1 -1
- package/dist/transform/bindings.mjs +50 -1
- package/dist/transform/index.cjs +2732 -8
- package/dist/transform/index.d.cts +15 -0
- package/dist/transform/index.mjs +3773 -17
- package/dist/transform/intrinsics.cjs +216 -3
- package/dist/transform/intrinsics.d.cts +21 -0
- package/dist/transform/intrinsics.js +1 -1
- package/dist/transform/intrinsics.mjs +193 -3
- package/dist/transform/props.cjs +441 -3
- package/dist/transform/props.d.cts +45 -0
- package/dist/transform/props.d.ts +1 -1
- package/dist/transform/props.d.ts.map +1 -1
- package/dist/transform/props.js +4 -4
- package/dist/transform/props.mjs +1482 -12
- package/dist/transform/server-fn-id.cjs +35 -1
- package/dist/transform/server-fn-id.d.cts +9 -0
- package/dist/transform/server-fn-id.d.ts +1 -1
- package/dist/transform/server-fn-id.js +2 -2
- package/dist/transform/server-fn-id.mjs +14 -1
- package/dist/transform/server-fns.cjs +114 -2
- package/dist/transform/server-fns.d.cts +12 -0
- package/dist/transform/server-fns.js +3 -3
- package/dist/transform/server-fns.mjs +1155 -11
- package/dist/transform/source-locations.cjs +330 -1
- package/dist/transform/source-locations.d.cts +43 -0
- package/dist/transform/source-locations.d.ts +1 -1
- package/dist/transform/source-locations.js +5 -5
- package/dist/transform/source-locations.mjs +307 -1
- package/dist/transform/templates.cjs +2701 -7
- package/dist/transform/templates.d.cts +69 -0
- package/dist/transform/templates.d.ts +2 -2
- package/dist/transform/templates.d.ts.map +1 -1
- package/dist/transform/templates.js +7 -7
- package/dist/transform/templates.mjs +3743 -16
- package/dist/transform-RWDTQUF5.mjs +893 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +6 -5
- package/dist/babel-VXKH6MRQ.mjs +0 -1
- package/dist/chunk-3SAEGOMQ.mjs +0 -1
- package/dist/chunk-5KMMN5QP.mjs +0 -1
- package/dist/chunk-IBRM4W7I.mjs +0 -10
- package/dist/chunk-OHHZCYIQ.mjs +0 -1
- package/dist/chunk-YKZ54NVE.mjs +0 -1
- package/dist/server-fns-6QM243HC.mjs +0 -2
- package/dist/server-functions-E3LKCZ4R.mjs +0 -1
- package/dist/transform-C4ZXRVGP.mjs +0 -8
package/dist/lower/template.mjs
CHANGED
|
@@ -1 +1,1042 @@
|
|
|
1
|
-
import{parseTemplate as ke}from"@fluixi/template-parser";var Z={kind:"eager"},X=["pointerdown","focusin","keydown"],C=new Set(["eager","idle","visible","interaction","media","never"]),m=class extends Error{};function P(t,e){let n=t.slice(5);if(!C.has(n))throw new m(`Unknown load strategy 'load:${n}'. Expected one of ${[...C].join(", ")}.`);switch(n){case"eager":return Z;case"idle":return{kind:"idle"};case"never":return{kind:"never"};case"visible":return e?{kind:"visible",rootMargin:e}:{kind:"visible"};case"interaction":return{kind:"interaction",events:e?Q(e):[...X]};case"media":if(!e)throw new m(`'load:media' needs a query, e.g. load:media="(min-width: 1024px)".`);return{kind:"media",query:e};default:throw new m(`Unhandled load strategy '${n}'.`)}}function Q(t){let e=t.split(/[\s,]+/).map(n=>n.trim()).filter(Boolean);if(e.length===0)throw new m("'load:interaction' was given no event names.");return e}var ee=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),D={className:"class",htmlFor:"for"},te=new Set(["innerHTML","outerHTML","innerText","textContent","value","checked","selected","muted","defaultValue","defaultChecked","children","ref"]),ne=new Set(["script","style","textarea","title"]);function $(t){return ne.has(t)}function O(t){return D[t]??t}function N(t){return t.kind!=="attr"||t.expr!==void 0||t.name.includes(":")?!1:!te.has(t.name)}function I(t){if(!t.static||$(t.tag))return!1;for(let e of t.props)if(!N(e))return!1;for(let e of t.children)if(e.kind!=="text"&&!(e.kind==="element"&&I(e)))return!1;return!0}function v(t){let e=t.props.map(ie).filter(Boolean).join(""),n=`<${t.tag}${e}>`;return ee.has(t.tag)?n:`${n}${t.children.map(re).join("")}</${t.tag}>`}function re(t){if(t.kind==="text")return se(t.value);if(t.kind!=="element")throw new Error(`serializeStatic: unexpected ${t.kind}`);return v(t)}function ie(t){let e=D[t.name]??t.name,n=t.literal;return n===!0||n===void 0?` ${e}`:n===!1||n===null?"":` ${e}="${oe(String(n))}"`}function oe(t){return t.replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}function se(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function E(t){let e=[],n,r=i=>{n??=i.at?.file,e.push(i.at?.line??0,i.at?.column??0);for(let o of i.children)o.kind==="element"&&r(o)};return r(t),n&&e.some(i=>i!==0)?{file:n,pos:e}:void 0}var ae="<!--fx-->",le="<!--fx/-->";function x(t){return t.kind==="expr"||t.kind==="component"||t.kind==="control"}function ce(t){return t.kind==="text"||t.kind==="element"&&_(t)}function _(t){return t.svg||$(t.tag)||!t.props.every(e=>N(e))?!1:t.children.every(e=>ce(e)||x(e))}function R(t){if(x(t))return!0;let e=t.children;return e?e.some(R):!1}function M(t){for(let e=0;e<t.children.length;e++){let n=t.children[e];if(n.kind==="text"&&(n.value===""||t.children[e+1]?.kind==="text"))return!1}return t.children.every(e=>e.kind!=="element"||M(e))}function j(t){if(!_(t)||!M(t)||!t.children.some(R))return null;let e=[],n=[],r=0,i=l=>{let s=`_n$${r++}`;return e.push({ref:s,expr:l}),s};return o(t,"_el$"),{html:ue(t),tag:t.tag,steps:e,holes:n};function o(l,s){let u=-1;l.children.forEach((a,d)=>{R(a)&&(u=d)});let c=null;for(let a=0;a<=u;a++){let d=l.children[a],p=c?`${c}.nextSibling`:`${s}.firstChild`;if(x(d)){let g=i(p),y=i(`holeEnd(${g})`);n.push({parentRef:s,startRef:g,endRef:y,node:d}),c=y;continue}let h=i(p);d.kind==="element"&&R(d)&&o(d,h),c=h}}}function ue(t){return v(L(t)).split(H).join(ae+le)}function L(t){return{...t,children:t.children.map(e=>x(e)?{kind:"text",value:H}:e.kind==="element"?L(e):e)}}var H="\0fx-hole\0";var S={version:1,module:"@fluixi/dom",symbols:["createNativeElement","insert","spread","setAttribute","delegateEvents","createComponent","createMemo","untrack","Show","For","Index","Switch","Match","Dynamic","ErrorBoundary"]},Ie={version:2,module:"@fluixi/dom",symbols:[...S.symbols,"template","cloneTemplate","walk"]};var f="@fluixi/reactive/signal",w={$signal:{export:"signal",module:f,returns:"signal-handle"},$memo:{export:"memo",module:f,returns:"memo-handle"},$effect:{export:"effect",module:f,returns:"effect"},$store:{export:"store",module:"@fluixi/reactive/store",returns:"store-handle"},$resource:{export:"resource",module:f,returns:"resource-handle"},$selector:{export:"createSelector",module:f,returns:"memo-accessor"},$deferred:{export:"createDeferred",module:f,returns:"memo-accessor"},$untrack:{export:"untrack",module:f,returns:"plain"},$untrackStore:{export:"untrackStore",module:"@fluixi/reactive/store",returns:"plain"}};var Se=new RegExp(`\\$(?:${Object.keys(w).map(t=>t.slice(1)).join("|")})\\s*\\(`);var B="__fx_source";var de="__fx_hole",k=(t,e,n,r)=>`(globalThis.${de} ?? ((f) => f))(${t},${e},${n}${r===void 0?"":`,${JSON.stringify(r)}`})`,pe="__fx_props",F=(t,e)=>`(globalThis.${pe} ?? ((p) => p))(${t},${JSON.stringify(e)})`;var fe={show:"Show",for:"For",index:"Index",switch:"Switch",match:"Match",dynamic:"Dynamic",errorBoundary:"ErrorBoundary",suspense:"Suspense"};function me(t){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(t)}function U(t){return me(t)?t:JSON.stringify(t)}function he(t){if(t.expr!==void 0){if(t.name==="ref"&&t.at)return k(`(${t.expr})`,t.at.line,t.at.column,t.expr);if(!t.reactive)return`(${t.expr})`;let e=`() => (${t.expr})`;return t.at?k(e,t.at.line,t.at.column,t.directive):e}return JSON.stringify(t.literal??!0)}function ge(t){return t.expr!==void 0?`(${t.expr})`:JSON.stringify(t.literal??!0)}function Ne(t,e,n){let r=t.filter(s=>s.kind==="spread"),o=t.filter(s=>s.kind!=="spread").map(s=>`${U(s.name)}: ${he(s)}`);e!=null&&o.push(`children: ${e}`);let l=`{ ${o.join(", ")} }`;return r.length>0?(n.add("mergeProps"),`mergeProps(${r.map(s=>s.expr).join(", ")}, ${l})`):l}function J(t,e){return e?k(t,e.line,e.column):t}function K(t,e,n){return t.length===1?b(t[0],e,n):`[${t.map(r=>b(r,e,n)).join(", ")}]`}function V(t,e,n,r,i,o){r.add("createMemo"),r.add("createComponent");let l=e.filter(p=>p.kind==="spread"),s=e.filter(p=>p.kind!=="spread"),u=s.map(p=>`get ${U(p.name)}() { return ${ge(p)}; }`);n.length>0&&u.push(`get children() { return ${K(n,r,i)}; }`);let c=`{ ${u.join(", ")} }`;l.length>0&&(r.add("mergeProps"),c=`mergeProps(${l.map(p=>p.expr).join(", ")}, ${c})`);let a={};for(let p of s)p.at&&(a[p.name]=[p.at.line,p.at.column]);return Object.keys(a).length&&(c=F(c,a)),`createMemo((${o?`globalThis.${B}?.(${JSON.stringify(o.file)},${o.line},${o.column},${JSON.stringify(t)}), `:""}() => createComponent(${t}, ${c})))`}function ve(t,e){let n=O(e.name),r=e.literal;return r===!0||r===void 0?`${t}.setAttribute(${JSON.stringify(n)}, "");`:r===!1||r===null?"":`${t}.setAttribute(${JSON.stringify(n)}, ${JSON.stringify(String(r))});`}var z=!1;function b(t,e,n){switch(t.kind){case"text":return JSON.stringify(t.value);case"expr":{if(!t.reactive)return`(${t.code})`;let r=`() => (${t.code})`;return t.at?k(r,t.at.line,t.at.column):r}case"fragment":return t.children.length===0?"null":K(t.children,e,n);case"component":return J(V(t.name,t.props,t.children,e,n,t.at),t.bindAt??t.at);case"control":{let r=fe[t.control]??t.control;return e.add(r),J(V(r,t.props,t.children,e,n),t.bindAt)}case"element":{if(n&&t.static&&!t.svg&&I(t)){e.add("templateNode");let s=`_tmpl$${n.length}`;n.push({id:s,html:v(t),tag:t.tag,svg:!1});let u=E(t);return u?`templateNode(${s}, ${JSON.stringify(t.tag)}, false, false, ${JSON.stringify(u)})`:`templateNode(${s}, ${JSON.stringify(t.tag)})`}if(n&&z){let s=j(t);if(s){e.add("templateNode"),e.add("insert"),e.add("holeEnd"),e.add("holeContent"),e.add("holeScope");let u=`_tmpl$${n.length}`;n.push({id:u,html:s.html,tag:s.tag,svg:!1});let c=E(t),a=[`const _el$ = templateNode(${u}, ${JSON.stringify(s.tag)}, true${c?`, false, ${JSON.stringify(c)}`:""});`];for(let d of s.steps)a.push(`const ${d.ref} = ${d.expr};`);for(let d of s.holes)a.push(`insert(${d.parentRef}, holeScope(${d.startRef}, () => (${b(d.node,e,n)})), ${d.endRef}, holeContent(${d.startRef}, ${d.endRef}));`);return a.push("return _el$;"),`(() => { ${a.join(" ")} })()`}}e.add("createNativeElement");let r=JSON.stringify(t.tag),i="_el$",o=[],l=t.at?`${r}, ${t.svg}, ${JSON.stringify(t.at)}`:t.svg?`${r}, true`:r;if(o.push(`const ${i} = createNativeElement(${l});`),t.props.length>0)if(!t.svg&&t.props.every(N))for(let s of t.props)o.push(ve(i,s));else{e.add("spread");let s=t.svg?", isSVG: true":"";o.push(`spread({ element: ${i}, props: ${Ne(t.props,null,e)}${s} });`)}for(let s of t.children){e.add("insert");let u=b(s,e,n),c=s.kind==="expr"&&s.reactive||s.kind==="component"||s.kind==="control";o.push(c?`insert(${i}, ${u}, null);`:`insert(${i}, ${u});`)}return o.push(`return ${i};`),`(() => { ${o.join(" ")} })()`}}}function W(t,e){if(!e||e.length===0)return t;let n=new Map(e.map(r=>[r.id,JSON.stringify(r.html)]));return t.replace(/_tmpl\$\d+/g,r=>n.get(r)??r)}var q={name:"imperative",contract:S,emit(t,e){let n=new Set,r=e?.templateClone!==!1?[]:void 0;return z=e?.partialTemplates===!0,{code:b(t,n,r),imports:Array.from(n),templates:r}}};function T(t){let e=t.split(/\r\n|\n|\r/),n=0;for(let i=0;i<e.length;i++)/[^ \t]/.test(e[i])&&(n=i);let r="";for(let i=0;i<e.length;i++){let o=e[i].replace(/\t/g," ");i!==0&&(o=o.replace(/^ +/,"")),i!==e.length-1&&(o=o.replace(/ +$/,"")),o&&(i!==n&&(o+=" "),r+=o)}return r}var G=new Set(["click","dblclick","input","change","submit","focus","blur","keydown","keyup","keypress","mousedown","mouseup"]);function be(t){return t.replace(/\\/g,"\\\\").replace(/`/g,"\\`").replace(/\$\{/g,"\\${")}function Y(t){return t.charAt(0).toUpperCase()+t.slice(1)}var A=class{constructor(e,n,r,i){this.holes=e;this.used=n;this.sourceFile=r;this.positionAt=i}tagAt(e){let n=e.loc?.start;if(!this.sourceFile||!this.positionAt||n==null)return;let{line:r,column:i}=this.positionAt(n);return{file:this.sourceFile,line:r,column:i}}hole(e){return this.holes[e]??{code:"undefined",reactive:!1}}emit(e){let{code:n,imports:r,templates:i}=q.emit(e,{});for(let o of r)this.used.add(o);return W(n,i)}lowerRoot(e){let n=this.lowerChildren(e);return n.length===1?n[0]:{kind:"fragment",children:n}}lowerChildren(e){let n=[];for(let r=0;r<e.length;r++){let i=e[r];if(i.kind==="Element"||i.kind==="Component"){let l=i.attributes.find(s=>s.kind==="IfDirective");if(l&&l.kind==="IfDirective"){let s=r+1;s<e.length&&this.isBlankText(e[s])&&s++;let u=e[s],c=u&&(u.kind==="Element"||u.kind==="Component")&&u.attributes.some(a=>a.kind==="ElseDirective");n.push(this.lowerIf(i,l.hole,c?u:null)),c&&(r=s);continue}if(i.attributes.some(s=>s.kind==="EachDirective")){n.push(this.lowerEach(i));continue}}let o=this.lowerNode(i);o&&n.push(o)}return n}isBlankText(e){return e.kind==="Text"&&!e.raw&&T(e.value)===""}lowerNode(e){switch(e.kind){case"Text":{if(e.raw)return{kind:"text",value:e.value};let n=T(e.value);return n?{kind:"text",value:n}:null}case"Comment":return null;case"Expression":{let n=this.hole(e.hole);return{kind:"expr",code:n.code,reactive:n.reactive,...n.at?{at:n.at}:{}}}case"Fragment":return{kind:"fragment",children:this.lowerChildren(e.children)};case"Element":return this.lowerElement(e);case"Component":return this.lowerComponent(e);default:return null}}lowerElement(e){let n=e.attributes.find(i=>i.kind==="Attribute"&&i.name==="is");if(e.tag==="component"&&n&&n.value&&n.value.kind==="hole"){let i=e.attributes.filter(l=>l!==n),o=this.lowerComponent({...e,kind:"Component",tag:"Dynamic",tagHole:null,attributes:i});return o.props.unshift({name:"component",kind:"attr",expr:`() => (${this.hole(n.value.hole).code})`}),o}let r=this.tagAt(e);return{kind:"element",tag:e.tag,svg:e.namespace==="svg",props:this.lowerAttributes(e.attributes),children:this.lowerChildren(e.children),static:!1,...r?{at:r}:{}}}lowerComponent(e){let n=e.tagHole!=null?this.hole(e.tagHole).code:e.tag,r=this.lowerAttributes(e.attributes),{slots:i,rest:o}=this.partitionSlots(e.children);for(let[a,d]of i){let p=d.length===1?d[0]:{kind:"fragment",children:d},h={name:a,kind:"attr",expr:this.emit(p),jsxElement:!0},g=r.findIndex(y=>y.name===a);g>=0?r[g]=h:r.push(h)}let l=e.attributes.find(a=>a.kind==="LoadDirective"),s=l?P(`load:${l.strategy}`,l.modifier):void 0,u=r.find(a=>a.at&&a.reactive)?.at,c=this.tagAt(e);return{kind:"component",name:n,props:r,children:this.lowerChildren(o),...c?{at:c}:{},...u?{bindAt:u}:{},...s?{load:s}:{}}}partitionSlots(e){let n=new Map,r=[];for(let i of e){if(i.kind==="Element"||i.kind==="Component"){let o=i.attributes.find(l=>l.kind==="Attribute"&&l.name==="slot");if(o&&o.value&&o.value.kind==="static"){let l={...i,attributes:i.attributes.filter(c=>c!==o)},s=l.kind==="Element"?this.lowerElement(l):this.lowerComponent(l),u=n.get(o.value.value)??[];u.push(s),n.set(o.value.value,u);continue}}r.push(i)}return{slots:n,rest:r}}lowerIf(e,n,r){let i=this.hole(n),o=[{name:"when",kind:"attr",expr:i.code,reactive:i.reactive,...i.at?{at:i.at}:{}}];if(r){let u=this.stripAndLower(r,c=>c.kind==="ElseDirective");o.push({name:"fallback",kind:"attr",expr:this.emit(u),jsxElement:!0})}let l=this.stripAndLower(e,u=>u.kind==="IfDirective"),s=this.tagAt(e);return{kind:"component",name:"Show",props:o,children:[l],...s?{at:s}:{},...i.at?{bindAt:i.at}:{}}}lowerEach(e){let n=e.attributes.find(c=>c.kind==="EachDirective");if(!n||n.kind!=="EachDirective")return this.lowerNode(e);let r=this.hole(n.hole),i=[{name:"each",kind:"attr",expr:r.code,reactive:r.reactive,...r.at?{at:r.at}:{}}];if(n.key){let c="static"in n.key?`(item) => item[${JSON.stringify(n.key.static)}]`:this.hole(n.key.hole).code;i.push({name:"by",kind:"attr",expr:c})}let o=this.itemArrow(e),l;if(o){let c={kind:"expr",code:o.body,reactive:o.bodyReactive},a=this.rebuildWithChildren(e,[c]);l=`(${o.params.join(", ")}) => (${this.emit(a)})`}else{let c=this.stripAndLower(e,a=>a.kind==="EachDirective");l=`() => (${this.emit(c)})`}let s=[{kind:"expr",code:l,reactive:!1}],u=this.tagAt(e);return{kind:"component",name:"For",props:i,children:s,...u?{at:u}:{},...r.at?{bindAt:r.at}:{}}}itemArrow(e){let n=e.children.filter(r=>!this.isBlankText(r));return n.length!==1||n[0].kind!=="Expression"?null:this.hole(n[0].hole).arrow??null}stripAndLower(e,n){let r={...e,attributes:e.attributes.filter(i=>!n(i))};return r.kind==="Element"?this.lowerElement(r):this.lowerComponent(r)}rebuildWithChildren(e,n){let r=this.lowerAttributes(e.attributes.filter(i=>i.kind!=="EachDirective"));if(e.kind==="Element"){let i=this.tagAt(e);return{kind:"element",tag:e.tag,svg:e.namespace==="svg",props:r,children:n,static:!1,...i?{at:i}:{}}}return{kind:"component",name:e.tag,props:r,children:n}}lowerAttributes(e){let n=[],r=[],i=!1,o,l=[],s=!1,u,c=[];for(let a of e)switch(a.kind){case"IfDirective":case"EachDirective":case"ElseDirective":break;case"Attribute":n.push(this.plainAttr(a));break;case"PropertyBinding":n.push({name:a.name,kind:"prop",expr:this.hole(a.hole).code,reactive:this.hole(a.hole).reactive,...this.hole(a.hole).at?{at:this.hole(a.hole).at}:{}});break;case"EventBinding":n.push(this.eventProp(a));break;case"RefBinding":n.push({name:"ref",kind:"ref",expr:this.hole(a.hole).code,reactive:this.hole(a.hole).reactive,...this.hole(a.hole).at?{at:this.hole(a.hole).at}:{}});break;case"Spread":n.push({name:"",kind:"spread",expr:this.hole(a.hole).code});break;case"ClassDirective":{let d=this.hole(a.hole);r.push(`${JSON.stringify(a.name)}: ${d.code}`),i=i||d.reactive,o??=d.at;break}case"StyleDirective":{let d=this.hole(a.hole);l.push(`${JSON.stringify(a.name)}: ${d.code}`),s=s||d.reactive,u??=d.at;break}case"BindDirective":n.push(...this.bindProps(a.name,this.hole(a.hole)));break;case"UseDirective":{let d=a.name??(a.hole!=null?this.hole(a.hole).code:null);if(!d)break;c.push(a.name!=null&&a.hole!=null?`[${d}, () => (${this.hole(a.hole).code})]`:`[${d}]`);break}}return r.length>0&&n.push({name:"classList",kind:"attr",expr:`{ ${r.join(", ")} }`,reactive:i,...o?{at:o}:{}}),l.length>0&&n.push({name:"style",kind:"attr",expr:`{ ${l.join(", ")} }`,reactive:s,...u?{at:u}:{}}),c.length>0&&n.push({name:"use",kind:"attr",expr:`[${c.join(", ")}]`}),n}eventProp(e){let n=e.name.toLowerCase(),r=this.hole(e.hole).code;if(!(e.syntax==="colon"||e.modifiers.length>0))return{name:"on"+Y(e.name),kind:"event",event:{name:n,delegated:G.has(n)},expr:r};let o=this.wrapHandler(r,e.modifiers),l=this.eventOptions(e.modifiers),s=l?`[${o}, ${l}]`:o;return{name:"on:"+n,kind:"attr",expr:s}}wrapHandler(e,n){let r=n.includes("self")?"if (e.target !== e.currentTarget) return; ":"",i=[];return n.includes("prevent")&&i.push("e.preventDefault();"),n.includes("stop")&&i.push("e.stopPropagation();"),!r&&i.length===0?e:`(e) => { ${r}${i.join(" ")} return (${e})(e); }`}eventOptions(e){let n=[];return e.includes("capture")&&n.push("capture: true"),e.includes("once")&&n.push("once: true"),e.includes("passive")&&n.push("passive: true"),n.length?`{ ${n.join(", ")} }`:null}bindProps(e,n){let r=n.code,i=e==="checked",o=i?"change":"input",l=i?"checked":"value";this.used.add("bindPair");let s=`bindPair(${r})`;return[{name:e,kind:"attr",expr:`${s}[0]()`,reactive:!0,directive:`bind:${e}`,...n.at?{at:n.at}:{}},{name:"on"+Y(o),kind:"event",event:{name:o,delegated:G.has(o)},expr:`(e) => ${s}[1](e.target.${l})`}]}plainAttr(e){let n=e.value,r=e.name;e.name==="class"?r=n&&n.kind==="hole"&&this.hole(n.hole).object?"classList":"className":e.name==="html"&&(r="innerHTML");let o={name:r,kind:"attr"};if(n==null)return o.literal=!0,o;if(n.kind==="static")return o.literal=n.value,o;if(n.kind==="hole"){let c=this.hole(n.hole);return o.expr=c.code,o.reactive=c.reactive,c.at&&(o.at=c.at),o}let l=!1,s,u=n.parts.map(c=>{if("text"in c)return be(c.text);let a=this.hole(c.hole);return l=l||a.reactive,s??=a.at,"${"+a.code+"}"}).join("");return o.expr="`"+u+"`",o.reactive=l,s&&(o.at=s),o}};function qe(t,e,n,r={}){let{root:i}=ke(t,{svg:r.svg});return new A(e,n,r.sourceFile,r.positionAt).lowerRoot(i.children)}export{qe as lowerTemplate};
|
|
1
|
+
// src/lower/template.ts
|
|
2
|
+
import {
|
|
3
|
+
parseTemplate
|
|
4
|
+
} from "@fluixi/template-parser";
|
|
5
|
+
|
|
6
|
+
// src/resolve/load-directive.ts
|
|
7
|
+
var EAGER = { kind: "eager" };
|
|
8
|
+
var DEFAULT_INTERACTION_EVENTS = ["pointerdown", "focusin", "keydown"];
|
|
9
|
+
var STRATEGIES = /* @__PURE__ */ new Set(["eager", "idle", "visible", "interaction", "media", "never"]);
|
|
10
|
+
var LoadDirectiveError = class extends Error {
|
|
11
|
+
};
|
|
12
|
+
function parseLoadDirective(name, value) {
|
|
13
|
+
const strategy = name.slice("load:".length);
|
|
14
|
+
if (!STRATEGIES.has(strategy)) {
|
|
15
|
+
throw new LoadDirectiveError(
|
|
16
|
+
`Unknown load strategy 'load:${strategy}'. Expected one of ${[...STRATEGIES].join(", ")}.`
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
switch (strategy) {
|
|
20
|
+
case "eager":
|
|
21
|
+
return EAGER;
|
|
22
|
+
case "idle":
|
|
23
|
+
return { kind: "idle" };
|
|
24
|
+
case "never":
|
|
25
|
+
return { kind: "never" };
|
|
26
|
+
case "visible":
|
|
27
|
+
return value ? { kind: "visible", rootMargin: value } : { kind: "visible" };
|
|
28
|
+
case "interaction":
|
|
29
|
+
return {
|
|
30
|
+
kind: "interaction",
|
|
31
|
+
events: value ? splitEvents(value) : [...DEFAULT_INTERACTION_EVENTS]
|
|
32
|
+
};
|
|
33
|
+
case "media":
|
|
34
|
+
if (!value) {
|
|
35
|
+
throw new LoadDirectiveError(
|
|
36
|
+
`'load:media' needs a query, e.g. load:media="(min-width: 1024px)".`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
return { kind: "media", query: value };
|
|
40
|
+
default:
|
|
41
|
+
throw new LoadDirectiveError(`Unhandled load strategy '${strategy}'.`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function splitEvents(value) {
|
|
45
|
+
const events = value.split(/[\s,]+/).map((e) => e.trim()).filter(Boolean);
|
|
46
|
+
if (events.length === 0) {
|
|
47
|
+
throw new LoadDirectiveError(`'load:interaction' was given no event names.`);
|
|
48
|
+
}
|
|
49
|
+
return events;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/codegen/serialize-static.ts
|
|
53
|
+
var VOID_ELEMENTS = /* @__PURE__ */ new Set([
|
|
54
|
+
"area",
|
|
55
|
+
"base",
|
|
56
|
+
"br",
|
|
57
|
+
"col",
|
|
58
|
+
"embed",
|
|
59
|
+
"hr",
|
|
60
|
+
"img",
|
|
61
|
+
"input",
|
|
62
|
+
"link",
|
|
63
|
+
"meta",
|
|
64
|
+
"param",
|
|
65
|
+
"source",
|
|
66
|
+
"track",
|
|
67
|
+
"wbr"
|
|
68
|
+
]);
|
|
69
|
+
var HTML_NAME = {
|
|
70
|
+
className: "class",
|
|
71
|
+
htmlFor: "for"
|
|
72
|
+
};
|
|
73
|
+
var PROPERTY_ONLY = /* @__PURE__ */ new Set([
|
|
74
|
+
"innerHTML",
|
|
75
|
+
"outerHTML",
|
|
76
|
+
"innerText",
|
|
77
|
+
"textContent",
|
|
78
|
+
"value",
|
|
79
|
+
"checked",
|
|
80
|
+
"selected",
|
|
81
|
+
"muted",
|
|
82
|
+
"defaultValue",
|
|
83
|
+
"defaultChecked",
|
|
84
|
+
"children",
|
|
85
|
+
"ref"
|
|
86
|
+
]);
|
|
87
|
+
var RAW_TEXT_ELEMENTS = /* @__PURE__ */ new Set(["script", "style", "textarea", "title"]);
|
|
88
|
+
function isRawTextElement(tag) {
|
|
89
|
+
return RAW_TEXT_ELEMENTS.has(tag);
|
|
90
|
+
}
|
|
91
|
+
function htmlAttrName(name) {
|
|
92
|
+
return HTML_NAME[name] ?? name;
|
|
93
|
+
}
|
|
94
|
+
function isPlainLiteralAttr(prop) {
|
|
95
|
+
if (prop.kind !== "attr") return false;
|
|
96
|
+
if (prop.expr !== void 0) return false;
|
|
97
|
+
if (prop.name.includes(":")) return false;
|
|
98
|
+
return !PROPERTY_ONLY.has(prop.name);
|
|
99
|
+
}
|
|
100
|
+
function isSerializable(node) {
|
|
101
|
+
if (!node.static) return false;
|
|
102
|
+
if (isRawTextElement(node.tag)) return false;
|
|
103
|
+
for (const prop of node.props) {
|
|
104
|
+
if (!isPlainLiteralAttr(prop)) return false;
|
|
105
|
+
}
|
|
106
|
+
for (const child of node.children) {
|
|
107
|
+
if (child.kind === "text") continue;
|
|
108
|
+
if (child.kind === "element" && isSerializable(child)) continue;
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
function serializeStatic(node) {
|
|
114
|
+
const attrs = node.props.map(serializeProp).filter(Boolean).join("");
|
|
115
|
+
const open = `<${node.tag}${attrs}>`;
|
|
116
|
+
if (VOID_ELEMENTS.has(node.tag)) return open;
|
|
117
|
+
return `${open}${node.children.map(serializeChild).join("")}</${node.tag}>`;
|
|
118
|
+
}
|
|
119
|
+
function serializeChild(child) {
|
|
120
|
+
if (child.kind === "text") return escapeText(child.value);
|
|
121
|
+
if (child.kind !== "element") throw new Error(`serializeStatic: unexpected ${child.kind}`);
|
|
122
|
+
return serializeStatic(child);
|
|
123
|
+
}
|
|
124
|
+
function serializeProp(prop) {
|
|
125
|
+
const name = HTML_NAME[prop.name] ?? prop.name;
|
|
126
|
+
const value = prop.literal;
|
|
127
|
+
if (value === true || value === void 0) return ` ${name}`;
|
|
128
|
+
if (value === false || value === null) return "";
|
|
129
|
+
return ` ${name}="${escapeAttr(String(value))}"`;
|
|
130
|
+
}
|
|
131
|
+
function escapeAttr(value) {
|
|
132
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
133
|
+
}
|
|
134
|
+
function escapeText(value) {
|
|
135
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
136
|
+
}
|
|
137
|
+
function templatePositions(root) {
|
|
138
|
+
const pos = [];
|
|
139
|
+
let file;
|
|
140
|
+
const walk = (el) => {
|
|
141
|
+
file ??= el.at?.file;
|
|
142
|
+
pos.push(el.at?.line ?? 0, el.at?.column ?? 0);
|
|
143
|
+
for (const child of el.children) if (child.kind === "element") walk(child);
|
|
144
|
+
};
|
|
145
|
+
walk(root);
|
|
146
|
+
return file && pos.some((n) => n !== 0) ? { file, pos } : void 0;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// src/codegen/partial-template.ts
|
|
150
|
+
var HOLE_START = "<!--fx-->";
|
|
151
|
+
var HOLE_END = "<!--fx/-->";
|
|
152
|
+
function isHole(node) {
|
|
153
|
+
return node.kind === "expr" || node.kind === "component" || node.kind === "control";
|
|
154
|
+
}
|
|
155
|
+
function isTemplateChild(node) {
|
|
156
|
+
return node.kind === "text" || node.kind === "element" && isTemplateElement(node);
|
|
157
|
+
}
|
|
158
|
+
function isTemplateElement(node) {
|
|
159
|
+
if (node.svg) return false;
|
|
160
|
+
if (isRawTextElement(node.tag)) return false;
|
|
161
|
+
if (!node.props.every((p) => isPlainLiteralAttr(p))) return false;
|
|
162
|
+
return node.children.every((c) => isTemplateChild(c) || isHole(c));
|
|
163
|
+
}
|
|
164
|
+
function containsHole(node) {
|
|
165
|
+
if (isHole(node)) return true;
|
|
166
|
+
const children = node.children;
|
|
167
|
+
return children ? children.some(containsHole) : false;
|
|
168
|
+
}
|
|
169
|
+
function addressable(el) {
|
|
170
|
+
for (let i = 0; i < el.children.length; i++) {
|
|
171
|
+
const child = el.children[i];
|
|
172
|
+
if (child.kind === "text") {
|
|
173
|
+
if (child.value === "") return false;
|
|
174
|
+
if (el.children[i + 1]?.kind === "text") return false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return el.children.every((c) => c.kind !== "element" || addressable(c));
|
|
178
|
+
}
|
|
179
|
+
function planPartialTemplate(root) {
|
|
180
|
+
if (!isTemplateElement(root)) return null;
|
|
181
|
+
if (!addressable(root)) return null;
|
|
182
|
+
if (!root.children.some(containsHole)) return null;
|
|
183
|
+
const steps = [];
|
|
184
|
+
const holes = [];
|
|
185
|
+
let refCount = 0;
|
|
186
|
+
const declare = (expr) => {
|
|
187
|
+
const ref = `_n$${refCount++}`;
|
|
188
|
+
steps.push({ ref, expr });
|
|
189
|
+
return ref;
|
|
190
|
+
};
|
|
191
|
+
walk(root, "_el$");
|
|
192
|
+
return { html: serializeWithMarkers(root), tag: root.tag, steps, holes };
|
|
193
|
+
function walk(el, elementRef) {
|
|
194
|
+
let last = -1;
|
|
195
|
+
el.children.forEach((child, i) => {
|
|
196
|
+
if (containsHole(child)) last = i;
|
|
197
|
+
});
|
|
198
|
+
let previous = null;
|
|
199
|
+
for (let i = 0; i <= last; i++) {
|
|
200
|
+
const child = el.children[i];
|
|
201
|
+
const expr = previous ? `${previous}.nextSibling` : `${elementRef}.firstChild`;
|
|
202
|
+
if (isHole(child)) {
|
|
203
|
+
const startRef = declare(expr);
|
|
204
|
+
const endRef = declare(`holeEnd(${startRef})`);
|
|
205
|
+
holes.push({ parentRef: elementRef, startRef, endRef, node: child });
|
|
206
|
+
previous = endRef;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
const ref = declare(expr);
|
|
210
|
+
if (child.kind === "element" && containsHole(child)) walk(child, ref);
|
|
211
|
+
previous = ref;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function serializeWithMarkers(el) {
|
|
216
|
+
return serializeStatic(withSentinels(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
|
|
217
|
+
}
|
|
218
|
+
function withSentinels(el) {
|
|
219
|
+
return {
|
|
220
|
+
...el,
|
|
221
|
+
children: el.children.map(
|
|
222
|
+
(child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ? withSentinels(child) : child
|
|
223
|
+
)
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
var SENTINEL = "\0fx-hole\0";
|
|
227
|
+
|
|
228
|
+
// src/codegen/contract.ts
|
|
229
|
+
var RUNTIME_CONTRACT_V1 = {
|
|
230
|
+
version: 1,
|
|
231
|
+
module: "@fluixi/dom",
|
|
232
|
+
symbols: [
|
|
233
|
+
"createNativeElement",
|
|
234
|
+
"insert",
|
|
235
|
+
"spread",
|
|
236
|
+
"setAttribute",
|
|
237
|
+
"delegateEvents",
|
|
238
|
+
"createComponent",
|
|
239
|
+
"createMemo",
|
|
240
|
+
"untrack",
|
|
241
|
+
"Show",
|
|
242
|
+
"For",
|
|
243
|
+
"Index",
|
|
244
|
+
"Switch",
|
|
245
|
+
"Match",
|
|
246
|
+
"Dynamic",
|
|
247
|
+
"ErrorBoundary"
|
|
248
|
+
]
|
|
249
|
+
};
|
|
250
|
+
var RUNTIME_CONTRACT_V2 = {
|
|
251
|
+
version: 2,
|
|
252
|
+
module: "@fluixi/dom",
|
|
253
|
+
symbols: [...RUNTIME_CONTRACT_V1.symbols, "template", "cloneTemplate", "walk"]
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// src/analyze/intrinsics.ts
|
|
257
|
+
var REACTIVE_MODULE = "@fluixi/reactive/signal";
|
|
258
|
+
var INTRINSICS = {
|
|
259
|
+
$signal: { export: "signal", module: REACTIVE_MODULE, returns: "signal-handle" },
|
|
260
|
+
$memo: { export: "memo", module: REACTIVE_MODULE, returns: "memo-handle" },
|
|
261
|
+
$effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
|
|
262
|
+
$store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
|
|
263
|
+
$resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
|
|
264
|
+
// No short runtime form for these yet, so the intrinsic stands for the `create*`
|
|
265
|
+
// name. Still worth having: no import to write, and when a shorter form is
|
|
266
|
+
// designed, the change is this line rather than a compiler pass.
|
|
267
|
+
$selector: { export: "createSelector", module: REACTIVE_MODULE, returns: "memo-accessor" },
|
|
268
|
+
$deferred: { export: "createDeferred", module: REACTIVE_MODULE, returns: "memo-accessor" },
|
|
269
|
+
// Not value constructors, but the compiler has to know them: they change what a read
|
|
270
|
+
// does. `untrack` suppresses dependency collection, which is exactly the kind of thing
|
|
271
|
+
// dependency analysis must not guess at.
|
|
272
|
+
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
273
|
+
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" }
|
|
274
|
+
};
|
|
275
|
+
var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
|
|
276
|
+
|
|
277
|
+
// src/transform/source-locations.ts
|
|
278
|
+
var SOURCE_MARKER = "__fx_source";
|
|
279
|
+
var HOLE_MARKER = "__fx_hole";
|
|
280
|
+
var markHole = (accessor, line, column, name) => `(globalThis.${HOLE_MARKER} ?? ((f) => f))(${accessor},${line},${column}${name === void 0 ? "" : `,${JSON.stringify(name)}`})`;
|
|
281
|
+
var PROPS_MARKER = "__fx_props";
|
|
282
|
+
var markProps = (object, at) => `(globalThis.${PROPS_MARKER} ?? ((p) => p))(${object},${JSON.stringify(at)})`;
|
|
283
|
+
|
|
284
|
+
// src/analyze/paren.ts
|
|
285
|
+
function skipQuoted(src, start) {
|
|
286
|
+
const quote = src[start];
|
|
287
|
+
for (let i = start + 1; i < src.length; i++) {
|
|
288
|
+
const c = src[i];
|
|
289
|
+
if (c === "\\") i++;
|
|
290
|
+
else if (c === quote) return i + 1;
|
|
291
|
+
else if (c === "\n") return -1;
|
|
292
|
+
}
|
|
293
|
+
return -1;
|
|
294
|
+
}
|
|
295
|
+
function skipTemplate(src, start) {
|
|
296
|
+
let i = start + 1;
|
|
297
|
+
while (i < src.length) {
|
|
298
|
+
const c = src[i];
|
|
299
|
+
if (c === "\\") {
|
|
300
|
+
i += 2;
|
|
301
|
+
} else if (c === "`") {
|
|
302
|
+
return i + 1;
|
|
303
|
+
} else if (c === "$" && src[i + 1] === "{") {
|
|
304
|
+
let depth = 1;
|
|
305
|
+
i += 2;
|
|
306
|
+
while (i < src.length && depth > 0) {
|
|
307
|
+
const k = src[i];
|
|
308
|
+
if (k === "\\") i += 2;
|
|
309
|
+
else if (k === "`") {
|
|
310
|
+
const end = skipTemplate(src, i);
|
|
311
|
+
if (end < 0) return -1;
|
|
312
|
+
i = end;
|
|
313
|
+
} else if (k === '"' || k === "'") {
|
|
314
|
+
const end = skipQuoted(src, i);
|
|
315
|
+
if (end < 0) return -1;
|
|
316
|
+
i = end;
|
|
317
|
+
} else {
|
|
318
|
+
if (k === "{") depth++;
|
|
319
|
+
else if (k === "}") depth--;
|
|
320
|
+
i++;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
} else {
|
|
324
|
+
i++;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return -1;
|
|
328
|
+
}
|
|
329
|
+
function skipRegex(src, start) {
|
|
330
|
+
let inClass = false;
|
|
331
|
+
for (let i = start + 1; i < src.length; i++) {
|
|
332
|
+
const c = src[i];
|
|
333
|
+
if (c === "\\") i++;
|
|
334
|
+
else if (c === "[") inClass = true;
|
|
335
|
+
else if (c === "]") inClass = false;
|
|
336
|
+
else if (c === "\n") return -1;
|
|
337
|
+
else if (c === "/" && !inClass) {
|
|
338
|
+
let end = i + 1;
|
|
339
|
+
while (end < src.length && /[a-z]/i.test(src[end])) end++;
|
|
340
|
+
return end;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return -1;
|
|
344
|
+
}
|
|
345
|
+
function divides(prev) {
|
|
346
|
+
return prev !== "" && /[A-Za-z0-9_$)\]]/.test(prev);
|
|
347
|
+
}
|
|
348
|
+
function topLevelComma(src) {
|
|
349
|
+
let depth = 0;
|
|
350
|
+
let prev = "";
|
|
351
|
+
let i = 0;
|
|
352
|
+
while (i < src.length) {
|
|
353
|
+
const c = src[i];
|
|
354
|
+
if (c === '"' || c === "'") {
|
|
355
|
+
const end = skipQuoted(src, i);
|
|
356
|
+
if (end < 0) return null;
|
|
357
|
+
i = end;
|
|
358
|
+
prev = c;
|
|
359
|
+
} else if (c === "`") {
|
|
360
|
+
const end = skipTemplate(src, i);
|
|
361
|
+
if (end < 0) return null;
|
|
362
|
+
i = end;
|
|
363
|
+
prev = c;
|
|
364
|
+
} else if (c === "/" && src[i + 1] === "/") {
|
|
365
|
+
return null;
|
|
366
|
+
} else if (c === "/" && src[i + 1] === "*") {
|
|
367
|
+
const end = src.indexOf("*/", i + 2);
|
|
368
|
+
if (end < 0) return null;
|
|
369
|
+
i = end + 2;
|
|
370
|
+
} else if (c === "/" && !divides(prev)) {
|
|
371
|
+
const end = skipRegex(src, i);
|
|
372
|
+
if (end < 0) return null;
|
|
373
|
+
i = end;
|
|
374
|
+
prev = "/";
|
|
375
|
+
} else {
|
|
376
|
+
if (c === "(" || c === "[" || c === "{") depth++;
|
|
377
|
+
else if (c === ")" || c === "]" || c === "}") {
|
|
378
|
+
depth--;
|
|
379
|
+
if (depth < 0) return null;
|
|
380
|
+
} else if (c === "," && depth === 0) return true;
|
|
381
|
+
if (!/\s/.test(c)) prev = c;
|
|
382
|
+
i++;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return depth === 0 ? false : null;
|
|
386
|
+
}
|
|
387
|
+
function paren(code, position) {
|
|
388
|
+
const src = code.trim();
|
|
389
|
+
if (src === "") return `(${code})`;
|
|
390
|
+
if (position === "arrowBody" && src[0] === "{") return `(${code})`;
|
|
391
|
+
return topLevelComma(src) === false ? code : `(${code})`;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// src/codegen/backends/imperative.ts
|
|
395
|
+
var CONTROL_COMPONENT = {
|
|
396
|
+
show: "Show",
|
|
397
|
+
for: "For",
|
|
398
|
+
index: "Index",
|
|
399
|
+
switch: "Switch",
|
|
400
|
+
match: "Match",
|
|
401
|
+
dynamic: "Dynamic",
|
|
402
|
+
errorBoundary: "ErrorBoundary",
|
|
403
|
+
suspense: "Suspense"
|
|
404
|
+
};
|
|
405
|
+
function isIdent(name) {
|
|
406
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
|
|
407
|
+
}
|
|
408
|
+
function propKey(name) {
|
|
409
|
+
return isIdent(name) ? name : JSON.stringify(name);
|
|
410
|
+
}
|
|
411
|
+
function propValue(p) {
|
|
412
|
+
if (p.expr !== void 0) {
|
|
413
|
+
if (p.name === "ref" && p.at)
|
|
414
|
+
return markHole(paren(p.expr, "value"), p.at.line, p.at.column, p.expr);
|
|
415
|
+
if (!p.reactive) return paren(p.expr, "value");
|
|
416
|
+
const accessor = `() => ${paren(p.expr, "arrowBody")}`;
|
|
417
|
+
return p.at ? markHole(accessor, p.at.line, p.at.column, p.directive) : accessor;
|
|
418
|
+
}
|
|
419
|
+
return JSON.stringify(p.literal ?? true);
|
|
420
|
+
}
|
|
421
|
+
function componentPropValue(p) {
|
|
422
|
+
if (p.expr !== void 0) return paren(p.expr, "value");
|
|
423
|
+
return JSON.stringify(p.literal ?? true);
|
|
424
|
+
}
|
|
425
|
+
function emitProps(props, childrenExpr, used) {
|
|
426
|
+
const spreads = props.filter((p) => p.kind === "spread");
|
|
427
|
+
const normal = props.filter((p) => p.kind !== "spread");
|
|
428
|
+
const entries = normal.map((p) => `${propKey(p.name)}: ${propValue(p)}`);
|
|
429
|
+
if (childrenExpr != null) entries.push(`children: ${childrenExpr}`);
|
|
430
|
+
const obj = `{ ${entries.join(", ")} }`;
|
|
431
|
+
if (spreads.length > 0) {
|
|
432
|
+
used.add("mergeProps");
|
|
433
|
+
return `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
434
|
+
}
|
|
435
|
+
return obj;
|
|
436
|
+
}
|
|
437
|
+
function markBind(code, at) {
|
|
438
|
+
return at ? markHole(code, at.line, at.column) : code;
|
|
439
|
+
}
|
|
440
|
+
function emitChildren(children, used, templates) {
|
|
441
|
+
if (children.length === 1) return emitNode(children[0], used, templates);
|
|
442
|
+
return `[${children.map((c) => emitNode(c, used, templates)).join(", ")}]`;
|
|
443
|
+
}
|
|
444
|
+
function emitComponent(name, props, children, used, templates, at) {
|
|
445
|
+
used.add("createMemo");
|
|
446
|
+
used.add("createComponent");
|
|
447
|
+
const spreads = props.filter((p) => p.kind === "spread");
|
|
448
|
+
const normal = props.filter((p) => p.kind !== "spread");
|
|
449
|
+
const entries = normal.map(
|
|
450
|
+
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
451
|
+
);
|
|
452
|
+
if (children.length > 0) {
|
|
453
|
+
entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
|
|
454
|
+
}
|
|
455
|
+
let obj = `{ ${entries.join(", ")} }`;
|
|
456
|
+
if (spreads.length > 0) {
|
|
457
|
+
used.add("mergeProps");
|
|
458
|
+
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
459
|
+
}
|
|
460
|
+
const propAt = {};
|
|
461
|
+
for (const p of normal) if (p.at) propAt[p.name] = [p.at.line, p.at.column];
|
|
462
|
+
if (Object.keys(propAt).length) obj = markProps(obj, propAt);
|
|
463
|
+
const mark = at ? `globalThis.${SOURCE_MARKER}?.(${JSON.stringify(at.file)},${at.line},${at.column},${JSON.stringify(name)}), ` : "";
|
|
464
|
+
return `createMemo(${paren(`${mark}() => createComponent(${name}, ${obj})`, "value")})`;
|
|
465
|
+
}
|
|
466
|
+
function staticAttrAssignment(el, prop) {
|
|
467
|
+
const name = htmlAttrName(prop.name);
|
|
468
|
+
const value = prop.literal;
|
|
469
|
+
if (value === true || value === void 0) return `${el}.setAttribute(${JSON.stringify(name)}, "");`;
|
|
470
|
+
if (value === false || value === null) return "";
|
|
471
|
+
return `${el}.setAttribute(${JSON.stringify(name)}, ${JSON.stringify(String(value))});`;
|
|
472
|
+
}
|
|
473
|
+
var partial = false;
|
|
474
|
+
function emitNode(node, used, templates) {
|
|
475
|
+
switch (node.kind) {
|
|
476
|
+
case "text":
|
|
477
|
+
return JSON.stringify(node.value);
|
|
478
|
+
case "expr": {
|
|
479
|
+
if (!node.reactive) return paren(node.code, "value");
|
|
480
|
+
const accessor = `() => ${paren(node.code, "arrowBody")}`;
|
|
481
|
+
return node.at ? markHole(accessor, node.at.line, node.at.column) : accessor;
|
|
482
|
+
}
|
|
483
|
+
case "fragment":
|
|
484
|
+
if (node.children.length === 0) return "null";
|
|
485
|
+
return emitChildren(node.children, used, templates);
|
|
486
|
+
case "component":
|
|
487
|
+
return markBind(emitComponent(node.name, node.props, node.children, used, templates, node.at), node.bindAt ?? node.at);
|
|
488
|
+
case "control": {
|
|
489
|
+
const name = CONTROL_COMPONENT[node.control] ?? node.control;
|
|
490
|
+
used.add(name);
|
|
491
|
+
return markBind(emitComponent(name, node.props, node.children, used, templates), node.bindAt);
|
|
492
|
+
}
|
|
493
|
+
case "element": {
|
|
494
|
+
if (templates && node.static && !node.svg && isSerializable(node)) {
|
|
495
|
+
used.add("templateNode");
|
|
496
|
+
const id = `_tmpl$${templates.length}`;
|
|
497
|
+
templates.push({ id, html: serializeStatic(node), tag: node.tag, svg: false });
|
|
498
|
+
const at = templatePositions(node);
|
|
499
|
+
return at ? `templateNode(${id}, ${JSON.stringify(node.tag)}, false, false, ${JSON.stringify(at)})` : `templateNode(${id}, ${JSON.stringify(node.tag)})`;
|
|
500
|
+
}
|
|
501
|
+
if (templates && partial) {
|
|
502
|
+
const plan = planPartialTemplate(node);
|
|
503
|
+
if (plan) {
|
|
504
|
+
used.add("templateNode");
|
|
505
|
+
used.add("insert");
|
|
506
|
+
used.add("holeEnd");
|
|
507
|
+
used.add("holeContent");
|
|
508
|
+
used.add("holeScope");
|
|
509
|
+
const id = `_tmpl$${templates.length}`;
|
|
510
|
+
templates.push({ id, html: plan.html, tag: plan.tag, svg: false });
|
|
511
|
+
const at = templatePositions(node);
|
|
512
|
+
const lines = [
|
|
513
|
+
`const _el$ = templateNode(${id}, ${JSON.stringify(plan.tag)}, true${at ? `, false, ${JSON.stringify(at)}` : ""});`
|
|
514
|
+
];
|
|
515
|
+
for (const step of plan.steps) lines.push(`const ${step.ref} = ${step.expr};`);
|
|
516
|
+
for (const hole of plan.holes) {
|
|
517
|
+
lines.push(
|
|
518
|
+
`insert(${hole.parentRef}, holeScope(${hole.startRef}, () => ${paren(emitNode(hole.node, used, templates), "arrowBody")}), ${hole.endRef}, holeContent(${hole.startRef}, ${hole.endRef}));`
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
lines.push("return _el$;");
|
|
522
|
+
return `(() => { ${lines.join(" ")} })()`;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
used.add("createNativeElement");
|
|
526
|
+
const tag = JSON.stringify(node.tag);
|
|
527
|
+
const el = "_el$";
|
|
528
|
+
const parts = [];
|
|
529
|
+
const createArgs = node.at ? `${tag}, ${node.svg}, ${JSON.stringify(node.at)}` : node.svg ? `${tag}, true` : tag;
|
|
530
|
+
parts.push(`const ${el} = createNativeElement(${createArgs});`);
|
|
531
|
+
if (node.props.length > 0) {
|
|
532
|
+
if (!node.svg && node.props.every(isPlainLiteralAttr)) {
|
|
533
|
+
for (const p of node.props) parts.push(staticAttrAssignment(el, p));
|
|
534
|
+
} else {
|
|
535
|
+
used.add("spread");
|
|
536
|
+
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
537
|
+
parts.push(`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag} });`);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
for (const child of node.children) {
|
|
541
|
+
used.add("insert");
|
|
542
|
+
const childCode = emitNode(child, used, templates);
|
|
543
|
+
const isFn = child.kind === "expr" && child.reactive || child.kind === "component" || child.kind === "control";
|
|
544
|
+
parts.push(isFn ? `insert(${el}, ${childCode}, null);` : `insert(${el}, ${childCode});`);
|
|
545
|
+
}
|
|
546
|
+
parts.push(`return ${el};`);
|
|
547
|
+
return `(() => { ${parts.join(" ")} })()`;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
function inlineTemplates(code, templates) {
|
|
552
|
+
if (!templates || templates.length === 0) return code;
|
|
553
|
+
const byId = new Map(templates.map((t) => [t.id, JSON.stringify(t.html)]));
|
|
554
|
+
return code.replace(/_tmpl\$\d+/g, (id) => byId.get(id) ?? id);
|
|
555
|
+
}
|
|
556
|
+
var imperativeBackend = {
|
|
557
|
+
name: "imperative",
|
|
558
|
+
contract: RUNTIME_CONTRACT_V1,
|
|
559
|
+
emit(node, options) {
|
|
560
|
+
const used = /* @__PURE__ */ new Set();
|
|
561
|
+
const templates = options?.templateClone !== false ? [] : void 0;
|
|
562
|
+
partial = options?.partialTemplates === true;
|
|
563
|
+
const code = emitNode(node, used, templates);
|
|
564
|
+
return { code, imports: Array.from(used), templates };
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
// src/lower/text.ts
|
|
569
|
+
function cleanTemplateText(value) {
|
|
570
|
+
const lines = value.split(/\r\n|\n|\r/);
|
|
571
|
+
let lastNonEmpty = 0;
|
|
572
|
+
for (let i = 0; i < lines.length; i++) {
|
|
573
|
+
if (/[^ \t]/.test(lines[i])) lastNonEmpty = i;
|
|
574
|
+
}
|
|
575
|
+
let out = "";
|
|
576
|
+
for (let i = 0; i < lines.length; i++) {
|
|
577
|
+
let line = lines[i].replace(/\t/g, " ");
|
|
578
|
+
if (i !== 0) line = line.replace(/^ +/, "");
|
|
579
|
+
if (i !== lines.length - 1) line = line.replace(/ +$/, "");
|
|
580
|
+
if (!line) continue;
|
|
581
|
+
if (i !== lastNonEmpty) line += " ";
|
|
582
|
+
out += line;
|
|
583
|
+
}
|
|
584
|
+
return out;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// src/lower/template.ts
|
|
588
|
+
var DELEGATED = /* @__PURE__ */ new Set([
|
|
589
|
+
"click",
|
|
590
|
+
"dblclick",
|
|
591
|
+
"input",
|
|
592
|
+
"change",
|
|
593
|
+
"submit",
|
|
594
|
+
"focus",
|
|
595
|
+
"blur",
|
|
596
|
+
"keydown",
|
|
597
|
+
"keyup",
|
|
598
|
+
"keypress",
|
|
599
|
+
"mousedown",
|
|
600
|
+
"mouseup"
|
|
601
|
+
]);
|
|
602
|
+
function escapeTemplate(s) {
|
|
603
|
+
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
604
|
+
}
|
|
605
|
+
function capitalize(s) {
|
|
606
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
607
|
+
}
|
|
608
|
+
var Lowerer = class {
|
|
609
|
+
constructor(holes, used, sourceFile, positionAt) {
|
|
610
|
+
this.holes = holes;
|
|
611
|
+
this.used = used;
|
|
612
|
+
this.sourceFile = sourceFile;
|
|
613
|
+
this.positionAt = positionAt;
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Where a tag is written, for the mark a component node carries.
|
|
617
|
+
*
|
|
618
|
+
* The parser reports offsets, and they are offsets into the file: the pieces it was given
|
|
619
|
+
* carry the positions the front-end recorded. Only built when the host asked for source
|
|
620
|
+
* locations, which is the only time either of these is set.
|
|
621
|
+
*/
|
|
622
|
+
tagAt(node) {
|
|
623
|
+
const start = node.loc?.start;
|
|
624
|
+
if (!this.sourceFile || !this.positionAt || start == null) return void 0;
|
|
625
|
+
const { line, column } = this.positionAt(start);
|
|
626
|
+
return { file: this.sourceFile, line, column };
|
|
627
|
+
}
|
|
628
|
+
hole(i) {
|
|
629
|
+
return this.holes[i] ?? { code: "undefined", reactive: false };
|
|
630
|
+
}
|
|
631
|
+
/** Emit a nested IR subtree to code (for `Show` fallback / `For` children). */
|
|
632
|
+
emit(node) {
|
|
633
|
+
const { code, imports, templates } = imperativeBackend.emit(node, {});
|
|
634
|
+
for (const s of imports) this.used.add(s);
|
|
635
|
+
return inlineTemplates(code, templates);
|
|
636
|
+
}
|
|
637
|
+
// --- content ------------------------------------------------------------
|
|
638
|
+
lowerRoot(children) {
|
|
639
|
+
const nodes = this.lowerChildren(children);
|
|
640
|
+
if (nodes.length === 1) return nodes[0];
|
|
641
|
+
return { kind: "fragment", children: nodes };
|
|
642
|
+
}
|
|
643
|
+
/** Lower a sibling list, handling text-drop and if/else/each restructuring. */
|
|
644
|
+
lowerChildren(children) {
|
|
645
|
+
const out = [];
|
|
646
|
+
for (let i = 0; i < children.length; i++) {
|
|
647
|
+
const child = children[i];
|
|
648
|
+
if (child.kind === "Element" || child.kind === "Component") {
|
|
649
|
+
const ifDir = child.attributes.find((a) => a.kind === "IfDirective");
|
|
650
|
+
if (ifDir && ifDir.kind === "IfDirective") {
|
|
651
|
+
let elseIdx = i + 1;
|
|
652
|
+
if (elseIdx < children.length && this.isBlankText(children[elseIdx])) elseIdx++;
|
|
653
|
+
const elseNode = children[elseIdx];
|
|
654
|
+
const hasElse = elseNode && (elseNode.kind === "Element" || elseNode.kind === "Component") && elseNode.attributes.some((a) => a.kind === "ElseDirective");
|
|
655
|
+
out.push(this.lowerIf(child, ifDir.hole, hasElse ? elseNode : null));
|
|
656
|
+
if (hasElse) i = elseIdx;
|
|
657
|
+
continue;
|
|
658
|
+
}
|
|
659
|
+
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
660
|
+
out.push(this.lowerEach(child));
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
const node = this.lowerNode(child);
|
|
665
|
+
if (node) out.push(node);
|
|
666
|
+
}
|
|
667
|
+
return out;
|
|
668
|
+
}
|
|
669
|
+
isBlankText(n) {
|
|
670
|
+
return n.kind === "Text" && !n.raw && cleanTemplateText(n.value) === "";
|
|
671
|
+
}
|
|
672
|
+
lowerNode(node) {
|
|
673
|
+
switch (node.kind) {
|
|
674
|
+
case "Text": {
|
|
675
|
+
if (node.raw) return { kind: "text", value: node.value };
|
|
676
|
+
const cleaned = cleanTemplateText(node.value);
|
|
677
|
+
return cleaned ? { kind: "text", value: cleaned } : null;
|
|
678
|
+
}
|
|
679
|
+
case "Comment":
|
|
680
|
+
return null;
|
|
681
|
+
// dropped, like JSX comments
|
|
682
|
+
case "Expression": {
|
|
683
|
+
const h = this.hole(node.hole);
|
|
684
|
+
return { kind: "expr", code: h.code, reactive: h.reactive, ...h.at ? { at: h.at } : {} };
|
|
685
|
+
}
|
|
686
|
+
case "Fragment":
|
|
687
|
+
return { kind: "fragment", children: this.lowerChildren(node.children) };
|
|
688
|
+
case "Element":
|
|
689
|
+
return this.lowerElement(node);
|
|
690
|
+
case "Component":
|
|
691
|
+
return this.lowerComponent(node);
|
|
692
|
+
default:
|
|
693
|
+
return null;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
lowerElement(el) {
|
|
697
|
+
const isAttr = el.attributes.find((a) => a.kind === "Attribute" && a.name === "is");
|
|
698
|
+
if (el.tag === "component" && isAttr && isAttr.value && isAttr.value.kind === "hole") {
|
|
699
|
+
const rest = el.attributes.filter((a) => a !== isAttr);
|
|
700
|
+
const node = this.lowerComponent({ ...el, kind: "Component", tag: "Dynamic", tagHole: null, attributes: rest });
|
|
701
|
+
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
702
|
+
return node;
|
|
703
|
+
}
|
|
704
|
+
const at = this.tagAt(el);
|
|
705
|
+
return {
|
|
706
|
+
kind: "element",
|
|
707
|
+
tag: el.tag,
|
|
708
|
+
svg: el.namespace === "svg",
|
|
709
|
+
props: this.lowerAttributes(el.attributes),
|
|
710
|
+
children: this.lowerChildren(el.children),
|
|
711
|
+
static: false,
|
|
712
|
+
...at ? { at } : {}
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
lowerComponent(el) {
|
|
716
|
+
const name = el.tagHole != null ? this.hole(el.tagHole).code : el.tag;
|
|
717
|
+
const props = this.lowerAttributes(el.attributes);
|
|
718
|
+
const { slots, rest } = this.partitionSlots(el.children);
|
|
719
|
+
for (const [slotName, nodes] of slots) {
|
|
720
|
+
const ir = nodes.length === 1 ? nodes[0] : { kind: "fragment", children: nodes };
|
|
721
|
+
const slotProp = {
|
|
722
|
+
name: slotName,
|
|
723
|
+
kind: "attr",
|
|
724
|
+
expr: this.emit(ir),
|
|
725
|
+
jsxElement: true
|
|
726
|
+
};
|
|
727
|
+
const clash = props.findIndex((p) => p.name === slotName);
|
|
728
|
+
if (clash >= 0) props[clash] = slotProp;
|
|
729
|
+
else props.push(slotProp);
|
|
730
|
+
}
|
|
731
|
+
const loadAttr = el.attributes.find((a) => a.kind === "LoadDirective");
|
|
732
|
+
const load = loadAttr ? parseLoadDirective(`load:${loadAttr.strategy}`, loadAttr.modifier) : void 0;
|
|
733
|
+
const bindAt = props.find((p) => p.at && p.reactive)?.at;
|
|
734
|
+
const at = this.tagAt(el);
|
|
735
|
+
return {
|
|
736
|
+
kind: "component",
|
|
737
|
+
name,
|
|
738
|
+
props,
|
|
739
|
+
children: this.lowerChildren(rest),
|
|
740
|
+
...at ? { at } : {},
|
|
741
|
+
...bindAt ? { bindAt } : {},
|
|
742
|
+
...load ? { load } : {}
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
/** Split a component's children into named slots (`slot="x"`) and the rest. */
|
|
746
|
+
partitionSlots(children) {
|
|
747
|
+
const slots = /* @__PURE__ */ new Map();
|
|
748
|
+
const rest = [];
|
|
749
|
+
for (const child of children) {
|
|
750
|
+
if (child.kind === "Element" || child.kind === "Component") {
|
|
751
|
+
const slot = child.attributes.find(
|
|
752
|
+
(a) => a.kind === "Attribute" && a.name === "slot"
|
|
753
|
+
);
|
|
754
|
+
if (slot && slot.value && slot.value.kind === "static") {
|
|
755
|
+
const stripped = { ...child, attributes: child.attributes.filter((a) => a !== slot) };
|
|
756
|
+
const ir = stripped.kind === "Element" ? this.lowerElement(stripped) : this.lowerComponent(stripped);
|
|
757
|
+
const bucket = slots.get(slot.value.value) ?? [];
|
|
758
|
+
bucket.push(ir);
|
|
759
|
+
slots.set(slot.value.value, bucket);
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
rest.push(child);
|
|
764
|
+
}
|
|
765
|
+
return { slots, rest };
|
|
766
|
+
}
|
|
767
|
+
// --- control-flow sugar -------------------------------------------------
|
|
768
|
+
/** `<el if=${cond}>…</el>` (+ optional `<el else>`) → `<Show when fallback>`. */
|
|
769
|
+
lowerIf(el, condHole, elseEl) {
|
|
770
|
+
const cond = this.hole(condHole);
|
|
771
|
+
const props = [
|
|
772
|
+
{ name: "when", kind: "attr", expr: cond.code, reactive: cond.reactive, ...cond.at ? { at: cond.at } : {} }
|
|
773
|
+
];
|
|
774
|
+
if (elseEl) {
|
|
775
|
+
const elseIR = this.stripAndLower(elseEl, (a) => a.kind === "ElseDirective");
|
|
776
|
+
props.push({ name: "fallback", kind: "attr", expr: this.emit(elseIR), jsxElement: true });
|
|
777
|
+
}
|
|
778
|
+
const child = this.stripAndLower(el, (a) => a.kind === "IfDirective");
|
|
779
|
+
const at = this.tagAt(el);
|
|
780
|
+
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
781
|
+
}
|
|
782
|
+
/** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
|
|
783
|
+
lowerEach(el) {
|
|
784
|
+
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
785
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
|
|
786
|
+
const each = this.hole(eachDir.hole);
|
|
787
|
+
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
788
|
+
if (eachDir.key) {
|
|
789
|
+
const by = "static" in eachDir.key ? `(item) => item[${JSON.stringify(eachDir.key.static)}]` : this.hole(eachDir.key.hole).code;
|
|
790
|
+
props.push({ name: "by", kind: "attr", expr: by });
|
|
791
|
+
}
|
|
792
|
+
const arrow = this.itemArrow(el);
|
|
793
|
+
let childrenCode;
|
|
794
|
+
if (arrow) {
|
|
795
|
+
const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
|
|
796
|
+
const repeated = this.rebuildWithChildren(el, [bodyIR]);
|
|
797
|
+
childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
|
|
798
|
+
} else {
|
|
799
|
+
const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
|
|
800
|
+
childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
|
|
801
|
+
}
|
|
802
|
+
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
803
|
+
const at = this.tagAt(el);
|
|
804
|
+
return { kind: "component", name: "For", props, children, ...at ? { at } : {}, ...each.at ? { bindAt: each.at } : {} };
|
|
805
|
+
}
|
|
806
|
+
/** The single `${item => expr}` child arrow, if that's the element's content. */
|
|
807
|
+
itemArrow(el) {
|
|
808
|
+
const kids = el.children.filter((c) => !this.isBlankText(c));
|
|
809
|
+
if (kids.length !== 1 || kids[0].kind !== "Expression") return null;
|
|
810
|
+
return this.hole(kids[0].hole).arrow ?? null;
|
|
811
|
+
}
|
|
812
|
+
/** Lower an element to IR, dropping the directive attribute matched by `drop`. */
|
|
813
|
+
stripAndLower(el, drop) {
|
|
814
|
+
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
815
|
+
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
816
|
+
}
|
|
817
|
+
/** Same element (minus each/key) but with explicit IR children. */
|
|
818
|
+
rebuildWithChildren(el, children) {
|
|
819
|
+
const props = this.lowerAttributes(
|
|
820
|
+
el.attributes.filter((a) => a.kind !== "EachDirective")
|
|
821
|
+
);
|
|
822
|
+
if (el.kind === "Element") {
|
|
823
|
+
const at = this.tagAt(el);
|
|
824
|
+
return {
|
|
825
|
+
kind: "element",
|
|
826
|
+
tag: el.tag,
|
|
827
|
+
svg: el.namespace === "svg",
|
|
828
|
+
props,
|
|
829
|
+
children,
|
|
830
|
+
static: false,
|
|
831
|
+
...at ? { at } : {}
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
return { kind: "component", name: el.tag, props, children };
|
|
835
|
+
}
|
|
836
|
+
// --- attributes ---------------------------------------------------------
|
|
837
|
+
lowerAttributes(attrs) {
|
|
838
|
+
const props = [];
|
|
839
|
+
const classEntries = [];
|
|
840
|
+
let classReactive = false;
|
|
841
|
+
let classAt;
|
|
842
|
+
const styleEntries = [];
|
|
843
|
+
let styleReactive = false;
|
|
844
|
+
let styleAt;
|
|
845
|
+
const usePairs = [];
|
|
846
|
+
for (const a of attrs) {
|
|
847
|
+
switch (a.kind) {
|
|
848
|
+
case "IfDirective":
|
|
849
|
+
case "EachDirective":
|
|
850
|
+
case "ElseDirective":
|
|
851
|
+
break;
|
|
852
|
+
// handled as control-flow at the parent level
|
|
853
|
+
case "Attribute":
|
|
854
|
+
props.push(this.plainAttr(a));
|
|
855
|
+
break;
|
|
856
|
+
case "PropertyBinding":
|
|
857
|
+
props.push({
|
|
858
|
+
name: a.name,
|
|
859
|
+
kind: "prop",
|
|
860
|
+
expr: this.hole(a.hole).code,
|
|
861
|
+
reactive: this.hole(a.hole).reactive,
|
|
862
|
+
...this.hole(a.hole).at ? { at: this.hole(a.hole).at } : {}
|
|
863
|
+
});
|
|
864
|
+
break;
|
|
865
|
+
case "EventBinding":
|
|
866
|
+
props.push(this.eventProp(a));
|
|
867
|
+
break;
|
|
868
|
+
case "RefBinding":
|
|
869
|
+
props.push({
|
|
870
|
+
name: "ref",
|
|
871
|
+
kind: "ref",
|
|
872
|
+
expr: this.hole(a.hole).code,
|
|
873
|
+
reactive: this.hole(a.hole).reactive,
|
|
874
|
+
// Which variable ends up holding the element, and where it was written.
|
|
875
|
+
...this.hole(a.hole).at ? { at: this.hole(a.hole).at } : {}
|
|
876
|
+
});
|
|
877
|
+
break;
|
|
878
|
+
case "Spread":
|
|
879
|
+
props.push({ name: "", kind: "spread", expr: this.hole(a.hole).code });
|
|
880
|
+
break;
|
|
881
|
+
case "ClassDirective": {
|
|
882
|
+
const h = this.hole(a.hole);
|
|
883
|
+
classEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
|
|
884
|
+
classReactive = classReactive || h.reactive;
|
|
885
|
+
classAt ??= h.at;
|
|
886
|
+
break;
|
|
887
|
+
}
|
|
888
|
+
case "StyleDirective": {
|
|
889
|
+
const h = this.hole(a.hole);
|
|
890
|
+
styleEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
|
|
891
|
+
styleReactive = styleReactive || h.reactive;
|
|
892
|
+
styleAt ??= h.at;
|
|
893
|
+
break;
|
|
894
|
+
}
|
|
895
|
+
case "BindDirective":
|
|
896
|
+
props.push(...this.bindProps(a.name, this.hole(a.hole)));
|
|
897
|
+
break;
|
|
898
|
+
case "UseDirective": {
|
|
899
|
+
const dir = a.name ?? (a.hole != null ? this.hole(a.hole).code : null);
|
|
900
|
+
if (!dir) break;
|
|
901
|
+
usePairs.push(a.name != null && a.hole != null ? `[${dir}, () => ${paren(this.hole(a.hole).code, "arrowBody")}]` : `[${dir}]`);
|
|
902
|
+
break;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
if (classEntries.length > 0) {
|
|
907
|
+
props.push({
|
|
908
|
+
name: "classList",
|
|
909
|
+
kind: "attr",
|
|
910
|
+
expr: `{ ${classEntries.join(", ")} }`,
|
|
911
|
+
reactive: classReactive,
|
|
912
|
+
...classAt ? { at: classAt } : {}
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
if (styleEntries.length > 0) {
|
|
916
|
+
props.push({
|
|
917
|
+
name: "style",
|
|
918
|
+
kind: "attr",
|
|
919
|
+
expr: `{ ${styleEntries.join(", ")} }`,
|
|
920
|
+
reactive: styleReactive,
|
|
921
|
+
...styleAt ? { at: styleAt } : {}
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
if (usePairs.length > 0) {
|
|
925
|
+
props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
|
|
926
|
+
}
|
|
927
|
+
return props;
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* Lower an event binding. Plain `@click`/`onClick` (no modifiers) stays a
|
|
931
|
+
* DELEGATED handler (unchanged, byte-identical with JSX). `on:click` or any
|
|
932
|
+
* modifier (`.capture`/`.once`/`.passive`/`.prevent`/`.stop`/`.self`) emits a
|
|
933
|
+
* NATIVE `on:name` prop the runtime attaches via addEventListener.
|
|
934
|
+
*/
|
|
935
|
+
eventProp(a) {
|
|
936
|
+
const dom = a.name.toLowerCase();
|
|
937
|
+
const handler = this.hole(a.hole).code;
|
|
938
|
+
const native = a.syntax === "colon" || a.modifiers.length > 0;
|
|
939
|
+
if (!native) {
|
|
940
|
+
return {
|
|
941
|
+
name: "on" + capitalize(a.name),
|
|
942
|
+
kind: "event",
|
|
943
|
+
event: { name: dom, delegated: DELEGATED.has(dom) },
|
|
944
|
+
expr: handler
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
const wrapped = this.wrapHandler(handler, a.modifiers);
|
|
948
|
+
const options = this.eventOptions(a.modifiers);
|
|
949
|
+
const value = options ? `[${wrapped}, ${options}]` : wrapped;
|
|
950
|
+
return { name: "on:" + dom, kind: "attr", expr: value };
|
|
951
|
+
}
|
|
952
|
+
/** Wrap a handler for `.prevent`/`.stop`/`.self` (no wrap when none apply). */
|
|
953
|
+
wrapHandler(handler, mods) {
|
|
954
|
+
const guard = mods.includes("self") ? "if (e.target !== e.currentTarget) return; " : "";
|
|
955
|
+
const pre = [];
|
|
956
|
+
if (mods.includes("prevent")) pre.push("e.preventDefault();");
|
|
957
|
+
if (mods.includes("stop")) pre.push("e.stopPropagation();");
|
|
958
|
+
if (!guard && pre.length === 0) return handler;
|
|
959
|
+
return `(e) => { ${guard}${pre.join(" ")} return (${handler})(e); }`;
|
|
960
|
+
}
|
|
961
|
+
/** addEventListener options object from `.capture`/`.once`/`.passive`, or null. */
|
|
962
|
+
eventOptions(mods) {
|
|
963
|
+
const opts = [];
|
|
964
|
+
if (mods.includes("capture")) opts.push("capture: true");
|
|
965
|
+
if (mods.includes("once")) opts.push("once: true");
|
|
966
|
+
if (mods.includes("passive")) opts.push("passive: true");
|
|
967
|
+
return opts.length ? `{ ${opts.join(", ")} }` : null;
|
|
968
|
+
}
|
|
969
|
+
/** `bind:value=${sig}` → `value={read()}` + `onInput={e => write(e…value)}`. */
|
|
970
|
+
bindProps(prop, h) {
|
|
971
|
+
const sigCode = h.code;
|
|
972
|
+
const checked = prop === "checked";
|
|
973
|
+
const evt = checked ? "change" : "input";
|
|
974
|
+
const accessor = checked ? "checked" : "value";
|
|
975
|
+
this.used.add("bindPair");
|
|
976
|
+
const sig = `bindPair(${sigCode})`;
|
|
977
|
+
return [
|
|
978
|
+
{
|
|
979
|
+
name: prop,
|
|
980
|
+
kind: "attr",
|
|
981
|
+
expr: `${sig}[0]()`,
|
|
982
|
+
reactive: true,
|
|
983
|
+
directive: `bind:${prop}`,
|
|
984
|
+
...h.at ? { at: h.at } : {}
|
|
985
|
+
},
|
|
986
|
+
{
|
|
987
|
+
name: "on" + capitalize(evt),
|
|
988
|
+
kind: "event",
|
|
989
|
+
event: { name: evt, delegated: DELEGATED.has(evt) },
|
|
990
|
+
// delegated handler: `target` is the bound element (currentTarget is the root)
|
|
991
|
+
expr: `(e) => ${sig}[1](e.target.${accessor})`
|
|
992
|
+
}
|
|
993
|
+
];
|
|
994
|
+
}
|
|
995
|
+
/** A plain attribute (incl. lit `?bool`, mixed values), old `mkProp` behavior. */
|
|
996
|
+
plainAttr(a) {
|
|
997
|
+
const v = a.value;
|
|
998
|
+
let name = a.name;
|
|
999
|
+
if (a.name === "class") {
|
|
1000
|
+
name = v && v.kind === "hole" && this.hole(v.hole).object ? "classList" : "className";
|
|
1001
|
+
} else if (a.name === "html") {
|
|
1002
|
+
name = "innerHTML";
|
|
1003
|
+
}
|
|
1004
|
+
const kind = "attr";
|
|
1005
|
+
const base = { name, kind };
|
|
1006
|
+
if (v == null) {
|
|
1007
|
+
base.literal = true;
|
|
1008
|
+
return base;
|
|
1009
|
+
}
|
|
1010
|
+
if (v.kind === "static") {
|
|
1011
|
+
base.literal = v.value;
|
|
1012
|
+
return base;
|
|
1013
|
+
}
|
|
1014
|
+
if (v.kind === "hole") {
|
|
1015
|
+
const h = this.hole(v.hole);
|
|
1016
|
+
base.expr = h.code;
|
|
1017
|
+
base.reactive = h.reactive;
|
|
1018
|
+
if (h.at) base.at = h.at;
|
|
1019
|
+
return base;
|
|
1020
|
+
}
|
|
1021
|
+
let reactive = false;
|
|
1022
|
+
let at;
|
|
1023
|
+
const body = v.parts.map((p) => {
|
|
1024
|
+
if ("text" in p) return escapeTemplate(p.text);
|
|
1025
|
+
const h = this.hole(p.hole);
|
|
1026
|
+
reactive = reactive || h.reactive;
|
|
1027
|
+
at ??= h.at;
|
|
1028
|
+
return "${" + h.code + "}";
|
|
1029
|
+
}).join("");
|
|
1030
|
+
base.expr = "`" + body + "`";
|
|
1031
|
+
base.reactive = reactive;
|
|
1032
|
+
if (at) base.at = at;
|
|
1033
|
+
return base;
|
|
1034
|
+
}
|
|
1035
|
+
};
|
|
1036
|
+
function lowerTemplate(pieces, holes, used, opts = {}) {
|
|
1037
|
+
const { root } = parseTemplate(pieces, { svg: opts.svg });
|
|
1038
|
+
return new Lowerer(holes, used, opts.sourceFile, opts.positionAt).lowerRoot(root.children);
|
|
1039
|
+
}
|
|
1040
|
+
export {
|
|
1041
|
+
lowerTemplate
|
|
1042
|
+
};
|