@fluixi/compiler 1.0.0-alpha.73 → 1.0.0-alpha.75
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/README.md +41 -16
- package/dist/analyze/expr-shape.cjs +1 -0
- package/dist/analyze/expr-shape.d.ts +15 -0
- package/dist/analyze/expr-shape.d.ts.map +1 -0
- package/dist/analyze/expr-shape.js +37 -0
- package/dist/analyze/expr-shape.mjs +1 -0
- package/dist/analyze/reactive-expr.cjs +1 -0
- package/dist/analyze/reactive-expr.d.ts +39 -0
- package/dist/analyze/reactive-expr.d.ts.map +1 -0
- package/dist/analyze/reactive-expr.js +31 -0
- package/dist/analyze/reactive-expr.mjs +1 -0
- package/dist/babel-GXO3KAVI.mjs +1 -0
- package/dist/chunk-3SAEGOMQ.mjs +1 -0
- package/dist/chunk-E2PW2OZU.mjs +1 -0
- package/dist/chunk-IBRM4W7I.mjs +10 -0
- package/dist/chunk-OHHZCYIQ.mjs +1 -0
- package/dist/chunk-TIE4XNAM.mjs +1 -0
- package/dist/frontend/babel/build-ir-lit.cjs +1 -1
- package/dist/frontend/babel/build-ir-lit.d.ts +1 -0
- package/dist/frontend/babel/build-ir-lit.d.ts.map +1 -1
- package/dist/frontend/babel/build-ir-lit.mjs +1 -1
- package/dist/frontend/babel/build-ir.cjs +1 -1
- package/dist/frontend/babel/build-ir.d.ts +13 -11
- package/dist/frontend/babel/build-ir.d.ts.map +1 -1
- package/dist/frontend/babel/build-ir.js +27 -217
- package/dist/frontend/babel/build-ir.mjs +1 -1
- package/dist/frontend/babel/index.cjs +1 -2
- package/dist/frontend/babel/index.mjs +1 -2
- package/dist/frontend/babel/lower-template.cjs +1 -1
- package/dist/frontend/babel/lower-template.d.ts +12 -9
- package/dist/frontend/babel/lower-template.d.ts.map +1 -1
- package/dist/frontend/babel/lower-template.js +33 -445
- package/dist/frontend/babel/lower-template.mjs +1 -1
- package/dist/frontend/babel/plugin.cjs +1 -2
- package/dist/frontend/babel/plugin.d.ts +24 -85
- package/dist/frontend/babel/plugin.d.ts.map +1 -1
- package/dist/frontend/babel/plugin.js +52 -1285
- package/dist/frontend/babel/plugin.mjs +1 -2
- package/dist/frontend/babel/server-functions.cjs +1 -1
- package/dist/frontend/babel/server-functions.d.ts.map +1 -1
- package/dist/frontend/babel/server-functions.js +2 -9
- package/dist/frontend/babel/server-functions.mjs +1 -1
- package/dist/index.cjs +8 -8
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.mjs +9 -9
- package/dist/integrations.cjs +15 -19
- package/dist/integrations.d.ts +39 -151
- package/dist/integrations.d.ts.map +1 -1
- package/dist/integrations.js +79 -491
- package/dist/integrations.mjs +11 -17
- package/dist/lower/compile-template.cjs +1 -0
- package/dist/lower/compile-template.d.ts +57 -0
- package/dist/lower/compile-template.d.ts.map +1 -0
- package/dist/lower/compile-template.js +72 -0
- package/dist/lower/compile-template.mjs +1 -0
- package/dist/lower/jsx.cjs +1 -0
- package/dist/lower/jsx.d.ts +24 -0
- package/dist/lower/jsx.d.ts.map +1 -0
- package/dist/lower/jsx.js +193 -0
- package/dist/lower/jsx.mjs +1 -0
- package/dist/lower/template.cjs +1 -0
- package/dist/lower/template.d.ts +41 -0
- package/dist/lower/template.d.ts.map +1 -0
- package/dist/lower/template.js +445 -0
- package/dist/lower/template.mjs +1 -0
- package/dist/lower/text.cjs +1 -0
- package/dist/lower/text.d.ts +11 -0
- package/dist/lower/text.d.ts.map +1 -0
- package/dist/lower/text.js +31 -0
- package/dist/lower/text.mjs +1 -0
- package/dist/options.cjs +1 -1
- package/dist/options.d.ts +0 -2
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +0 -1
- package/dist/options.mjs +1 -1
- package/dist/server-fns-6QM243HC.mjs +2 -0
- package/dist/server-functions-E3LKCZ4R.mjs +1 -0
- package/dist/transform/bindings.cjs +1 -0
- package/dist/transform/bindings.d.ts +17 -0
- package/dist/transform/bindings.d.ts.map +1 -0
- package/dist/transform/bindings.js +69 -0
- package/dist/transform/bindings.mjs +1 -0
- package/dist/transform/index.cjs +3 -0
- package/dist/transform/index.d.ts +12 -0
- package/dist/transform/index.d.ts.map +1 -0
- package/dist/transform/index.js +10 -0
- package/dist/transform/index.mjs +12 -0
- package/dist/transform/server-fn-id.cjs +1 -0
- package/dist/transform/server-fn-id.d.ts +10 -0
- package/dist/transform/server-fn-id.d.ts.map +1 -0
- package/dist/transform/server-fn-id.js +18 -0
- package/dist/transform/server-fn-id.mjs +1 -0
- package/dist/transform/server-fns.cjs +2 -0
- package/dist/transform/server-fns.d.ts +13 -0
- package/dist/transform/server-fns.d.ts.map +1 -0
- package/dist/transform/server-fns.js +94 -0
- package/dist/transform/server-fns.mjs +11 -0
- package/dist/transform/templates.cjs +3 -0
- package/dist/transform/templates.d.ts +26 -0
- package/dist/transform/templates.d.ts.map +1 -0
- package/dist/transform/templates.js +347 -0
- package/dist/transform/templates.mjs +12 -0
- package/dist/transform-PJX2GNSV.mjs +3 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +30 -8
- package/dist/babel-E7EQLX36.mjs +0 -2
- package/dist/chunk-EF6V4GTD.mjs +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var b={kind:"eager"},w=["pointerdown","focusin","keydown"],h=new Set(["eager","idle","visible","interaction","media","never"]),c=class extends Error{};function d(e){return e.startsWith("load:")}function E(e,t){let n=e.slice(5);if(!h.has(n))throw new c(`Unknown load strategy 'load:${n}'. Expected one of ${[...h].join(", ")}.`);switch(n){case"eager":return b;case"idle":return{kind:"idle"};case"never":return{kind:"never"};case"visible":return t?{kind:"visible",rootMargin:t}:{kind:"visible"};case"interaction":return{kind:"interaction",events:t?J(t):[...w]};case"media":if(!t)throw new c(`'load:media' needs a query, e.g. load:media="(min-width: 1024px)".`);return{kind:"media",query:t};default:throw new c(`Unhandled load strategy '${n}'.`)}}function J(e){let t=e.split(/[\s,]+/).map(n=>n.trim()).filter(Boolean);if(t.length===0)throw new c("'load:interaction' was given no event names.");return t}var L="children";function f(e){switch(e.kind){case"call":return!0;case"member":return e.property!==L;case"compound":return e.parts.some(f);case"opaque":return!1}}var C=new Set(["CallExpression","OptionalCallExpression"]),X=new Set(["MemberExpression","OptionalMemberExpression"]);function m(e){if(!e)return{kind:"opaque"};if(C.has(e.type))return{kind:"call"};if(X.has(e.type)){let t=e.property;return{kind:"member",property:!(e.computed===!0)&&t?.type==="Identifier"?t.name:null}}return e.type==="ConditionalExpression"?{kind:"compound",parts:[e.test,e.consequent,e.alternate].map(l)}:e.type==="LogicalExpression"||e.type==="BinaryExpression"?{kind:"compound",parts:[e.left,e.right].map(l)}:e.type==="TemplateLiteral"?{kind:"compound",parts:e.expressions.map(l)}:e.type==="ObjectExpression"?{kind:"compound",parts:e.properties.filter(n=>(n.type==="ObjectProperty"||n.type==="Property")&&n.computed!==!0).map(n=>l(n.value))}:e.type==="ArrayExpression"?{kind:"compound",parts:e.elements.filter(n=>n!=null&&n.type!=="SpreadElement").map(l)}:{kind:"opaque"}}var l=e=>m(e);function A(e){switch(e.kind){case"event":return"event";case"ref":return"ref";case"spread":return"spread";case"class":case"style":return"class-or-style-directive";case"attr":case"prop":return e.reactive?"reactive-prop":e.expr!==void 0?"expression-prop":void 0;default:return"expression-prop"}}function k(e){let t=[],n=new Map,o=r=>{switch(r.kind){case"text":return!0;case"expr":return!1;case"component":case"control":for(let s of r.children)o(s);return!1;case"fragment":for(let s of r.children)o(s);return!1;case"element":{let s=!0;for(let a of r.children)o(a)||(s=!1);let i;for(let a of r.props)if(i=A(a),i)break;return!i&&!s&&(i=$(r)),r.static=!i,i?n.set(r,i):t.push(r),r.static}default:return!1}};for(let r of Array.isArray(e)?e:[e])o(r);return{staticElements:t,reasons:n}}function $(e){for(let t of e.children){if(t.kind==="expr")return"expression-child";if(t.kind==="component")return"component-child";if(t.kind==="control")return"control-child";if(t.kind==="element"&&!t.static||t.kind==="fragment")return"expression-child"}return"expression-child"}function v(e){let t=e.split(/\r\n|\n|\r/),n=0;for(let r=0;r<t.length;r++)/[^ \t]/.test(t[r])&&(n=r);let o="";for(let r=0;r<t.length;r++){let s=t[r].replace(/\t/g," ");r!==0&&(s=s.replace(/^ +/,"")),r!==t.length-1&&(s=s.replace(/ +$/,"")),s&&(r!==n&&(s+=" "),o+=s)}return o}var T=new Set(["svg","path","circle","rect","line","polygon","polyline","ellipse","g","defs","clipPath","text"]),D=/^on[A-Z]/,P=new Set(["click","dblclick","input","change","submit","focus","blur","keydown","keyup","keypress","mousedown","mouseup"]),N=e=>f(m(e));function u(e){if(e.type==="StringLiteral"||e.type==="NumericLiteral"||e.type==="BooleanLiteral"||e.type==="Literal"&&(typeof e.value=="string"||typeof e.value=="number"||typeof e.value=="boolean"))return e.value}var y=e=>!!e&&typeof u(e)=="string";function M(e,t){return e.type==="JSXIdentifier"?{tag:e.name,component:e.name[0]!==e.name[0].toLowerCase()}:e.type==="JSXMemberExpression"?{tag:t.code(e),component:!0}:e.type==="JSXNamespacedName"?{tag:`${e.namespace.name}:${e.name.name}`,component:!1}:{tag:"div",component:!1}}function x(e){return e.type==="JSXIdentifier"?e.name==="class"?"className":e.name:e.type==="JSXNamespacedName"?`${e.namespace.name}:${e.name.name}`:"unknown"}function j(e,t){let n=x(e.name),o=D.test(n),r=o||n.startsWith("on:")||n==="ref",i={name:n,kind:o?"event":"attr"};if(o){let p=n.slice(2).toLowerCase();i.event={name:p,delegated:P.has(p)}}let a=e.value;if(a==null)return i.literal=!0,i;if(y(a))return i.literal=u(a),i;if(a.type==="JSXExpressionContainer"&&a.expression?.type!=="JSXEmptyExpression"){let p=a.expression,g=u(p);return g!==void 0?(i.literal=g,i):(i.expr=t.code(p),i.reactive=r?!1:N(p),(p.type==="JSXElement"||p.type==="JSXFragment")&&(i.jsxElement=!0),i)}return i.literal=!0,i}function S(e,t){let n=e.value;return n==null?null:y(n)?JSON.stringify(u(n)):n.type==="JSXExpressionContainer"&&n.expression?.type!=="JSXEmptyExpression"?t.code(n.expression):null}function q(e,t){let n=[],o=[];for(let r of e){if(r.type==="JSXSpreadAttribute"){n.push({name:"",kind:"spread",expr:t.code(r.argument)});continue}if(r.type!=="JSXAttribute"||d(x(r.name)))continue;let s=r.name.type==="JSXNamespacedName"?r.name.namespace.name:null;if(s==="use"){let i=r.name.name.name;t.used.add(i);let a=S(r,t);o.push(a!=null?`[${i}, () => (${a})]`:`[${i}]`);continue}if(s==="oncapture"){let i=r.name.name.name.toLowerCase(),a=S(r,t)??"undefined";n.push({name:"on:"+i,kind:"attr",expr:`[${a}, { capture: true }]`});continue}n.push(j(r,t))}return o.length>0&&n.push({name:"use",kind:"attr",expr:`[${o.join(", ")}]`}),n}function R(e,t){let n=[];for(let o of e)if(o.type==="JSXText"){let r=v(o.value);r&&n.push({kind:"text",value:r})}else if(o.type==="JSXExpressionContainer"){if(o.expression?.type!=="JSXEmptyExpression"){let r=o.expression;n.push({kind:"expr",code:t.code(r),reactive:N(r)})}}else o.type==="JSXElement"||o.type==="JSXFragment"?n.push(I(o,t)):o.type==="JSXSpreadChild"&&n.push({kind:"expr",code:t.code(o.expression),reactive:!1});return n}function O(e){for(let t of e){if(t.type!=="JSXAttribute")continue;let n=x(t.name);if(d(n)){if(t.value&&!y(t.value))throw new c(`'${n}' needs a literal value, not an expression — the strategy is compile-time.`);return E(n,t.value?u(t.value):null)}}}function I(e,t){if(e.type==="JSXFragment")return{kind:"fragment",children:R(e.children,t)};let{tag:n,component:o}=M(e.openingElement.name,t),r=q(e.openingElement.attributes,t),s=R(e.children,t);if(o){let i=O(e.openingElement.attributes);return{kind:"component",name:n,props:r,children:s,...i?{load:i}:{}}}return{kind:"element",tag:n,svg:T.has(n),props:r,children:s,static:!1}}function Z(e,t){let n=I(e,t);return k(n),n}export{T as SVG_TAGS,Z as buildJsxIR};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var R=Object.defineProperty;var W=Object.getOwnPropertyDescriptor;var q=Object.getOwnPropertyNames;var G=Object.prototype.hasOwnProperty;var K=(t,e)=>{for(var n in e)R(t,n,{get:e[n],enumerable:!0})},Y=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of q(e))!G.call(t,i)&&i!==n&&R(t,i,{get:()=>e[i],enumerable:!(r=W(e,i))||r.enumerable});return t};var Z=t=>Y(R({},"__esModule",{value:!0}),t);var Re={};K(Re,{lowerTemplate:()=>Ne});module.exports=Z(Re);var U=require("@fluixi/template-parser");var X={kind:"eager"},Q=["pointerdown","focusin","keydown"],T=new Set(["eager","idle","visible","interaction","media","never"]),u=class extends Error{};function S(t,e){let n=t.slice(5);if(!T.has(n))throw new u(`Unknown load strategy 'load:${n}'. Expected one of ${[...T].join(", ")}.`);switch(n){case"eager":return X;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?ee(e):[...Q]};case"media":if(!e)throw new u(`'load:media' needs a query, e.g. load:media="(min-width: 1024px)".`);return{kind:"media",query:e};default:throw new u(`Unhandled load strategy '${n}'.`)}}function ee(t){let e=t.split(/[\s,]+/).map(n=>n.trim()).filter(Boolean);if(e.length===0)throw new u("'load:interaction' was given no event names.");return e}var te=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),C={className:"class",htmlFor:"for"},ne=new Set(["innerHTML","outerHTML","innerText","textContent","value","checked","selected","muted","defaultValue","defaultChecked","children","ref"]),re=new Set(["script","style","textarea","title"]);function $(t){return re.has(t)}function A(t){return C[t]??t}function h(t){return t.kind!=="attr"||t.expr!==void 0||t.name.includes(":")?!1:!ne.has(t.name)}function b(t){if(!t.static||$(t.tag))return!1;for(let e of t.props)if(!h(e))return!1;for(let e of t.children)if(e.kind!=="text"&&!(e.kind==="element"&&b(e)))return!1;return!0}function f(t){let e=t.props.map(oe).filter(Boolean).join(""),n=`<${t.tag}${e}>`;return te.has(t.tag)?n:`${n}${t.children.map(ie).join("")}</${t.tag}>`}function ie(t){if(t.kind==="text")return ae(t.value);if(t.kind!=="element")throw new Error(`serializeStatic: unexpected ${t.kind}`);return f(t)}function oe(t){let e=C[t.name]??t.name,n=t.literal;return n===!0||n===void 0?` ${e}`:n===!1||n===null?"":` ${e}="${se(String(n))}"`}function se(t){return t.replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}function ae(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}var le="<!--fx-->",ce="<!--fx/-->";function N(t){return t.kind==="expr"||t.kind==="component"||t.kind==="control"}function de(t){return t.kind==="text"||t.kind==="element"&&P(t)}function P(t){return t.svg||$(t.tag)||!t.props.every(e=>h(e))?!1:t.children.every(e=>de(e)||N(e))}function k(t){if(N(t))return!0;let e=t.children;return e?e.some(k):!1}function L(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"||L(e))}function D(t){if(!P(t)||!L(t)||!t.children.some(k))return null;let e=[],n=[],r=0,i=c=>{let s=`_n$${r++}`;return e.push({ref:s,expr:c}),s};return o(t,"_el$"),{html:pe(t),tag:t.tag,steps:e,holes:n};function o(c,s){let a=-1;c.children.forEach((d,p)=>{k(d)&&(a=p)});let l=null;for(let d=0;d<=a;d++){let p=c.children[d],m=l?`${l}.nextSibling`:`${s}.firstChild`;if(N(p)){let w=i(m),y=i(`holeEnd(${w})`);n.push({parentRef:s,startRef:w,endRef:y,node:p}),l=y;continue}let v=i(m);p.kind==="element"&&k(p)&&o(p,v),l=v}}}function pe(t){return f(j(t)).split(O).join(le+ce)}function j(t){return{...t,children:t.children.map(e=>N(e)?{kind:"text",value:O}:e.kind==="element"?j(e):e)}}var O="\0fx-hole\0";var x={version:1,module:"@fluixi/dom",symbols:["createNativeElement","insert","spread","setAttribute","delegateEvents","createComponent","createMemo","untrack","Show","For","Index","Switch","Match","Dynamic","ErrorBoundary"]},we={version:2,module:"@fluixi/dom",symbols:[...x.symbols,"template","cloneTemplate","walk"]};var ue={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 M(t){return me(t)?t:JSON.stringify(t)}function he(t){return t.expr!==void 0?t.reactive?`() => (${t.expr})`:`(${t.expr})`:JSON.stringify(t.literal??!0)}function fe(t){return t.expr!==void 0?`(${t.expr})`:JSON.stringify(t.literal??!0)}function ge(t,e,n){let r=t.filter(s=>s.kind==="spread"),o=t.filter(s=>s.kind!=="spread").map(s=>`${M(s.name)}: ${he(s)}`);e!=null&&o.push(`children: ${e}`);let c=`{ ${o.join(", ")} }`;return r.length>0?(n.add("mergeProps"),`mergeProps(${r.map(s=>s.expr).join(", ")}, ${c})`):c}function H(t,e,n){return t.length===1?g(t[0],e,n):`[${t.map(r=>g(r,e,n)).join(", ")}]`}function _(t,e,n,r,i){r.add("createMemo"),r.add("createComponent");let o=e.filter(l=>l.kind==="spread"),s=e.filter(l=>l.kind!=="spread").map(l=>`get ${M(l.name)}() { return ${fe(l)}; }`);n.length>0&&s.push(`get children() { return ${H(n,r,i)}; }`);let a=`{ ${s.join(", ")} }`;return o.length>0&&(r.add("mergeProps"),a=`mergeProps(${o.map(l=>l.expr).join(", ")}, ${a})`),`createMemo(() => createComponent(${t}, ${a}))`}function ve(t,e){let n=A(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 B=!1;function g(t,e,n){switch(t.kind){case"text":return JSON.stringify(t.value);case"expr":return t.reactive?`() => (${t.code})`:`(${t.code})`;case"fragment":return t.children.length===0?"null":H(t.children,e,n);case"component":return _(t.name,t.props,t.children,e,n);case"control":{let r=ue[t.control]??t.control;return e.add(r),_(r,t.props,t.children,e,n)}case"element":{if(n&&t.static&&!t.svg&&b(t)){e.add("templateNode");let s=`_tmpl$${n.length}`;return n.push({id:s,html:f(t),tag:t.tag,svg:!1}),`templateNode(${s}, ${JSON.stringify(t.tag)})`}if(n&&B){let s=D(t);if(s){e.add("templateNode"),e.add("insert"),e.add("holeEnd"),e.add("holeContent"),e.add("holeScope");let a=`_tmpl$${n.length}`;n.push({id:a,html:s.html,tag:s.tag,svg:!1});let l=[`const _el$ = templateNode(${a}, ${JSON.stringify(s.tag)}, true);`];for(let d of s.steps)l.push(`const ${d.ref} = ${d.expr};`);for(let d of s.holes)l.push(`insert(${d.parentRef}, holeScope(${d.startRef}, () => (${g(d.node,e,n)})), ${d.endRef}, holeContent(${d.startRef}, ${d.endRef}));`);return l.push("return _el$;"),`(() => { ${l.join(" ")} })()`}}e.add("createNativeElement");let r=JSON.stringify(t.tag),i="_el$",o=[],c=t.svg?`${r}, true`:r;if(o.push(`const ${i} = createNativeElement(${c});`),t.props.length>0)if(!t.svg&&t.props.every(h))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: ${ge(t.props,null,e)}${s} });`)}for(let s of t.children){e.add("insert");let a=g(s,e,n),l=s.kind==="expr"&&s.reactive||s.kind==="component"||s.kind==="control";o.push(l?`insert(${i}, ${a}, null);`:`insert(${i}, ${a});`)}return o.push(`return ${i};`),`(() => { ${o.join(" ")} })()`}}}function J(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 z={name:"imperative",contract:x,emit(t,e){let n=new Set,r=e?.templateClone!==!1?[]:void 0;return B=e?.partialTemplates===!0,{code:g(t,n,r),imports:Array.from(n),templates:r}}};function E(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 V=new Set(["click","dblclick","input","change","submit","focus","blur","keydown","keyup","keypress","mousedown","mouseup"]);function ke(t){return t.replace(/\\/g,"\\\\").replace(/`/g,"\\`").replace(/\$\{/g,"\\${")}function F(t){return t.charAt(0).toUpperCase()+t.slice(1)}var I=class{constructor(e,n){this.holes=e;this.used=n}hole(e){return this.holes[e]??{code:"undefined",reactive:!1}}emit(e){let{code:n,imports:r,templates:i}=z.emit(e,{});for(let o of r)this.used.add(o);return J(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 c=i.attributes.find(s=>s.kind==="IfDirective");if(c&&c.kind==="IfDirective"){let s=r+1;s<e.length&&this.isBlankText(e[s])&&s++;let a=e[s],l=a&&(a.kind==="Element"||a.kind==="Component")&&a.attributes.some(d=>d.kind==="ElseDirective");n.push(this.lowerIf(i,c.hole,l?a:null)),l&&(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&&E(e.value)===""}lowerNode(e){switch(e.kind){case"Text":{if(e.raw)return{kind:"text",value:e.value};let n=E(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}}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(r=>r.kind==="Attribute"&&r.name==="is");if(e.tag==="component"&&n&&n.value&&n.value.kind==="hole"){let r=e.attributes.filter(o=>o!==n),i=this.lowerComponent({...e,kind:"Component",tag:"Dynamic",tagHole:null,attributes:r});return i.props.unshift({name:"component",kind:"attr",expr:`() => (${this.hole(n.value.hole).code})`}),i}return{kind:"element",tag:e.tag,svg:e.namespace==="svg",props:this.lowerAttributes(e.attributes),children:this.lowerChildren(e.children),static:!1}}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,l]of i){let d=l.length===1?l[0]:{kind:"fragment",children:l},p={name:a,kind:"attr",expr:this.emit(d),jsxElement:!0},m=r.findIndex(v=>v.name===a);m>=0?r[m]=p:r.push(p)}let c=e.attributes.find(a=>a.kind==="LoadDirective"),s=c?S(`load:${c.strategy}`,c.modifier):void 0;return{kind:"component",name:n,props:r,children:this.lowerChildren(o),...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(c=>c.kind==="Attribute"&&c.name==="slot");if(o&&o.value&&o.value.kind==="static"){let c={...i,attributes:i.attributes.filter(l=>l!==o)},s=c.kind==="Element"?this.lowerElement(c):this.lowerComponent(c),a=n.get(o.value.value)??[];a.push(s),n.set(o.value.value,a);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}];if(r){let s=this.stripAndLower(r,a=>a.kind==="ElseDirective");o.push({name:"fallback",kind:"attr",expr:this.emit(s),jsxElement:!0})}let c=this.stripAndLower(e,s=>s.kind==="IfDirective");return{kind:"component",name:"Show",props:o,children:[c]}}lowerEach(e){let n=e.attributes.find(a=>a.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}];if(n.key){let a="static"in n.key?`(item) => item[${JSON.stringify(n.key.static)}]`:this.hole(n.key.hole).code;i.push({name:"by",kind:"attr",expr:a})}let o=this.itemArrow(e),c;if(o){let a={kind:"expr",code:o.body,reactive:o.bodyReactive},l=this.rebuildWithChildren(e,[a]);c=`(${o.params.join(", ")}) => (${this.emit(l)})`}else{let a=this.stripAndLower(e,l=>l.kind==="EachDirective");c=`() => (${this.emit(a)})`}return{kind:"component",name:"For",props:i,children:[{kind:"expr",code:c,reactive:!1}]}}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"));return e.kind==="Element"?{kind:"element",tag:e.tag,svg:e.namespace==="svg",props:r,children:n,static:!1}:{kind:"component",name:e.tag,props:r,children:n}}lowerAttributes(e){let n=[],r=[],i=!1,o=[],c=!1,s=[];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});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});break;case"Spread":n.push({name:"",kind:"spread",expr:this.hole(a.hole).code});break;case"ClassDirective":{let l=this.hole(a.hole);r.push(`${JSON.stringify(a.name)}: ${l.code}`),i=i||l.reactive;break}case"StyleDirective":{let l=this.hole(a.hole);o.push(`${JSON.stringify(a.name)}: ${l.code}`),c=c||l.reactive;break}case"BindDirective":n.push(...this.bindProps(a.name,this.hole(a.hole).code));break;case"UseDirective":{let l=a.name??(a.hole!=null?this.hole(a.hole).code:null);if(!l)break;s.push(a.name!=null&&a.hole!=null?`[${l}, () => (${this.hole(a.hole).code})]`:`[${l}]`);break}}return r.length>0&&n.push({name:"classList",kind:"attr",expr:`{ ${r.join(", ")} }`,reactive:i}),o.length>0&&n.push({name:"style",kind:"attr",expr:`{ ${o.join(", ")} }`,reactive:c}),s.length>0&&n.push({name:"use",kind:"attr",expr:`[${s.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"+F(e.name),kind:"event",event:{name:n,delegated:V.has(n)},expr:r};let o=this.wrapHandler(r,e.modifiers),c=this.eventOptions(e.modifiers),s=c?`[${o}, ${c}]`: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=e==="checked",i=r?"change":"input",o=r?"checked":"value",c=`(${n})`;return[{name:e,kind:"attr",expr:`${c}[0]()`,reactive:!0},{name:"on"+F(i),kind:"event",event:{name:i,delegated:V.has(i)},expr:`(e) => ${c}[1](e.target.${o})`}]}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 a=this.hole(n.hole);return o.expr=a.code,o.reactive=a.reactive,o}let c=!1,s=n.parts.map(a=>{if("text"in a)return ke(a.text);let l=this.hole(a.hole);return c=c||l.reactive,"${"+l.code+"}"}).join("");return o.expr="`"+s+"`",o.reactive=c,o}};function Ne(t,e,n,r={}){let{root:i}=(0,U.parseTemplate)(t,{svg:r.svg});return new I(e,n).lowerRoot(i.children)}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lower a parsed Template AST (`@fluixi/template-parser`) into the host-agnostic IR.
|
|
3
|
+
*
|
|
4
|
+
* No parser in here: the `${…}` holes arrive already described as `TemplateHole`,
|
|
5
|
+
* since reading them means reading JavaScript and that belongs to a front-end.
|
|
6
|
+
*
|
|
7
|
+
* Pipeline: pieces → parseTemplate → Template AST → lowerTemplate → IRNode
|
|
8
|
+
*/
|
|
9
|
+
import { type Piece } from '@fluixi/template-parser';
|
|
10
|
+
import type { IRNode } from '../ir/nodes.js';
|
|
11
|
+
/**
|
|
12
|
+
* One `${…}` slot, as the front-end describes it. Conclusions about the
|
|
13
|
+
* expression only — never a syntax tree, or the parser dependency creeps back.
|
|
14
|
+
*/
|
|
15
|
+
export interface TemplateHole {
|
|
16
|
+
/** The expression source, verbatim. Anything nested inside it is already compiled. */
|
|
17
|
+
code: string;
|
|
18
|
+
/** Whether it must be wired reactively (see analyze/reactive-expr.ts). */
|
|
19
|
+
reactive: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Set when the hole is a single-expression arrow — `${item => …}`. `each` turns
|
|
22
|
+
* that into `For`'s render function, so it needs the parameters and the body
|
|
23
|
+
* separately rather than the arrow as written.
|
|
24
|
+
*/
|
|
25
|
+
arrow?: {
|
|
26
|
+
params: string[];
|
|
27
|
+
body: string;
|
|
28
|
+
bodyReactive: boolean;
|
|
29
|
+
};
|
|
30
|
+
/** Whether the hole is an object literal — `class=${{…}}` lowers to classList. */
|
|
31
|
+
object?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Lower a parsed template to IR. `pieces` are the static runs and hole slots as
|
|
35
|
+
* the parser wants them; `holes` describes each `${…}` in the same order. `used`
|
|
36
|
+
* collects the runtime symbols the emitted code imports.
|
|
37
|
+
*/
|
|
38
|
+
export declare function lowerTemplate(pieces: Piece[], holes: TemplateHole[], used: Set<string>, opts?: {
|
|
39
|
+
svg?: boolean;
|
|
40
|
+
}): IRNode;
|
|
41
|
+
//# sourceMappingURL=template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/lower/template.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAML,KAAK,KAAK,EAGX,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,MAAM,EAAsB,MAAM,gBAAgB,CAAC;AAKjE;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,sFAAsF;IACtF,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,OAAO,CAAA;KAAE,CAAC;IAClE,kFAAkF;IAClF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AA0bD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,KAAK,EAAE,EACf,KAAK,EAAE,YAAY,EAAE,EACrB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,EACjB,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAO,GAC3B,MAAM,CAGR"}
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lower a parsed Template AST (`@fluixi/template-parser`) into the host-agnostic IR.
|
|
3
|
+
*
|
|
4
|
+
* No parser in here: the `${…}` holes arrive already described as `TemplateHole`,
|
|
5
|
+
* since reading them means reading JavaScript and that belongs to a front-end.
|
|
6
|
+
*
|
|
7
|
+
* Pipeline: pieces → parseTemplate → Template AST → lowerTemplate → IRNode
|
|
8
|
+
*/
|
|
9
|
+
import { parseTemplate, } from '@fluixi/template-parser';
|
|
10
|
+
import { parseLoadDirective } from '../resolve/load-directive.js';
|
|
11
|
+
import { imperativeBackend, inlineTemplates } from '../codegen/backends/imperative.js';
|
|
12
|
+
import { cleanTemplateText } from './text.js';
|
|
13
|
+
const DELEGATED = new Set([
|
|
14
|
+
'click', 'dblclick', 'input', 'change', 'submit', 'focus', 'blur',
|
|
15
|
+
'keydown', 'keyup', 'keypress', 'mousedown', 'mouseup',
|
|
16
|
+
]);
|
|
17
|
+
/** Escape a static text run for embedding in a template literal. */
|
|
18
|
+
function escapeTemplate(s) {
|
|
19
|
+
return s.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
|
|
20
|
+
}
|
|
21
|
+
function capitalize(s) {
|
|
22
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
23
|
+
}
|
|
24
|
+
class Lowerer {
|
|
25
|
+
holes;
|
|
26
|
+
used;
|
|
27
|
+
constructor(holes, used) {
|
|
28
|
+
this.holes = holes;
|
|
29
|
+
this.used = used;
|
|
30
|
+
}
|
|
31
|
+
hole(i) {
|
|
32
|
+
// -1 marks a recovered-from missing binding; emit a harmless undefined.
|
|
33
|
+
return this.holes[i] ?? { code: 'undefined', reactive: false };
|
|
34
|
+
}
|
|
35
|
+
/** Emit a nested IR subtree to code (for `Show` fallback / `For` children). */
|
|
36
|
+
emit(node) {
|
|
37
|
+
const { code, imports, templates } = imperativeBackend.emit(node, {});
|
|
38
|
+
for (const s of imports)
|
|
39
|
+
this.used.add(s);
|
|
40
|
+
// No module scope reachable from a nested subtree, so templates are inlined;
|
|
41
|
+
// `createTemplate` caches by HTML, so this is a lookup rather than a re-parse.
|
|
42
|
+
return inlineTemplates(code, templates);
|
|
43
|
+
}
|
|
44
|
+
// --- content ------------------------------------------------------------
|
|
45
|
+
lowerRoot(children) {
|
|
46
|
+
const nodes = this.lowerChildren(children);
|
|
47
|
+
if (nodes.length === 1)
|
|
48
|
+
return nodes[0];
|
|
49
|
+
return { kind: 'fragment', children: nodes };
|
|
50
|
+
}
|
|
51
|
+
/** Lower a sibling list, handling text-drop and if/else/each restructuring. */
|
|
52
|
+
lowerChildren(children) {
|
|
53
|
+
const out = [];
|
|
54
|
+
for (let i = 0; i < children.length; i++) {
|
|
55
|
+
const child = children[i];
|
|
56
|
+
if (child.kind === 'Element' || child.kind === 'Component') {
|
|
57
|
+
const ifDir = child.attributes.find((a) => a.kind === 'IfDirective');
|
|
58
|
+
if (ifDir && ifDir.kind === 'IfDirective') {
|
|
59
|
+
// Pair with a following `else` sibling (skipping a whitespace-only text).
|
|
60
|
+
let elseIdx = i + 1;
|
|
61
|
+
if (elseIdx < children.length && this.isBlankText(children[elseIdx]))
|
|
62
|
+
elseIdx++;
|
|
63
|
+
const elseNode = children[elseIdx];
|
|
64
|
+
const hasElse = elseNode &&
|
|
65
|
+
(elseNode.kind === 'Element' || elseNode.kind === 'Component') &&
|
|
66
|
+
elseNode.attributes.some((a) => a.kind === 'ElseDirective');
|
|
67
|
+
out.push(this.lowerIf(child, ifDir.hole, hasElse ? elseNode : null));
|
|
68
|
+
if (hasElse)
|
|
69
|
+
i = elseIdx;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (child.attributes.some((a) => a.kind === 'EachDirective')) {
|
|
73
|
+
out.push(this.lowerEach(child));
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const node = this.lowerNode(child);
|
|
78
|
+
if (node)
|
|
79
|
+
out.push(node);
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
isBlankText(n) {
|
|
84
|
+
return n.kind === 'Text' && !n.raw && cleanTemplateText(n.value) === '';
|
|
85
|
+
}
|
|
86
|
+
lowerNode(node) {
|
|
87
|
+
switch (node.kind) {
|
|
88
|
+
case 'Text': {
|
|
89
|
+
if (node.raw)
|
|
90
|
+
return { kind: 'text', value: node.value };
|
|
91
|
+
const cleaned = cleanTemplateText(node.value);
|
|
92
|
+
return cleaned ? { kind: 'text', value: cleaned } : null;
|
|
93
|
+
}
|
|
94
|
+
case 'Comment':
|
|
95
|
+
return null; // dropped, like JSX comments
|
|
96
|
+
case 'Expression': {
|
|
97
|
+
const h = this.hole(node.hole);
|
|
98
|
+
return { kind: 'expr', code: h.code, reactive: h.reactive };
|
|
99
|
+
}
|
|
100
|
+
case 'Fragment':
|
|
101
|
+
return { kind: 'fragment', children: this.lowerChildren(node.children) };
|
|
102
|
+
case 'Element':
|
|
103
|
+
return this.lowerElement(node);
|
|
104
|
+
case 'Component':
|
|
105
|
+
return this.lowerComponent(node);
|
|
106
|
+
default:
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
lowerElement(el) {
|
|
111
|
+
// `<component is=${C} …>…</component>` → `<Dynamic component=${C} …>` (a
|
|
112
|
+
// reactive component swap). Dynamic reads `component` via normalizeAccessor,
|
|
113
|
+
// so it must be an ACCESSOR `() => C` (a bare function would be mis-called).
|
|
114
|
+
const isAttr = el.attributes.find((a) => a.kind === 'Attribute' && a.name === 'is');
|
|
115
|
+
if (el.tag === 'component' && isAttr && isAttr.value && isAttr.value.kind === 'hole') {
|
|
116
|
+
const rest = el.attributes.filter((a) => a !== isAttr);
|
|
117
|
+
const node = this.lowerComponent({ ...el, kind: 'Component', tag: 'Dynamic', tagHole: null, attributes: rest });
|
|
118
|
+
node.props.unshift({ name: 'component', kind: 'attr', expr: `() => (${this.hole(isAttr.value.hole).code})` });
|
|
119
|
+
return node;
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
kind: 'element',
|
|
123
|
+
tag: el.tag,
|
|
124
|
+
svg: el.namespace === 'svg',
|
|
125
|
+
props: this.lowerAttributes(el.attributes),
|
|
126
|
+
children: this.lowerChildren(el.children),
|
|
127
|
+
static: false,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
lowerComponent(el) {
|
|
131
|
+
// `<${Comp} />`: the tag is an interpolated expression — use the hole's code
|
|
132
|
+
// as the component identity, so `createComponent(<expr>, …)` is emitted.
|
|
133
|
+
const name = el.tagHole != null ? this.hole(el.tagHole).code : el.tag;
|
|
134
|
+
const props = this.lowerAttributes(el.attributes);
|
|
135
|
+
// Named slots: a child with `slot="header"` becomes `props.header` (a lazy
|
|
136
|
+
// getter, like `children`); the rest stay the default `children`. This is the
|
|
137
|
+
// Solid convention — the component reads `props.header` / `props.children`.
|
|
138
|
+
const { slots, rest } = this.partitionSlots(el.children);
|
|
139
|
+
for (const [slotName, nodes] of slots) {
|
|
140
|
+
const ir = nodes.length === 1 ? nodes[0] : { kind: 'fragment', children: nodes };
|
|
141
|
+
const slotProp = {
|
|
142
|
+
name: slotName,
|
|
143
|
+
kind: 'attr',
|
|
144
|
+
expr: this.emit(ir),
|
|
145
|
+
jsxElement: true,
|
|
146
|
+
};
|
|
147
|
+
// A slot replaces a same-named attribute rather than joining it. `<Card header>`
|
|
148
|
+
// alongside `<span slot="header">` otherwise emits the key twice — valid JS where
|
|
149
|
+
// the last wins, but the first is dead code and esbuild warns on it. Content is
|
|
150
|
+
// the more specific intent, so the slot is what survives.
|
|
151
|
+
const clash = props.findIndex((p) => p.name === slotName);
|
|
152
|
+
if (clash >= 0)
|
|
153
|
+
props[clash] = slotProp;
|
|
154
|
+
else
|
|
155
|
+
props.push(slotProp);
|
|
156
|
+
}
|
|
157
|
+
// `load:` is a directive, not a prop: it says when this component's module is
|
|
158
|
+
// fetched. Parsed with the same helper the JSX path uses so the two cannot drift.
|
|
159
|
+
const loadAttr = el.attributes.find((a) => a.kind === 'LoadDirective');
|
|
160
|
+
const load = loadAttr
|
|
161
|
+
? parseLoadDirective(`load:${loadAttr.strategy}`, loadAttr.modifier)
|
|
162
|
+
: undefined;
|
|
163
|
+
return {
|
|
164
|
+
kind: 'component',
|
|
165
|
+
name,
|
|
166
|
+
props,
|
|
167
|
+
children: this.lowerChildren(rest),
|
|
168
|
+
...(load ? { load } : {}),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/** Split a component's children into named slots (`slot="x"`) and the rest. */
|
|
172
|
+
partitionSlots(children) {
|
|
173
|
+
const slots = new Map();
|
|
174
|
+
const rest = [];
|
|
175
|
+
for (const child of children) {
|
|
176
|
+
if (child.kind === 'Element' || child.kind === 'Component') {
|
|
177
|
+
const slot = child.attributes.find((a) => a.kind === 'Attribute' && a.name === 'slot');
|
|
178
|
+
if (slot && slot.value && slot.value.kind === 'static') {
|
|
179
|
+
const stripped = { ...child, attributes: child.attributes.filter((a) => a !== slot) };
|
|
180
|
+
const ir = stripped.kind === 'Element' ? this.lowerElement(stripped) : this.lowerComponent(stripped);
|
|
181
|
+
const bucket = slots.get(slot.value.value) ?? [];
|
|
182
|
+
bucket.push(ir);
|
|
183
|
+
slots.set(slot.value.value, bucket);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
rest.push(child);
|
|
188
|
+
}
|
|
189
|
+
return { slots, rest };
|
|
190
|
+
}
|
|
191
|
+
// --- control-flow sugar -------------------------------------------------
|
|
192
|
+
/** `<el if=${cond}>…</el>` (+ optional `<el else>`) → `<Show when fallback>`. */
|
|
193
|
+
lowerIf(el, condHole, elseEl) {
|
|
194
|
+
const cond = this.hole(condHole);
|
|
195
|
+
const props = [
|
|
196
|
+
{ name: 'when', kind: 'attr', expr: cond.code, reactive: cond.reactive },
|
|
197
|
+
];
|
|
198
|
+
if (elseEl) {
|
|
199
|
+
const elseIR = this.stripAndLower(elseEl, (a) => a.kind === 'ElseDirective');
|
|
200
|
+
props.push({ name: 'fallback', kind: 'attr', expr: this.emit(elseIR), jsxElement: true });
|
|
201
|
+
}
|
|
202
|
+
const child = this.stripAndLower(el, (a) => a.kind === 'IfDirective');
|
|
203
|
+
return { kind: 'component', name: 'Show', props, children: [child] };
|
|
204
|
+
}
|
|
205
|
+
/** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
|
|
206
|
+
lowerEach(el) {
|
|
207
|
+
const eachDir = el.attributes.find((a) => a.kind === 'EachDirective');
|
|
208
|
+
if (!eachDir || eachDir.kind !== 'EachDirective')
|
|
209
|
+
return this.lowerNode(el);
|
|
210
|
+
const each = this.hole(eachDir.hole);
|
|
211
|
+
const props = [{ name: 'each', kind: 'attr', expr: each.code, reactive: each.reactive }];
|
|
212
|
+
if (eachDir.key) {
|
|
213
|
+
// `<For by>` wants a KEY FUNCTION, not a string. `key="id"` selects the
|
|
214
|
+
// `id` field: `(item) => item["id"]`; `key=${fn}` passes the function.
|
|
215
|
+
const by = 'static' in eachDir.key
|
|
216
|
+
? `(item) => item[${JSON.stringify(eachDir.key.static)}]`
|
|
217
|
+
: this.hole(eachDir.key.hole).code;
|
|
218
|
+
props.push({ name: 'by', kind: 'attr', expr: by });
|
|
219
|
+
}
|
|
220
|
+
// The element minus its each/key, repeated per item.
|
|
221
|
+
const arrow = this.itemArrow(el);
|
|
222
|
+
let childrenCode;
|
|
223
|
+
if (arrow) {
|
|
224
|
+
const bodyIR = { kind: 'expr', code: arrow.body, reactive: arrow.bodyReactive };
|
|
225
|
+
const repeated = this.rebuildWithChildren(el, [bodyIR]);
|
|
226
|
+
childrenCode = `(${arrow.params.join(', ')}) => (${this.emit(repeated)})`;
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
const repeated = this.stripAndLower(el, (a) => a.kind === 'EachDirective');
|
|
230
|
+
childrenCode = `() => (${this.emit(repeated)})`;
|
|
231
|
+
}
|
|
232
|
+
const children = [{ kind: 'expr', code: childrenCode, reactive: false }];
|
|
233
|
+
return { kind: 'component', name: 'For', props, children };
|
|
234
|
+
}
|
|
235
|
+
/** The single `${item => expr}` child arrow, if that's the element's content. */
|
|
236
|
+
itemArrow(el) {
|
|
237
|
+
const kids = el.children.filter((c) => !this.isBlankText(c));
|
|
238
|
+
if (kids.length !== 1 || kids[0].kind !== 'Expression')
|
|
239
|
+
return null;
|
|
240
|
+
return this.hole(kids[0].hole).arrow ?? null;
|
|
241
|
+
}
|
|
242
|
+
/** Lower an element to IR, dropping the directive attribute matched by `drop`. */
|
|
243
|
+
stripAndLower(el, drop) {
|
|
244
|
+
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
245
|
+
return clone.kind === 'Element' ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
246
|
+
}
|
|
247
|
+
/** Same element (minus each/key) but with explicit IR children. */
|
|
248
|
+
rebuildWithChildren(el, children) {
|
|
249
|
+
const props = this.lowerAttributes(el.attributes.filter((a) => a.kind !== 'EachDirective'));
|
|
250
|
+
if (el.kind === 'Element') {
|
|
251
|
+
return { kind: 'element', tag: el.tag, svg: el.namespace === 'svg', props, children, static: false };
|
|
252
|
+
}
|
|
253
|
+
return { kind: 'component', name: el.tag, props, children };
|
|
254
|
+
}
|
|
255
|
+
// --- attributes ---------------------------------------------------------
|
|
256
|
+
lowerAttributes(attrs) {
|
|
257
|
+
const props = [];
|
|
258
|
+
const classEntries = [];
|
|
259
|
+
let classReactive = false;
|
|
260
|
+
const styleEntries = [];
|
|
261
|
+
let styleReactive = false;
|
|
262
|
+
const usePairs = [];
|
|
263
|
+
for (const a of attrs) {
|
|
264
|
+
switch (a.kind) {
|
|
265
|
+
case 'IfDirective':
|
|
266
|
+
case 'EachDirective':
|
|
267
|
+
case 'ElseDirective':
|
|
268
|
+
break; // handled as control-flow at the parent level
|
|
269
|
+
case 'Attribute':
|
|
270
|
+
props.push(this.plainAttr(a));
|
|
271
|
+
break;
|
|
272
|
+
case 'PropertyBinding':
|
|
273
|
+
props.push({ name: a.name, kind: 'prop', expr: this.hole(a.hole).code, reactive: this.hole(a.hole).reactive });
|
|
274
|
+
break;
|
|
275
|
+
case 'EventBinding':
|
|
276
|
+
props.push(this.eventProp(a));
|
|
277
|
+
break;
|
|
278
|
+
case 'RefBinding':
|
|
279
|
+
props.push({ name: 'ref', kind: 'ref', expr: this.hole(a.hole).code, reactive: this.hole(a.hole).reactive });
|
|
280
|
+
break;
|
|
281
|
+
case 'Spread':
|
|
282
|
+
props.push({ name: '', kind: 'spread', expr: this.hole(a.hole).code });
|
|
283
|
+
break;
|
|
284
|
+
case 'ClassDirective': {
|
|
285
|
+
const h = this.hole(a.hole);
|
|
286
|
+
classEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
|
|
287
|
+
classReactive = classReactive || h.reactive;
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
case 'StyleDirective': {
|
|
291
|
+
const h = this.hole(a.hole);
|
|
292
|
+
styleEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
|
|
293
|
+
styleReactive = styleReactive || h.reactive;
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
case 'BindDirective':
|
|
297
|
+
props.push(...this.bindProps(a.name, this.hole(a.hole).code));
|
|
298
|
+
break;
|
|
299
|
+
case 'UseDirective': {
|
|
300
|
+
// `use=${dir}` → [dir]; `use:name` → [name]; `use:name=${opts}` →
|
|
301
|
+
// [name, () => opts]. `name` is an in-scope identifier (like Solid).
|
|
302
|
+
const dir = a.name ?? (a.hole != null ? this.hole(a.hole).code : null);
|
|
303
|
+
if (!dir)
|
|
304
|
+
break;
|
|
305
|
+
// parens so an object literal opts isn't read as a block body
|
|
306
|
+
usePairs.push(a.name != null && a.hole != null ? `[${dir}, () => (${this.hole(a.hole).code})]` : `[${dir}]`);
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
// Merge all class:/style: directives into one classList/style prop so they
|
|
312
|
+
// don't collide as duplicate keys in the emitted props object.
|
|
313
|
+
if (classEntries.length > 0) {
|
|
314
|
+
props.push({ name: 'classList', kind: 'attr', expr: `{ ${classEntries.join(', ')} }`, reactive: classReactive });
|
|
315
|
+
}
|
|
316
|
+
if (styleEntries.length > 0) {
|
|
317
|
+
props.push({ name: 'style', kind: 'attr', expr: `{ ${styleEntries.join(', ')} }`, reactive: styleReactive });
|
|
318
|
+
}
|
|
319
|
+
// All use: directives normalize to one `use` prop = an array of [dir, accessor?] pairs.
|
|
320
|
+
if (usePairs.length > 0) {
|
|
321
|
+
props.push({ name: 'use', kind: 'attr', expr: `[${usePairs.join(', ')}]` });
|
|
322
|
+
}
|
|
323
|
+
return props;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Lower an event binding. Plain `@click`/`onClick` (no modifiers) stays a
|
|
327
|
+
* DELEGATED handler (unchanged, byte-identical with JSX). `on:click` or any
|
|
328
|
+
* modifier (`.capture`/`.once`/`.passive`/`.prevent`/`.stop`/`.self`) emits a
|
|
329
|
+
* NATIVE `on:name` prop the runtime attaches via addEventListener.
|
|
330
|
+
*/
|
|
331
|
+
eventProp(a) {
|
|
332
|
+
const dom = a.name.toLowerCase();
|
|
333
|
+
const handler = this.hole(a.hole).code;
|
|
334
|
+
const native = a.syntax === 'colon' || a.modifiers.length > 0;
|
|
335
|
+
if (!native) {
|
|
336
|
+
return {
|
|
337
|
+
name: 'on' + capitalize(a.name),
|
|
338
|
+
kind: 'event',
|
|
339
|
+
event: { name: dom, delegated: DELEGATED.has(dom) },
|
|
340
|
+
expr: handler,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
const wrapped = this.wrapHandler(handler, a.modifiers);
|
|
344
|
+
const options = this.eventOptions(a.modifiers);
|
|
345
|
+
const value = options ? `[${wrapped}, ${options}]` : wrapped;
|
|
346
|
+
return { name: 'on:' + dom, kind: 'attr', expr: value };
|
|
347
|
+
}
|
|
348
|
+
/** Wrap a handler for `.prevent`/`.stop`/`.self` (no wrap when none apply). */
|
|
349
|
+
wrapHandler(handler, mods) {
|
|
350
|
+
const guard = mods.includes('self') ? 'if (e.target !== e.currentTarget) return; ' : '';
|
|
351
|
+
const pre = [];
|
|
352
|
+
if (mods.includes('prevent'))
|
|
353
|
+
pre.push('e.preventDefault();');
|
|
354
|
+
if (mods.includes('stop'))
|
|
355
|
+
pre.push('e.stopPropagation();');
|
|
356
|
+
if (!guard && pre.length === 0)
|
|
357
|
+
return handler;
|
|
358
|
+
return `(e) => { ${guard}${pre.join(' ')} return (${handler})(e); }`;
|
|
359
|
+
}
|
|
360
|
+
/** addEventListener options object from `.capture`/`.once`/`.passive`, or null. */
|
|
361
|
+
eventOptions(mods) {
|
|
362
|
+
const opts = [];
|
|
363
|
+
if (mods.includes('capture'))
|
|
364
|
+
opts.push('capture: true');
|
|
365
|
+
if (mods.includes('once'))
|
|
366
|
+
opts.push('once: true');
|
|
367
|
+
if (mods.includes('passive'))
|
|
368
|
+
opts.push('passive: true');
|
|
369
|
+
return opts.length ? `{ ${opts.join(', ')} }` : null;
|
|
370
|
+
}
|
|
371
|
+
/** `bind:value=${sig}` → `value={sig[0]()}` + `onInput={e => sig[1](e…value)}`. */
|
|
372
|
+
bindProps(prop, sigCode) {
|
|
373
|
+
const checked = prop === 'checked';
|
|
374
|
+
const evt = checked ? 'change' : 'input';
|
|
375
|
+
const accessor = checked ? 'checked' : 'value';
|
|
376
|
+
// Parenthesize the signal expression — it may be more than an identifier
|
|
377
|
+
// (`[get, set] as const`, `props.model`, a call), and `expr[0]()` would then
|
|
378
|
+
// mis-parse (e.g. `[...] as const[0]`).
|
|
379
|
+
const sig = `(${sigCode})`;
|
|
380
|
+
return [
|
|
381
|
+
{ name: prop, kind: 'attr', expr: `${sig}[0]()`, reactive: true },
|
|
382
|
+
{
|
|
383
|
+
name: 'on' + capitalize(evt),
|
|
384
|
+
kind: 'event',
|
|
385
|
+
event: { name: evt, delegated: DELEGATED.has(evt) },
|
|
386
|
+
// delegated handler: `target` is the bound element (currentTarget is the root)
|
|
387
|
+
expr: `(e) => ${sig}[1](e.target.${accessor})`,
|
|
388
|
+
},
|
|
389
|
+
];
|
|
390
|
+
}
|
|
391
|
+
/** A plain attribute (incl. lit `?bool`, mixed values) — old `mkProp` behavior. */
|
|
392
|
+
plainAttr(a) {
|
|
393
|
+
const v = a.value;
|
|
394
|
+
// `class=${{...}}` (object literal) -> classList (Solid convention), so it
|
|
395
|
+
// doesn't collide with a static `class="box"` (which stays className) — both
|
|
396
|
+
// apply. `class` -> className; `style=${{...}}` stays `style` (runtime merges).
|
|
397
|
+
// `html=${x}` is the raw-HTML escape hatch -> innerHTML.
|
|
398
|
+
let name = a.name;
|
|
399
|
+
if (a.name === 'class') {
|
|
400
|
+
name = v && v.kind === 'hole' && this.hole(v.hole).object ? 'classList' : 'className';
|
|
401
|
+
}
|
|
402
|
+
else if (a.name === 'html') {
|
|
403
|
+
name = 'innerHTML';
|
|
404
|
+
}
|
|
405
|
+
const kind = 'attr';
|
|
406
|
+
const base = { name, kind };
|
|
407
|
+
if (v == null) {
|
|
408
|
+
base.literal = true;
|
|
409
|
+
return base;
|
|
410
|
+
}
|
|
411
|
+
if (v.kind === 'static') {
|
|
412
|
+
base.literal = v.value;
|
|
413
|
+
return base;
|
|
414
|
+
}
|
|
415
|
+
if (v.kind === 'hole') {
|
|
416
|
+
const h = this.hole(v.hole);
|
|
417
|
+
base.expr = h.code;
|
|
418
|
+
base.reactive = h.reactive;
|
|
419
|
+
return base;
|
|
420
|
+
}
|
|
421
|
+
// mixed: template literal, reactive if any hole is
|
|
422
|
+
let reactive = false;
|
|
423
|
+
const body = v.parts
|
|
424
|
+
.map((p) => {
|
|
425
|
+
if ('text' in p)
|
|
426
|
+
return escapeTemplate(p.text);
|
|
427
|
+
const h = this.hole(p.hole);
|
|
428
|
+
reactive = reactive || h.reactive;
|
|
429
|
+
return '${' + h.code + '}';
|
|
430
|
+
})
|
|
431
|
+
.join('');
|
|
432
|
+
base.expr = '`' + body + '`';
|
|
433
|
+
base.reactive = reactive;
|
|
434
|
+
return base;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Lower a parsed template to IR. `pieces` are the static runs and hole slots as
|
|
439
|
+
* the parser wants them; `holes` describes each `${…}` in the same order. `used`
|
|
440
|
+
* collects the runtime symbols the emitted code imports.
|
|
441
|
+
*/
|
|
442
|
+
export function lowerTemplate(pieces, holes, used, opts = {}) {
|
|
443
|
+
const { root } = parseTemplate(pieces, { svg: opts.svg });
|
|
444
|
+
return new Lowerer(holes, used).lowerRoot(root.children);
|
|
445
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{parseTemplate as de}from"@fluixi/template-parser";var F={kind:"eager"},U=["pointerdown","focusin","keydown"],y=new Set(["eager","idle","visible","interaction","media","never"]),u=class extends Error{};function T(t,e){let n=t.slice(5);if(!y.has(n))throw new u(`Unknown load strategy 'load:${n}'. Expected one of ${[...y].join(", ")}.`);switch(n){case"eager":return F;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?W(e):[...U]};case"media":if(!e)throw new u(`'load:media' needs a query, e.g. load:media="(min-width: 1024px)".`);return{kind:"media",query:e};default:throw new u(`Unhandled load strategy '${n}'.`)}}function W(t){let e=t.split(/[\s,]+/).map(n=>n.trim()).filter(Boolean);if(e.length===0)throw new u("'load:interaction' was given no event names.");return e}var q=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),S={className:"class",htmlFor:"for"},G=new Set(["innerHTML","outerHTML","innerText","textContent","value","checked","selected","muted","defaultValue","defaultChecked","children","ref"]),K=new Set(["script","style","textarea","title"]);function R(t){return K.has(t)}function C(t){return S[t]??t}function h(t){return t.kind!=="attr"||t.expr!==void 0||t.name.includes(":")?!1:!G.has(t.name)}function $(t){if(!t.static||R(t.tag))return!1;for(let e of t.props)if(!h(e))return!1;for(let e of t.children)if(e.kind!=="text"&&!(e.kind==="element"&&$(e)))return!1;return!0}function f(t){let e=t.props.map(Z).filter(Boolean).join(""),n=`<${t.tag}${e}>`;return q.has(t.tag)?n:`${n}${t.children.map(Y).join("")}</${t.tag}>`}function Y(t){if(t.kind==="text")return Q(t.value);if(t.kind!=="element")throw new Error(`serializeStatic: unexpected ${t.kind}`);return f(t)}function Z(t){let e=S[t.name]??t.name,n=t.literal;return n===!0||n===void 0?` ${e}`:n===!1||n===null?"":` ${e}="${X(String(n))}"`}function X(t){return t.replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}function Q(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}var ee="<!--fx-->",te="<!--fx/-->";function N(t){return t.kind==="expr"||t.kind==="component"||t.kind==="control"}function ne(t){return t.kind==="text"||t.kind==="element"&&A(t)}function A(t){return t.svg||R(t.tag)||!t.props.every(e=>h(e))?!1:t.children.every(e=>ne(e)||N(e))}function k(t){if(N(t))return!0;let e=t.children;return e?e.some(k):!1}function P(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"||P(e))}function L(t){if(!A(t)||!P(t)||!t.children.some(k))return null;let e=[],n=[],r=0,i=c=>{let s=`_n$${r++}`;return e.push({ref:s,expr:c}),s};return o(t,"_el$"),{html:re(t),tag:t.tag,steps:e,holes:n};function o(c,s){let a=-1;c.children.forEach((d,p)=>{k(d)&&(a=p)});let l=null;for(let d=0;d<=a;d++){let p=c.children[d],m=l?`${l}.nextSibling`:`${s}.firstChild`;if(N(p)){let I=i(m),w=i(`holeEnd(${I})`);n.push({parentRef:s,startRef:I,endRef:w,node:p}),l=w;continue}let v=i(m);p.kind==="element"&&k(p)&&o(p,v),l=v}}}function re(t){return f(D(t)).split(j).join(ee+te)}function D(t){return{...t,children:t.children.map(e=>N(e)?{kind:"text",value:j}:e.kind==="element"?D(e):e)}}var j="\0fx-hole\0";var b={version:1,module:"@fluixi/dom",symbols:["createNativeElement","insert","spread","setAttribute","delegateEvents","createComponent","createMemo","untrack","Show","For","Index","Switch","Match","Dynamic","ErrorBoundary"]},ge={version:2,module:"@fluixi/dom",symbols:[...b.symbols,"template","cloneTemplate","walk"]};var ie={show:"Show",for:"For",index:"Index",switch:"Switch",match:"Match",dynamic:"Dynamic",errorBoundary:"ErrorBoundary",suspense:"Suspense"};function oe(t){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(t)}function _(t){return oe(t)?t:JSON.stringify(t)}function se(t){return t.expr!==void 0?t.reactive?`() => (${t.expr})`:`(${t.expr})`:JSON.stringify(t.literal??!0)}function ae(t){return t.expr!==void 0?`(${t.expr})`:JSON.stringify(t.literal??!0)}function le(t,e,n){let r=t.filter(s=>s.kind==="spread"),o=t.filter(s=>s.kind!=="spread").map(s=>`${_(s.name)}: ${se(s)}`);e!=null&&o.push(`children: ${e}`);let c=`{ ${o.join(", ")} }`;return r.length>0?(n.add("mergeProps"),`mergeProps(${r.map(s=>s.expr).join(", ")}, ${c})`):c}function M(t,e,n){return t.length===1?g(t[0],e,n):`[${t.map(r=>g(r,e,n)).join(", ")}]`}function O(t,e,n,r,i){r.add("createMemo"),r.add("createComponent");let o=e.filter(l=>l.kind==="spread"),s=e.filter(l=>l.kind!=="spread").map(l=>`get ${_(l.name)}() { return ${ae(l)}; }`);n.length>0&&s.push(`get children() { return ${M(n,r,i)}; }`);let a=`{ ${s.join(", ")} }`;return o.length>0&&(r.add("mergeProps"),a=`mergeProps(${o.map(l=>l.expr).join(", ")}, ${a})`),`createMemo(() => createComponent(${t}, ${a}))`}function ce(t,e){let n=C(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 H=!1;function g(t,e,n){switch(t.kind){case"text":return JSON.stringify(t.value);case"expr":return t.reactive?`() => (${t.code})`:`(${t.code})`;case"fragment":return t.children.length===0?"null":M(t.children,e,n);case"component":return O(t.name,t.props,t.children,e,n);case"control":{let r=ie[t.control]??t.control;return e.add(r),O(r,t.props,t.children,e,n)}case"element":{if(n&&t.static&&!t.svg&&$(t)){e.add("templateNode");let s=`_tmpl$${n.length}`;return n.push({id:s,html:f(t),tag:t.tag,svg:!1}),`templateNode(${s}, ${JSON.stringify(t.tag)})`}if(n&&H){let s=L(t);if(s){e.add("templateNode"),e.add("insert"),e.add("holeEnd"),e.add("holeContent"),e.add("holeScope");let a=`_tmpl$${n.length}`;n.push({id:a,html:s.html,tag:s.tag,svg:!1});let l=[`const _el$ = templateNode(${a}, ${JSON.stringify(s.tag)}, true);`];for(let d of s.steps)l.push(`const ${d.ref} = ${d.expr};`);for(let d of s.holes)l.push(`insert(${d.parentRef}, holeScope(${d.startRef}, () => (${g(d.node,e,n)})), ${d.endRef}, holeContent(${d.startRef}, ${d.endRef}));`);return l.push("return _el$;"),`(() => { ${l.join(" ")} })()`}}e.add("createNativeElement");let r=JSON.stringify(t.tag),i="_el$",o=[],c=t.svg?`${r}, true`:r;if(o.push(`const ${i} = createNativeElement(${c});`),t.props.length>0)if(!t.svg&&t.props.every(h))for(let s of t.props)o.push(ce(i,s));else{e.add("spread");let s=t.svg?", isSVG: true":"";o.push(`spread({ element: ${i}, props: ${le(t.props,null,e)}${s} });`)}for(let s of t.children){e.add("insert");let a=g(s,e,n),l=s.kind==="expr"&&s.reactive||s.kind==="component"||s.kind==="control";o.push(l?`insert(${i}, ${a}, null);`:`insert(${i}, ${a});`)}return o.push(`return ${i};`),`(() => { ${o.join(" ")} })()`}}}function B(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 J={name:"imperative",contract:b,emit(t,e){let n=new Set,r=e?.templateClone!==!1?[]:void 0;return H=e?.partialTemplates===!0,{code:g(t,n,r),imports:Array.from(n),templates:r}}};function x(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 z=new Set(["click","dblclick","input","change","submit","focus","blur","keydown","keyup","keypress","mousedown","mouseup"]);function pe(t){return t.replace(/\\/g,"\\\\").replace(/`/g,"\\`").replace(/\$\{/g,"\\${")}function V(t){return t.charAt(0).toUpperCase()+t.slice(1)}var E=class{constructor(e,n){this.holes=e;this.used=n}hole(e){return this.holes[e]??{code:"undefined",reactive:!1}}emit(e){let{code:n,imports:r,templates:i}=J.emit(e,{});for(let o of r)this.used.add(o);return B(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 c=i.attributes.find(s=>s.kind==="IfDirective");if(c&&c.kind==="IfDirective"){let s=r+1;s<e.length&&this.isBlankText(e[s])&&s++;let a=e[s],l=a&&(a.kind==="Element"||a.kind==="Component")&&a.attributes.some(d=>d.kind==="ElseDirective");n.push(this.lowerIf(i,c.hole,l?a:null)),l&&(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&&x(e.value)===""}lowerNode(e){switch(e.kind){case"Text":{if(e.raw)return{kind:"text",value:e.value};let n=x(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}}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(r=>r.kind==="Attribute"&&r.name==="is");if(e.tag==="component"&&n&&n.value&&n.value.kind==="hole"){let r=e.attributes.filter(o=>o!==n),i=this.lowerComponent({...e,kind:"Component",tag:"Dynamic",tagHole:null,attributes:r});return i.props.unshift({name:"component",kind:"attr",expr:`() => (${this.hole(n.value.hole).code})`}),i}return{kind:"element",tag:e.tag,svg:e.namespace==="svg",props:this.lowerAttributes(e.attributes),children:this.lowerChildren(e.children),static:!1}}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,l]of i){let d=l.length===1?l[0]:{kind:"fragment",children:l},p={name:a,kind:"attr",expr:this.emit(d),jsxElement:!0},m=r.findIndex(v=>v.name===a);m>=0?r[m]=p:r.push(p)}let c=e.attributes.find(a=>a.kind==="LoadDirective"),s=c?T(`load:${c.strategy}`,c.modifier):void 0;return{kind:"component",name:n,props:r,children:this.lowerChildren(o),...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(c=>c.kind==="Attribute"&&c.name==="slot");if(o&&o.value&&o.value.kind==="static"){let c={...i,attributes:i.attributes.filter(l=>l!==o)},s=c.kind==="Element"?this.lowerElement(c):this.lowerComponent(c),a=n.get(o.value.value)??[];a.push(s),n.set(o.value.value,a);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}];if(r){let s=this.stripAndLower(r,a=>a.kind==="ElseDirective");o.push({name:"fallback",kind:"attr",expr:this.emit(s),jsxElement:!0})}let c=this.stripAndLower(e,s=>s.kind==="IfDirective");return{kind:"component",name:"Show",props:o,children:[c]}}lowerEach(e){let n=e.attributes.find(a=>a.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}];if(n.key){let a="static"in n.key?`(item) => item[${JSON.stringify(n.key.static)}]`:this.hole(n.key.hole).code;i.push({name:"by",kind:"attr",expr:a})}let o=this.itemArrow(e),c;if(o){let a={kind:"expr",code:o.body,reactive:o.bodyReactive},l=this.rebuildWithChildren(e,[a]);c=`(${o.params.join(", ")}) => (${this.emit(l)})`}else{let a=this.stripAndLower(e,l=>l.kind==="EachDirective");c=`() => (${this.emit(a)})`}return{kind:"component",name:"For",props:i,children:[{kind:"expr",code:c,reactive:!1}]}}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"));return e.kind==="Element"?{kind:"element",tag:e.tag,svg:e.namespace==="svg",props:r,children:n,static:!1}:{kind:"component",name:e.tag,props:r,children:n}}lowerAttributes(e){let n=[],r=[],i=!1,o=[],c=!1,s=[];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});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});break;case"Spread":n.push({name:"",kind:"spread",expr:this.hole(a.hole).code});break;case"ClassDirective":{let l=this.hole(a.hole);r.push(`${JSON.stringify(a.name)}: ${l.code}`),i=i||l.reactive;break}case"StyleDirective":{let l=this.hole(a.hole);o.push(`${JSON.stringify(a.name)}: ${l.code}`),c=c||l.reactive;break}case"BindDirective":n.push(...this.bindProps(a.name,this.hole(a.hole).code));break;case"UseDirective":{let l=a.name??(a.hole!=null?this.hole(a.hole).code:null);if(!l)break;s.push(a.name!=null&&a.hole!=null?`[${l}, () => (${this.hole(a.hole).code})]`:`[${l}]`);break}}return r.length>0&&n.push({name:"classList",kind:"attr",expr:`{ ${r.join(", ")} }`,reactive:i}),o.length>0&&n.push({name:"style",kind:"attr",expr:`{ ${o.join(", ")} }`,reactive:c}),s.length>0&&n.push({name:"use",kind:"attr",expr:`[${s.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"+V(e.name),kind:"event",event:{name:n,delegated:z.has(n)},expr:r};let o=this.wrapHandler(r,e.modifiers),c=this.eventOptions(e.modifiers),s=c?`[${o}, ${c}]`: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=e==="checked",i=r?"change":"input",o=r?"checked":"value",c=`(${n})`;return[{name:e,kind:"attr",expr:`${c}[0]()`,reactive:!0},{name:"on"+V(i),kind:"event",event:{name:i,delegated:z.has(i)},expr:`(e) => ${c}[1](e.target.${o})`}]}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 a=this.hole(n.hole);return o.expr=a.code,o.reactive=a.reactive,o}let c=!1,s=n.parts.map(a=>{if("text"in a)return pe(a.text);let l=this.hole(a.hole);return c=c||l.reactive,"${"+l.code+"}"}).join("");return o.expr="`"+s+"`",o.reactive=c,o}};function ye(t,e,n,r={}){let{root:i}=de(t,{svg:r.svg});return new E(e,n).lowerRoot(i.children)}export{ye as lowerTemplate};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var f=Object.defineProperty;var o=Object.getOwnPropertyDescriptor;var c=Object.getOwnPropertyNames;var p=Object.prototype.hasOwnProperty;var s=(l,e)=>{for(var n in e)f(l,n,{get:e[n],enumerable:!0})},a=(l,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of c(e))!p.call(l,t)&&t!==n&&f(l,t,{get:()=>e[t],enumerable:!(r=o(e,t))||r.enumerable});return l};var g=l=>a(f({},"__esModule",{value:!0}),l);var h={};s(h,{cleanTemplateText:()=>u});module.exports=g(h);function u(l){let e=l.split(/\r\n|\n|\r/),n=0;for(let t=0;t<e.length;t++)/[^ \t]/.test(e[t])&&(n=t);let r="";for(let t=0;t<e.length;t++){let i=e[t].replace(/\t/g," ");t!==0&&(i=i.replace(/^ +/,"")),t!==e.length-1&&(i=i.replace(/ +$/,"")),i&&(t!==n&&(i+=" "),r+=i)}return r}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whitespace handling for template text runs.
|
|
3
|
+
*
|
|
4
|
+
* JSX and `` html`...` `` collapse a text run the same way — the rule came from
|
|
5
|
+
* JSX and the template syntax adopted it so an author moving between the two
|
|
6
|
+
* gets the same output. It is pure string work with no parser in it, so it lives
|
|
7
|
+
* here rather than in a front-end, where both can reach it and neither owns it.
|
|
8
|
+
*/
|
|
9
|
+
/** Canonical JSX text cleaning — kept identical to plugin.ts cleanTemplateText. */
|
|
10
|
+
export declare function cleanTemplateText(value: string): string;
|
|
11
|
+
//# sourceMappingURL=text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/lower/text.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,mFAAmF;AACnF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAgBvD"}
|