@forinda/kickjs-view 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs-view v0.2.0
2
+ * @forinda/kickjs-view v0.3.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -10,11 +10,11 @@
10
10
  */
11
11
  const FORM_STATE_KEYS={flash:`__inertia_flash`,errors:`__inertia_errors`,old:`__kick_old`,previous:`__inertia_previous`},SECRET_FIELD=/pass(word|code)?|passwd|pwd|secret|token|card|cvv|cvc|csrf|otp|(^|[_-])pin($|[_-])/i;function rememberPage(ctx,layout){if(layout===!1)return;let c=ctx,data=c?.session?.data,method=String(c?.req?.method??``).toUpperCase(),url=c?.req?.originalUrl??c?.req?.url;data&&method===`GET`&&typeof url==`string`&&(data[FORM_STATE_KEYS.previous]=url)}const FORM_GLOBALS=/* @__PURE__ */ new Set([`$errors`,`$old`,`$flash`]),emptyFormState=()=>({$errors:{},$old:{},$flash:{}});function takeFormState(ctx){let data=ctx?.session?.data,out={};if(!data)return out;let take=(name,key)=>{if(data[key]===void 0)return;let value=data[key];delete data[key],out[name]=value&&typeof value==`object`?value:{}};return take(`$errors`,FORM_STATE_KEYS.errors),take(`$old`,FORM_STATE_KEYS.old),take(`$flash`,FORM_STATE_KEYS.flash),out}const AST_FORMAT_VERSION=1;function advance(base,text,upTo){let{line,column}=base;for(let i=0;i<upTo;i++){let c=text.charCodeAt(i);c===10?(line++,column=1):c===13?text.charCodeAt(i+1)!==10&&(line++,column=1):column++}return{offset:base.offset+upTo,line,column}}const START={offset:0,line:1,column:1},positionAt=(source,offset)=>advance(START,source,offset),error=(file,span,message)=>({file,span,message,severity:`error`}),warning=(file,span,message)=>({file,span,message,severity:`warning`}),VALIDATORS={required:{args:[],message:`This field is required`},email:{args:[],message:`Enter a valid email address`},url:{args:[],message:`Enter a valid URL`},minLength:{args:[`number`],message:`Use at least {0} characters`},maxLength:{args:[`number`],message:`Use at most {0} characters`},min:{args:[`number`],message:`Enter {0} or more`},max:{args:[`number`],message:`Enter {0} or less`},pattern:{args:[`string`],message:`Enter a value in the expected format`},in:{args:[`string`],message:`Choose one of: {0}`}};function split(s,sep){let out=[],quote=``,at=0;for(let i=0;i<s.length;i++){let c=s[i];quote?c===`\\`?i++:c===quote&&(quote=``):c===`'`||c===`"`?quote=c:c===sep&&(out.push(s.slice(at,i)),at=i+1)}return out.push(s.slice(at)),out.map(p=>p.trim())}function parseValidate(src){let rules=[];for(let part of split(src,`|`)){if(!part)return{error:`empty validator in validate`};let[name,...raw]=split(part,`:`),spec=VALIDATORS[name];if(!spec)return{error:`unknown validator '${name}'`};if(raw.length!==spec.args.length)return{error:`${name} takes ${spec.args.length} argument${spec.args.length===1?``:`s`}`};let args=[];for(let i=0;i<raw.length;i++){let a=raw[i];if(spec.args[i]===`number`){if(!/^-?\d+(\.\d+)?$/.test(a))return{error:`${name} takes a number`};args.push(Number(a))}else{let m=/^(['"])([\s\S]*)\1$/.exec(a);if(!m)return{error:`${name} takes a quoted string`};args.push(m[2].split(`\\${m[1]}`).join(m[1]))}}if(name===`pattern`)try{RegExp(args[0])}catch{return{error:`pattern '${args[0]}' isn't a valid regular expression`}}rules.push([name,...args])}return{rules}}const NAMED={amp:`&`,lt:`<`,gt:`>`,quot:`"`,apos:`'`,nbsp:`\xA0`,colon:`:`,Tab:` `,NewLine:`
12
12
  `,sol:`/`,bsol:`\\`,period:`.`,comma:`,`,semi:`;`,equals:`=`,quest:`?`,num:`#`,excl:`!`,lpar:`(`,rpar:`)`,plus:`+`,hyphen:`-`,dash:`-`,copy:`©`,reg:`®`,trade:`™`,hellip:`…`,mdash:`—`,ndash:`–`,laquo:`«`,raquo:`»`};function decodeEntities(s){return s.includes(`&`)?s.replace(/&(?:#[xX]([0-9a-fA-F]+)|#(\d+)|([A-Za-z][A-Za-z0-9]*));?/g,(m,hex,dec,name)=>{if(hex!==void 0||dec!==void 0){let code=parseInt(hex??dec,hex===void 0?10:16);return code>0&&code<=1114111&&!(code>=55296&&code<=57343)?String.fromCodePoint(code):`�`}return Object.hasOwn(NAMED,name)&&m.endsWith(`;`)?NAMED[name]:m}):s}const URL_ATTRS=/* @__PURE__ */ new Set([`href`,`src`,`action`,`formaction`,`poster`,`cite`,`background`,`ping`,`xlink:href`,`srcset`]);function isUrlAttr(tag,attr){let name=attr.toLowerCase();return URL_ATTRS.has(name)||name===`data`&&tag.toLowerCase()===`object`}function layoutResolver(layouts){if(layouts===void 0)return()=>void 0;if(typeof layouts==`string`)return()=>layouts;let prefixes=Object.keys(layouts).toSorted((a,b)=>b.length-a.length);return page=>{let hit=prefixes.find(p=>p===``||page===p||page.startsWith(`${p}/`));return hit===void 0?void 0:layouts[hit]}}function configuredLayouts(project){let names=/* @__PURE__ */ new Set;if(typeof project.layouts==`string`)names.add(project.layouts);else if(project.layouts)for(let v of Object.values(project.layouts))names.add(v);return project.baseLayout&&names.add(project.baseLayout),names}const SEGMENT=/^[A-Za-z0-9_-]+$/;function templateName(name){if(typeof name==`string`&&name)return name.split(`/`).every(s=>SEGMENT.test(s))?name:void 0}function componentTag(name,componentsDir){return name.slice(componentsDir.length+1).split(`/`).join(`-`).toLowerCase()}const isComponent=(name,componentsDir)=>name.startsWith(`${componentsDir}/`);function optimize(nodes){let out=[];for(let n of nodes){let node=deep(n),last=out[out.length-1];if(node.k===`s`){if(!node.t)continue;if(last?.k===`s`){last.t+=node.t;continue}}out.push(node)}return out}function deep(n){switch(n.k){case`if`:return{...n,br:n.br.map(b=>({...b,b:optimize(b.b)}))};case`for`:return n.em?{...n,b:optimize(n.b),em:optimize(n.em)}:{...n,b:optimize(n.b)};case`sw`:return{...n,cs:n.cs.map(c=>({...c,b:optimize(c.b)})),...n.d?{d:optimize(n.d)}:{}};case`cmp`:return{...n,f:Object.fromEntries(Object.entries(n.f).map(([k,v])=>[k,optimize(v)]))};case`o`:return{...n,fb:optimize(n.fb)};case`s`:return{...n};default:return n}}const ANIMATION=/* @__PURE__ */ new Set([`animate`,`set`,`animatemotion`,`animatetransform`]),ANIMATION_VALUES=/* @__PURE__ */ new Set([`values`,`to`,`from`,`by`,`attributename`]),JSON_SCRIPT_TYPES=/* @__PURE__ */ new Set([`application/json`,`application/ld+json`]),NO_REFILL=/* @__PURE__ */ new Set([`password`,`file`,`hidden`,`submit`,`button`,`reset`,`image`]);function oldOf(name){return{k:`d`,n:`$old`,p:[{e:{k:`l`,v:name},o:1}]}}function equals(e,value){return{k:`b`,o:`==`,l:e,r:value}}const LOOP_VARS$1=/* @__PURE__ */ new Set([`$index`,`$first`,`$last`,`$even`,`$odd`,`$count`]),isFallthrough=a=>{if(a.kind===`class`||a.kind===`style`)return!0;let n=a.name.toLowerCase();return[`class`,`id`,`style`,`role`].includes(n)||n.startsWith(`aria-`)||n.startsWith(`data-`)},camel=name=>name.replace(/-([a-z])/g,(_,c)=>c.toUpperCase());var Checker=class{project;ast;component;diagnostics=[];slots=0;scopes=[/* @__PURE__ */ new Map];loops=[];warned=/* @__PURE__ */ new Set;tags=/* @__PURE__ */ new Map;signatures=/* @__PURE__ */ new Map;constructor(project,ast,component){this.project=project,this.ast=ast,this.component=component;for(let name of project.templates.keys()){if(!isComponent(name,project.componentsDir))continue;let tag=componentTag(name,project.componentsDir);this.tags.set(tag,[...this.tags.get(tag)??[],name])}}err(span,message){this.diagnostics.push(error(this.ast.file,span,message))}warn(span,message){this.diagnostics.push(warning(this.ast.file,span,message))}slot(){return this.slots++}bind(name,b){this.scopes[this.scopes.length-1].set(name,b)}lookup(name){for(let i=this.scopes.length-1;i>=0;i--){let b=this.scopes[i].get(name);if(b)return b}}scoped(fn){this.scopes.push(/* @__PURE__ */ new Map);try{return fn()}finally{this.scopes.pop()}}expr(e,where=`text`,whole=!1){switch(e.kind){case`literal`:return{k:`l`,v:e.value};case`unary`:return{k:`u`,o:e.op,a:this.expr(e.arg)};case`binary`:case`logical`:return{k:`b`,o:e.op,l:this.expr(e.left),r:this.expr(e.right)};case`conditional`:return{k:`c`,t:this.expr(e.test),y:this.expr(e.whenTrue),n:this.expr(e.whenFalse)};case`pipe`:return this.project.pipes.has(e.name)||this.err(e.span,`unknown pipe '${e.name}'`),e.name===`raw`&&!(whole&&where===`text`)&&this.err(e.span,where===`attr`?`raw isn't allowed in an attribute: it could break out of the quotes`:where===`text`?`raw must be the last pipe of the whole {{ }}: {{ value | raw }}`:`raw isn't allowed inside <${where===`rcdata`?`title> or <textarea`:`script`}>`),{k:`p`,n:e.name,i:this.expr(e.input),a:e.args.map(a=>this.expr(a))};case`path`:{let p=e.segments.map(s=>{let seg=s.computed?{e:this.expr(s.computed)}:{n:s.name};return s.optional&&(seg.o=1),seg});if(LOOP_VARS$1.has(e.root)){let loop=this.loops[this.loops.length-1];return loop===void 0?(this.err(e.span,`${e.root} is only available inside @for`),{k:`x`}):{k:`m`,s:loop,w:e.root}}let b=this.lookup(e.root);return b?.kind===`meta`?{k:`m`,s:b.s,w:b.w}:b?{k:`v`,s:b.s,p}:FORM_GLOBALS.has(e.root)?{k:`d`,n:e.root,p}:this.component?(this.warned.has(e.root)||(this.warned.add(e.root),this.warn(e.span,`'${e.root}' isn't an input of this component — declare it with @input ${e.root}`)),{k:`x`}):{k:`d`,n:e.root,p}}}}nodes(list,parent){let out=[],stack=[{list,i:0,parent}];for(;stack.length;){let f=stack[stack.length-1];if(f.i>=f.list.length){stack.pop(),f.close&&f.parent?.tag.toLowerCase()===`select`&&(this.select=void 0),f.close&&out.push({k:`s`,t:f.close});continue}let n=f.list[f.i++];if(n.k!==`element`){this.node(n,out,f.parent);continue}let open=this.element(n,out);open&&stack.push({list:open.children,i:0,parent:open,close:`</${open.tag}>`})}return out}node(n,out,parent){switch(n.k){case`text`:out.push({k:`s`,t:n.value});return;case`interp`:if(n.ctx===`comment`){this.err(n.span,`{{ }} isn't allowed inside an HTML comment — use {{-- --}} for a template comment`);return}if(n.ctx===`raw-text`){out.push(...this.rawText(n,parent));return}out.push({k:`i`,c:n.ctx===`rcdata`?`rcdata`:`text`,e:this.expr(n.expr,n.ctx===`rcdata`?`rcdata`:`text`,!0),at:at(n.span)});return;case`comment`:for(let c of n.children)c.k===`interp`&&this.node(c,out);out.push({k:`s`,t:`<!--${n.children.map(c=>c.k===`text`?c.value:``).join(``)}-->`});return;case`element`:{let open=this.element(n,out);open&&out.push(...this.nodes(open.children,open),{k:`s`,t:`</${open.tag}>`});return}case`if`:out.push({k:`if`,br:n.branches.map(b=>this.scoped(()=>{let cond=b.cond&&this.expr(b.cond),alias;b.alias&&(alias=this.slot(),this.bind(b.alias,{kind:`slot`,s:alias}));let br={b:this.nodes(b.body,parent)};return cond&&(br.c=cond),alias!==void 0&&(br.a=alias),br}))});return;case`for`:{let e=this.expr(n.of),m=this.slot(),it,body=this.scoped(()=>{typeof n.item==`string`?(it=this.slot(),this.bind(n.item,{kind:`slot`,s:it})):(it=[this.slot(),this.slot()],this.bind(n.item[0],{kind:`slot`,s:it[0]}),this.bind(n.item[1],{kind:`slot`,s:it[1]}));for(let[name,w]of Object.entries(n.lets))this.bind(name,{kind:`meta`,s:m,w});this.loops.push(m);try{return this.nodes(n.body,parent)}finally{this.loops.pop()}}),node={k:`for`,e,it,m,b:body,at:at(n.span)};n.empty&&(node.em=this.scoped(()=>this.nodes(n.empty,parent))),out.push(node);return}case`switch`:{let node={k:`sw`,e:this.expr(n.on),cs:n.cases.map(c=>({e:this.expr(c.value),b:this.scoped(()=>this.nodes(c.body,parent))}))};n.default&&(node.d=this.scoped(()=>this.nodes(n.default,parent))),out.push(node);return}case`let`:{let e=this.expr(n.expr),s=this.slot();this.scopes[this.scopes.length-1].has(n.name)&&this.err(n.span,`@let ${n.name} is declared twice here`),this.bind(n.name,{kind:`slot`,s}),out.push({k:`let`,s,e});return}case`verbatim`:out.push({k:`s`,t:n.text});return;case`slot-outlet`:out.push({k:`o`,n:n.name??``,fb:this.scoped(()=>this.nodes(n.fallback,parent))});return;case`slot-fill`:this.err(n.span,`@slot('…') { } goes at the top of a page with a layout, or inside a component tag`);return;case`assets`:out.push({k:`as`,en:n.entry})}}rawText(n,parent){let tag=parent?.tag.toLowerCase(),type=parent?.attrs.find(a=>a.name.toLowerCase()===`type`),typeValue=type?.kind===`static`?String(type.parts?.[0]??``).trim().toLowerCase():``,isJson=tag===`script`&&JSON_SCRIPT_TYPES.has(typeValue),only=parent?.children.filter(c=>!(c.k===`text`&&!c.value.trim())).length===1;return isJson&&only&&n.expr.kind===`pipe`&&n.expr.name===`json`?[{k:`i`,c:`json`,e:this.expr(n.expr,`json`),at:at(n.span)}]:(this.err(n.span,tag===`script`?`{{ }} isn't allowed inside <script>: pass data as <script type="application/json">{{ value | json }}<\/script>`:`{{ }} isn't allowed inside <${tag}>`),[])}element(el,out){let lower=el.tag.toLowerCase();if(isClientScript(el)){this.err(el.span,`<script client> must be at the top level of the component`);return}if(!el.foreign&&lower.includes(`-`)){let names=this.tags.get(lower);if(names&&names.length>1){this.err(el.span,`<${lower}> matches more than one component: ${names.join(`, `)}`);return}if(names){this.call(el,names[0],out);return}this.hasClientScript||this.warn(el.span,`<${lower}> isn't a component (no ${this.project.componentsDir}/…/${lower}.kick) — it's output as a custom element`)}let attrs=this.attrs(el),refill=this.refill(el,attrs);if(attrs.some(a=>a.t!==`s`)||el===this.rootElement){out.push({k:`s`,t:`<${el.tag}`});let node={k:`A`,tag:el.tag,a:attrs,at:at(el.span)};el===this.rootElement&&(node.f=1),out.push(node)}else out.push({k:`s`,t:`<${el.tag}${attrs.map(a=>staticAttr(a)).join(``)}`});if(el.void||el.selfClosing&&el.foreign)out.push({k:`s`,t:el.selfClosing?` />`:`>`});else return out.push({k:`s`,t:`>`}),refill&&lower===`textarea`&&el.children.length===0&&out.push({k:`s`,t:`
13
- `},{k:`i`,c:`rcdata`,e:oldOf(refill),at:at(el.span)}),el}validates=!1;validateAttr(el,a,out){if(a.kind!==`static`){this.err(a.span,`validate must be written, not bound`);return}if(!el.attrs.some(x=>x.name.toLowerCase()===`name`)){this.err(a.span,`validate needs a name on the same field`);return}let r=parseValidate(decodeEntities(String(a.parts?.[0]??``)));`error`in r?this.err(a.span,r.error):(this.validates=!0,out.push({t:`s`,n:`data-kick-validate`,v:JSON.stringify(r.rules).replace(/&/g,`&amp;`)}))}select;refill(el,attrs){if(el.foreign)return;let tag=el.tag.toLowerCase(),has=n=>el.attrs.some(a=>a.name.toLowerCase()===n),stat=n=>{let a=el.attrs.find(x=>x.name.toLowerCase()===n&&x.kind===`static`);return a?String(a.parts?.[0]??``):void 0},valueExpr=()=>{let a=el.attrs.find(x=>x.name.toLowerCase()===`value`);if(a?.kind===`static`)return{k:`l`,v:decodeEntities(String(a.parts?.[0]??``))};if(a?.kind===`interp`&&a.parts?.length===1&&typeof a.parts[0]!=`string`)return{k:`b`,o:`+`,l:{k:`l`,v:``},r:this.expr(a.parts[0],`attr`)}};if(tag===`option`){let value=valueExpr();if(this.select===void 0||value===void 0||has(`selected`))return;attrs.push({t:`b`,n:`selected`,e:equals(oldOf(this.select),value)});return}if(tag!==`input`&&tag!==`textarea`&&tag!==`select`)return;let name=stat(`name`);if(!name||SECRET_FIELD.test(name))return;if(tag===`select`)return this.select=name,name;if(tag===`textarea`)return name;if(has(`type`)&&stat(`type`)===void 0)return;let type=(stat(`type`)??`text`).toLowerCase();if(!NO_REFILL.has(type)){if(type===`checkbox`||type===`radio`){if(has(`checked`))return;let value=has(`value`)?valueExpr():{k:`l`,v:`on`};return value?(attrs.push({t:`b`,n:`checked`,e:equals(oldOf(name),value)}),name):void 0}if(!has(`value`))return attrs.push({t:`b`,n:`value`,e:oldOf(name)}),name}}rootElement;hasClientScript=!1;attrs(el){let tag=el.tag,out=[],httpEquiv=el.attrs.find(a=>a.name.toLowerCase()===`http-equiv`),refresh=tag.toLowerCase()===`meta`&&httpEquiv?.kind===`static`&&String(httpEquiv.parts?.[0]).trim().toLowerCase()===`refresh`;for(let a of el.attrs){let lower=a.name.toLowerCase();if(lower===`validate`&&!el.foreign){this.validateAttr(el,a,out);continue}if(a.kind!==`static`&&a.kind!==`bare`&&a.kind!==`class`&&a.kind!==`style`){if(lower.startsWith(`on`)){this.err(a.span,`${a.name} can't be interpolated: event handler attributes run their value as script`);continue}if(lower===`srcdoc`){this.err(a.span,`srcdoc can't be interpolated: its value is a whole HTML document`);continue}if(lower===`style`){this.err(a.span,`style can't be interpolated — bind one property with [style.prop]="value"`);continue}if(lower===`class`&&a.kind===`interp`){this.err(a.span,`class can't be interpolated — add classes with [class.name]="condition"`);continue}if(ANIMATION.has(tag.toLowerCase())&&ANIMATION_VALUES.has(lower)){this.err(a.span,`${a.name} can't be interpolated on <${tag}>: an animation can set href to a javascript: URL`);continue}if(refresh&&lower===`content`){this.err(a.span,`a refresh <meta>'s content can't be interpolated: it holds a URL to navigate to`);continue}}let url=isUrlAttr(tag,a.name)?lower===`srcset`?`srcset`:`url`:void 0;if(a.kind!==`bind`&&a.kind!==`class`&&a.kind!==`style`||a.expr)switch(a.kind){case`bare`:out.push({t:`s`,n:a.name,v:!0});break;case`static`:out.push({t:`s`,n:a.name,v:String(a.parts?.[0]??``)});break;case`interp`:{let node={t:`i`,n:a.name,p:a.parts.map(p=>typeof p==`string`?p:this.expr(p,`attr`))};url&&(node.u=url),out.push(node);break}case`bind`:{let node={t:`b`,n:a.name,e:this.expr(a.expr,`attr`)};url&&(node.u=url),out.push(node);break}case`class`:out.push({t:`c`,n:a.name,e:this.expr(a.expr,`attr`)});break;case`style`:{let node={t:`y`,n:a.name,e:this.expr(a.expr,`attr`)};a.unit&&(node.x=a.unit),out.push(node);break}}}return out}signature(name){let cached=this.signatures.get(name);if(cached)return cached;let ast=this.project.templates.get(name),outlets=/* @__PURE__ */ new Set,stack=[ast.body];for(;stack.length;)for(let n of stack.pop())if(n.k===`slot-outlet`)outlets.add(n.name??``),stack.push(n.fallback);else if(n.k===`element`||n.k===`comment`)stack.push(n.children);else if(n.k===`if`)for(let b of n.branches)stack.push(b.body);else n.k===`for`?stack.push(n.body,n.empty??[]):n.k===`switch`?stack.push(...n.cases.map(c=>c.body),n.default??[]):n.k===`slot-fill`&&stack.push(n.body);let sig={name,inputs:new Map(ast.inputs.map(i=>[i.name,{required:!i.default,def:i.default?.value}])),outlets,roots:rootElements(ast.body).length};return this.signatures.set(name,sig),sig}call(el,name,out){let sig=this.signature(name),target=this.project.templates.get(name),slotOf=/* @__PURE__ */ new Map;target.inputs.forEach((i,idx)=>slotOf.set(i.name,idx));let inputs=[],given=/* @__PURE__ */ new Set,fallthrough=[];for(let a of el.attrs){if(isFallthrough(a)){fallthrough.push(a);continue}let input=sig.inputs.has(a.name)?a.name:camel(a.name);if(!sig.inputs.has(input)){this.err(a.span,`unknown input '${a.name}' on <${el.tag}> — its inputs are ${[...sig.inputs.keys()].join(`, `)||`none`}`);continue}if(a.kind===`bind`&&!a.expr)continue;given.add(input);let e;if(a.kind===`bind`)e=this.expr(a.expr);else if(a.kind===`bare`)e={k:`l`,v:!0};else if(a.kind===`static`){let v=String(a.parts?.[0]??``);typeof sig.inputs.get(input).def==`boolean`&&(v===`true`||v===`false`)&&this.warn(a.span,`${a.name}="${v}" passes the string "${v}", which is truthy — write [${a.name}]="${v}"`),e={k:`l`,v:decodeEntities(v)}}else if(a.kind===`interp`)e=a.parts.reduce((acc,p)=>({k:`b`,o:`+`,l:acc,r:typeof p==`string`?{k:`l`,v:decodeEntities(p)}:this.expr(p)}),{k:`l`,v:``});else{this.err(a.span,`[${a.kind}.${a.name}] goes on the component's root element, not an input`);continue}inputs.push({s:slotOf.get(input),e})}for(let[input,info]of sig.inputs)info.required&&!given.has(input)&&this.err(el.span,`missing input '${input}' on <${el.tag}>`);fallthrough.length&&sig.roots!==1&&this.err(fallthrough[0].span,`<${el.tag}> passes ${fallthrough.map(a=>a.name).join(`, `)} to its root element, but ${name}.kick needs exactly one root element for that`);let fills={},rest=[];for(let c of el.children)c.k===`slot-fill`?(c.name in fills?this.err(c.span,`slot '${c.name}' is filled twice`):sig.outlets.has(c.name)||this.err(c.span,`<${el.tag}> has no slot '${c.name}'`),fills[c.name]=this.scoped(()=>this.nodes(c.body))):rest.push(c);rest.some(c=>!(c.k===`text`&&!c.value.trim()))&&(sig.outlets.has(``)?fills[``]=this.scoped(()=>this.nodes(rest)):this.err(el.span,`<${el.tag}> has no default <slot /> for the content inside it`));let fa=this.attrs({...el,attrs:fallthrough});out.push({k:`cmp`,n:name,in:inputs,fa,f:fills,at:at(el.span)})}layoutChain(start,span,page){let outlets=/* @__PURE__ */ new Set,seen=[page],name=start;for(;name;){if(!templateName(name)){this.err(span,`'${name}' isn't a valid template name`);return}if(seen.includes(name)){this.err(span,`layout cycle: ${[...seen,name].join(` → `)}`);return}let ast=this.project.templates.get(name);if(!ast){this.err(span,`layout '${name}' not found`);return}seen.push(name);for(let o of this.signature(name).outlets)outlets.add(o);name=ast.layout??void 0}return outlets}};const IMPORTS=/(^|[\n;])\s*import[\s{*'"]|\bimport\s*\(|(^|[\n;])\s*export\s*(\*(\s+as\s+[\w$]+)?|\{[^}]*\})\s*from\s*['"]/;function isClientScript(n){return n.k===`element`&&n.tag.toLowerCase()===`script`&&n.attrs.some(a=>a.name.toLowerCase()===`client`&&a.kind===`bare`)}function rootElements(body){let roots=[];for(let n of body)(n.k!==`text`||n.value.trim())&&n.k!==`comment`&&n.k!==`let`&&roots.push(n.k===`element`?n:null);return roots}function staticAttr(a){return a.v===!0?` ${a.n}`:` ${a.n}="${a.v.replace(/"/g,`&quot;`)}"`}const at=span=>[span.start.line,span.start.column],zero={start:{offset:0,line:1,column:1},end:{offset:0,line:1,column:1}};function check(name,project){let ast=project.templates.get(name);if(!ast||!templateName(name))return{diagnostics:[error(`${name}.kick`,zero,`template '${name}' not found`)]};let component=isComponent(name,project.componentsDir),c=new Checker(project,ast,component);if(!component&&ast.inputs.length)for(let i of ast.inputs)c.diagnostics.push(error(ast.file,i.span,`only components declare @input`));let inputs=ast.inputs.map((i,idx)=>{let s=idx,entry={n:i.name,s,r:!i.default};return i.default&&(entry.d=i.default.value),entry});c.slots=inputs.length;for(let i of inputs)c.bind(i.n,{kind:`slot`,s:i.s});let top=[],client;for(let n of ast.body){if(n.k===`element`&&n.tag.toLowerCase()===`script`&&n.attrs.some(a=>a.name.toLowerCase()===`client`&&a.kind!==`bare`)){c.diagnostics.push(error(ast.file,n.span,`write <script client> with no value`));continue}if(n.k!==`element`||!isClientScript(n)){top.push(n);continue}if(!component){c.diagnostics.push(error(ast.file,n.span,`<script client> is only for components (src/views/${project.componentsDir}/…)`));continue}if(client){c.diagnostics.push(error(ast.file,n.span,`a component has one <script client>`));continue}if(n.children.some(ch=>ch.k!==`text`)){c.diagnostics.push(error(ast.file,n.span,`<script client> can't use {{ }} — pass data through attributes`));continue}let code=n.children.map(ch=>ch.k===`text`?ch.value:``).join(``);if(IMPORTS.test(code)){c.diagnostics.push(error(ast.file,n.span,`client scripts can't import yet — keep the script self-contained`));continue}let tag=componentTag(name,project.componentsDir);tag===`kick-validate`?c.diagnostics.push(error(ast.file,n.span,`kick-validate is reserved for the validator script`)):/^[a-z][a-z0-9-]*$/.test(tag)?client={code,js:!1}:c.diagnostics.push(error(ast.file,n.span,`<script client> needs a component name of lowercase letters, digits and dashes: '${tag}'`))}if(c.hasClientScript=client!==void 0,component){let roots=rootElements(top);roots.length===1&&roots[0]&&(c.rootElement=roots[0])}let fills={},body=[],fillNodes=[];for(let n of top)n.k===`slot-fill`&&!component?fillNodes.push(n):body.push(n);let base=project.baseLayout;base!==void 0&&!project.templates.has(base)&&c.diagnostics.push(error(ast.file,zero,`base layout '${base}' not found`));let mapped=component||configuredLayouts(project).has(name)?void 0:layoutResolver(project.layouts)(name),layoutName=ast.layout===void 0?mapped:ast.layout??void 0,outlets=layoutName===void 0?void 0:c.layoutChain(layoutName,ast.layoutSpan??zero,name);base!==void 0&&ast.layout!==null&&!component&&name!==base&&project.templates.has(base)&&(outlets=/* @__PURE__ */ new Set([...outlets??[],...c.signature(base).outlets]));for(let f of fillNodes)f.name in fills?c.diagnostics.push(error(ast.file,f.span,`slot '${f.name}' is filled twice`)):outlets?outlets.has(f.name)?fills[f.name]=optimize(c.nodes(f.body)):c.diagnostics.push(error(ast.file,f.span,`the layout has no slot '${f.name}'`)):(c.diagnostics.push(warning(ast.file,f.span,`@slot('${f.name}') has no layout to fill here`)),fills[f.name]=optimize(c.nodes(f.body)));let template={v:1,name,file:ast.file,slots:0,inputs,fills,body:optimize(c.nodes(body))};return template.slots=c.slots,ast.layout!==void 0&&(template.layout=ast.layout),client&&(template.client=client),c.validates&&(template.validates=!0),c.diagnostics.some(d=>d.severity===`error`)?{diagnostics:c.diagnostics}:{template,diagnostics:c.diagnostics}}const FORBIDDEN_NAMES=/* @__PURE__ */ new Set([`__proto__`,`constructor`,`prototype`,`__defineGetter__`,`__defineSetter__`,`__lookupGetter__`,`__lookupSetter__`]),KEYWORDS$1={new:`constructing objects`,typeof:"`typeof`",instanceof:"`instanceof`",in:"`in`",delete:"`delete`",void:"`void`",function:`functions`,class:`classes`,await:"`await`",yield:"`yield`",this:"`this`"},FORBIDDEN_PUNCT={"(":`calls`,"=":`assignment`,"=>":`functions`,"*":"`*`","/":"`/`","%":"`%`","`":`template literals`,"{":`object literals`,"}":"`}`","...":`spread`,";":"`;`",",":"`,`","&":"`&`","^":"`^`","~":"`~`"},PUNCT=/* @__PURE__ */ "=== !== ... ?. ?? == != <= >= && || => ? : | ! < > + - . [ ] ( ) = * / % ` { } ; , & ^ ~".split(` `);var Fail=class extends Error{start;end;constructor(start,end,message){super(message),this.start=start,this.end=end}};const ESCAPES={"\\":`\\`,"'":`'`,'"':`"`,n:`
13
+ `},{k:`i`,c:`rcdata`,e:oldOf(refill),at:at(el.span)}),el}validates=!1;validateAttr(el,a,out){if(a.kind!==`static`){this.err(a.span,`validate must be written, not bound`);return}if(!el.attrs.some(x=>x.name.toLowerCase()===`name`)){this.err(a.span,`validate needs a name on the same field`);return}let r=parseValidate(decodeEntities(String(a.parts?.[0]??``)));`error`in r?this.err(a.span,r.error):(this.validates=!0,out.push({t:`s`,n:`data-kick-validate`,v:JSON.stringify(r.rules).replace(/&/g,`&amp;`)}))}select;refill(el,attrs){if(el.foreign)return;let tag=el.tag.toLowerCase(),has=n=>el.attrs.some(a=>a.name.toLowerCase()===n),stat=n=>{let a=el.attrs.find(x=>x.name.toLowerCase()===n&&x.kind===`static`);return a?String(a.parts?.[0]??``):void 0},valueExpr=()=>{let a=el.attrs.find(x=>x.name.toLowerCase()===`value`);if(a?.kind===`static`)return{k:`l`,v:decodeEntities(String(a.parts?.[0]??``))};if(a?.kind===`interp`&&a.parts?.length===1&&typeof a.parts[0]!=`string`)return{k:`b`,o:`+`,l:{k:`l`,v:``},r:this.expr(a.parts[0],`attr`)}};if(tag===`option`){let value=valueExpr();if(this.select===void 0||value===void 0||has(`selected`))return;attrs.push({t:`b`,n:`selected`,e:equals(oldOf(this.select),value)});return}if(tag!==`input`&&tag!==`textarea`&&tag!==`select`)return;let name=stat(`name`);if(!name||SECRET_FIELD.test(name))return;if(tag===`select`)return this.select=name,name;if(tag===`textarea`)return name;if(has(`type`)&&stat(`type`)===void 0)return;let type=(stat(`type`)??`text`).toLowerCase();if(!NO_REFILL.has(type)){if(type===`checkbox`||type===`radio`){if(has(`checked`))return;let value=has(`value`)?valueExpr():{k:`l`,v:`on`};return value?(attrs.push({t:`b`,n:`checked`,e:equals(oldOf(name),value)}),name):void 0}if(!has(`value`))return attrs.push({t:`b`,n:`value`,e:oldOf(name)}),name}}rootElement;hasClientScript=!1;attrs(el){let tag=el.tag,out=[],httpEquiv=el.attrs.find(a=>a.name.toLowerCase()===`http-equiv`),refresh=tag.toLowerCase()===`meta`&&httpEquiv?.kind===`static`&&String(httpEquiv.parts?.[0]).trim().toLowerCase()===`refresh`;for(let a of el.attrs){let lower=a.name.toLowerCase();if(lower===`validate`&&!el.foreign){this.validateAttr(el,a,out);continue}if(a.kind!==`static`&&a.kind!==`bare`&&a.kind!==`class`&&a.kind!==`style`){if(lower.startsWith(`on`)){this.err(a.span,`${a.name} can't be interpolated: event handler attributes run their value as script`);continue}if(lower===`srcdoc`){this.err(a.span,`srcdoc can't be interpolated: its value is a whole HTML document`);continue}if(lower===`style`){this.err(a.span,`style can't be interpolated — bind one property with [style.prop]="value"`);continue}if(lower===`class`&&a.kind===`interp`){this.err(a.span,`class can't be interpolated — add classes with [class.name]="condition"`);continue}if(ANIMATION.has(tag.toLowerCase())&&ANIMATION_VALUES.has(lower)){this.err(a.span,`${a.name} can't be interpolated on <${tag}>: an animation can set href to a javascript: URL`);continue}if(refresh&&lower===`content`){this.err(a.span,`a refresh <meta>'s content can't be interpolated: it holds a URL to navigate to`);continue}}let url=isUrlAttr(tag,a.name)?lower===`srcset`?`srcset`:`url`:void 0;if(a.kind!==`bind`&&a.kind!==`class`&&a.kind!==`style`||a.expr)switch(a.kind){case`bare`:out.push({t:`s`,n:a.name,v:!0});break;case`static`:out.push({t:`s`,n:a.name,v:String(a.parts?.[0]??``)});break;case`interp`:{let node={t:`i`,n:a.name,p:a.parts.map(p=>typeof p==`string`?p:this.expr(p,`attr`))};url&&(node.u=url),out.push(node);break}case`bind`:{let node={t:`b`,n:a.name,e:this.expr(a.expr,`attr`)};url&&(node.u=url),out.push(node);break}case`class`:out.push({t:`c`,n:a.name,e:this.expr(a.expr,`attr`)});break;case`style`:{let node={t:`y`,n:a.name,e:this.expr(a.expr,`attr`)};a.unit&&(node.x=a.unit),out.push(node);break}}}return out}signature(name){let cached=this.signatures.get(name);if(cached)return cached;let ast=this.project.templates.get(name),outlets=/* @__PURE__ */ new Set,stack=[ast.body];for(;stack.length;)for(let n of stack.pop())if(n.k===`slot-outlet`)outlets.add(n.name??``),stack.push(n.fallback);else if(n.k===`element`||n.k===`comment`)stack.push(n.children);else if(n.k===`if`)for(let b of n.branches)stack.push(b.body);else n.k===`for`?stack.push(n.body,n.empty??[]):n.k===`switch`?stack.push(...n.cases.map(c=>c.body),n.default??[]):n.k===`slot-fill`&&stack.push(n.body);let sig={name,inputs:new Map(ast.inputs.map(i=>[i.name,{required:!i.default,def:i.default?.value}])),outlets,roots:rootElements(ast.body).length};return this.signatures.set(name,sig),sig}call(el,name,out){let sig=this.signature(name),target=this.project.templates.get(name),slotOf=/* @__PURE__ */ new Map;target.inputs.forEach((i,idx)=>slotOf.set(i.name,idx));let inputs=[],given=/* @__PURE__ */ new Set,fallthrough=[];for(let a of el.attrs){if(isFallthrough(a)){fallthrough.push(a);continue}let input=sig.inputs.has(a.name)?a.name:camel(a.name);if(!sig.inputs.has(input)){this.err(a.span,`unknown input '${a.name}' on <${el.tag}> — its inputs are ${[...sig.inputs.keys()].join(`, `)||`none`}`);continue}if(a.kind===`bind`&&!a.expr)continue;given.add(input);let e;if(a.kind===`bind`)e=this.expr(a.expr);else if(a.kind===`bare`)e={k:`l`,v:!0};else if(a.kind===`static`){let v=String(a.parts?.[0]??``);typeof sig.inputs.get(input).def==`boolean`&&(v===`true`||v===`false`)&&this.warn(a.span,`${a.name}="${v}" passes the string "${v}", which is truthy — write [${a.name}]="${v}"`),e={k:`l`,v:decodeEntities(v)}}else if(a.kind===`interp`)e=a.parts.reduce((acc,p)=>({k:`b`,o:`+`,l:acc,r:typeof p==`string`?{k:`l`,v:decodeEntities(p)}:this.expr(p)}),{k:`l`,v:``});else{this.err(a.span,`[${a.kind}.${a.name}] goes on the component's root element, not an input`);continue}inputs.push({s:slotOf.get(input),e})}for(let[input,info]of sig.inputs)info.required&&!given.has(input)&&this.err(el.span,`missing input '${input}' on <${el.tag}>`);fallthrough.length&&sig.roots!==1&&this.err(fallthrough[0].span,`<${el.tag}> passes ${fallthrough.map(a=>a.name).join(`, `)} to its root element, but ${name}.kick needs exactly one root element for that`);let fills={},rest=[];for(let c of el.children)c.k===`slot-fill`?(c.name in fills?this.err(c.span,`slot '${c.name}' is filled twice`):sig.outlets.has(c.name)||this.err(c.span,`<${el.tag}> has no slot '${c.name}'`),fills[c.name]=this.scoped(()=>this.nodes(c.body))):rest.push(c);rest.some(c=>!(c.k===`text`&&!c.value.trim()))&&(sig.outlets.has(``)?fills[``]=this.scoped(()=>this.nodes(rest)):this.err(el.span,`<${el.tag}> has no default <slot /> for the content inside it`));let fa=this.attrs({...el,attrs:fallthrough});out.push({k:`cmp`,n:name,in:inputs,fa,f:fills,at:at(el.span)})}layoutChain(start,span,page){let outlets=/* @__PURE__ */ new Set,seen=[page],name=start;for(;name;){if(!templateName(name)){this.err(span,`'${name}' isn't a valid template name`);return}if(seen.includes(name)){this.err(span,`layout cycle: ${[...seen,name].join(` → `)}`);return}let ast=this.project.templates.get(name);if(!ast){this.err(span,`layout '${name}' not found`);return}seen.push(name);for(let o of this.signature(name).outlets)outlets.add(o);name=ast.layout??void 0}return outlets}};function isClientScript(n){return n.k===`element`&&n.tag.toLowerCase()===`script`&&n.attrs.some(a=>a.name.toLowerCase()===`client`&&a.kind===`bare`)}function rootElements(body){let roots=[];for(let n of body)(n.k!==`text`||n.value.trim())&&n.k!==`comment`&&n.k!==`let`&&roots.push(n.k===`element`?n:null);return roots}function staticAttr(a){return a.v===!0?` ${a.n}`:` ${a.n}="${a.v.replace(/"/g,`&quot;`)}"`}const at=span=>[span.start.line,span.start.column],zero={start:{offset:0,line:1,column:1},end:{offset:0,line:1,column:1}};function check(name,project){let ast=project.templates.get(name);if(!ast||!templateName(name))return{diagnostics:[error(`${name}.kick`,zero,`template '${name}' not found`)]};let component=isComponent(name,project.componentsDir),c=new Checker(project,ast,component);if(!component&&ast.inputs.length)for(let i of ast.inputs)c.diagnostics.push(error(ast.file,i.span,`only components declare @input`));let inputs=ast.inputs.map((i,idx)=>{let s=idx,entry={n:i.name,s,r:!i.default};return i.default&&(entry.d=i.default.value),entry});c.slots=inputs.length;for(let i of inputs)c.bind(i.n,{kind:`slot`,s:i.s});let top=[],client;for(let n of ast.body){if(n.k===`element`&&n.tag.toLowerCase()===`script`&&n.attrs.some(a=>a.name.toLowerCase()===`client`&&a.kind!==`bare`)){c.diagnostics.push(error(ast.file,n.span,`write <script client> with no value`));continue}if(n.k!==`element`||!isClientScript(n)){top.push(n);continue}if(!component){c.diagnostics.push(error(ast.file,n.span,`<script client> is only for components (src/views/${project.componentsDir}/…)`));continue}if(client){c.diagnostics.push(error(ast.file,n.span,`a component has one <script client>`));continue}if(n.children.some(ch=>ch.k!==`text`)){c.diagnostics.push(error(ast.file,n.span,`<script client> can't use {{ }} — pass data through attributes`));continue}let code=n.children.map(ch=>ch.k===`text`?ch.value:``).join(``),tag=componentTag(name,project.componentsDir);tag===`kick-validate`?c.diagnostics.push(error(ast.file,n.span,`kick-validate is reserved for the validator script`)):/^[a-z][a-z0-9-]*$/.test(tag)?client={code,js:!1}:c.diagnostics.push(error(ast.file,n.span,`<script client> needs a component name of lowercase letters, digits and dashes: '${tag}'`))}if(c.hasClientScript=client!==void 0,component){let roots=rootElements(top);roots.length===1&&roots[0]&&(c.rootElement=roots[0])}let fills={},body=[],fillNodes=[];for(let n of top)n.k===`slot-fill`&&!component?fillNodes.push(n):body.push(n);let base=project.baseLayout;base!==void 0&&!project.templates.has(base)&&c.diagnostics.push(error(ast.file,zero,`base layout '${base}' not found`));let mapped=component||configuredLayouts(project).has(name)?void 0:layoutResolver(project.layouts)(name),layoutName=ast.layout===void 0?mapped:ast.layout??void 0,outlets=layoutName===void 0?void 0:c.layoutChain(layoutName,ast.layoutSpan??zero,name);base!==void 0&&ast.layout!==null&&!component&&name!==base&&project.templates.has(base)&&(outlets=/* @__PURE__ */ new Set([...outlets??[],...c.signature(base).outlets]));for(let f of fillNodes)f.name in fills?c.diagnostics.push(error(ast.file,f.span,`slot '${f.name}' is filled twice`)):outlets?outlets.has(f.name)?fills[f.name]=optimize(c.nodes(f.body)):c.diagnostics.push(error(ast.file,f.span,`the layout has no slot '${f.name}'`)):(c.diagnostics.push(warning(ast.file,f.span,`@slot('${f.name}') has no layout to fill here`)),fills[f.name]=optimize(c.nodes(f.body)));let template={v:1,name,file:ast.file,slots:0,inputs,fills,body:optimize(c.nodes(body))};return template.slots=c.slots,ast.layout!==void 0&&(template.layout=ast.layout),client&&(template.client=client),c.validates&&(template.validates=!0),c.diagnostics.some(d=>d.severity===`error`)?{diagnostics:c.diagnostics}:{template,diagnostics:c.diagnostics}}const FORBIDDEN_NAMES=/* @__PURE__ */ new Set([`__proto__`,`constructor`,`prototype`,`__defineGetter__`,`__defineSetter__`,`__lookupGetter__`,`__lookupSetter__`]),KEYWORDS$1={new:`constructing objects`,typeof:"`typeof`",instanceof:"`instanceof`",in:"`in`",delete:"`delete`",void:"`void`",function:`functions`,class:`classes`,await:"`await`",yield:"`yield`",this:"`this`"},FORBIDDEN_PUNCT={"(":`calls`,"=":`assignment`,"=>":`functions`,"*":"`*`","/":"`/`","%":"`%`","`":`template literals`,"{":`object literals`,"}":"`}`","...":`spread`,";":"`;`",",":"`,`","&":"`&`","^":"`^`","~":"`~`"},PUNCT=/* @__PURE__ */ "=== !== ... ?. ?? == != <= >= && || => ? : | ! < > + - . [ ] ( ) = * / % ` { } ; , & ^ ~".split(` `);var Fail=class extends Error{start;end;constructor(start,end,message){super(message),this.start=start,this.end=end}};const ESCAPES={"\\":`\\`,"'":`'`,'"':`"`,n:`
14
14
  `,t:` `,r:`\r`};function tokenize(src){let out=[],i=0;for(;i<src.length;){let c=src[i];if(/\s/.test(c)){i++;continue}let start=i;if(/[A-Za-z_$]/.test(c)){for(;i<src.length&&/[\w$]/.test(src[i]);)i++;out.push({t:`ident`,v:src.slice(start,i),start,end:i});continue}if(/\d/.test(c)){for(;i<src.length&&/\d/.test(src[i]);)i++;if(src[i]===`.`&&/\d/.test(src[i+1]??``))for(i++;i<src.length&&/\d/.test(src[i]);)i++;out.push({t:`num`,v:Number(src.slice(start,i)),start,end:i});continue}if(c===`'`||c===`"`){let v=``;for(i++;i<src.length&&src[i]!==c;)if(src[i]===`\\`){let e=src[i+1]??``;if(e===`u`){let hex=src.slice(i+2,i+6);if(!/^[0-9a-fA-F]{4}$/.test(hex))throw new Fail(i,i+2,`a \\u escape needs four hex digits`);v+=String.fromCharCode(parseInt(hex,16)),i+=6;continue}if(!(e in ESCAPES))throw new Fail(i,i+2,`unknown escape \\${e}`);v+=ESCAPES[e],i+=2}else v+=src[i],i++;if(i>=src.length)throw new Fail(start,i,`unterminated string`);i++,out.push({t:`str`,v,start,end:i});continue}let p=PUNCT.find(op=>src.startsWith(op,i));if(!p)throw new Fail(i,i+1,`unexpected character '${c}'`);i+=p.length,out.push({t:`punct`,v:p,start,end:i})}return out.push({t:`eof`,v:``,start:src.length,end:src.length}),out}const BP={pipe:1,ternary:2,nullish:3,or:4,and:5,eq:6,rel:7,add:8,unary:9},BINARY={"??":[BP.nullish,`logical`],"||":[BP.or,`logical`],"&&":[BP.and,`logical`],"==":[BP.eq,`binary`],"!=":[BP.eq,`binary`],"===":[BP.eq,`binary`],"!==":[BP.eq,`binary`],"<":[BP.rel,`binary`],">":[BP.rel,`binary`],"<=":[BP.rel,`binary`],">=":[BP.rel,`binary`],"+":[BP.add,`binary`],"-":[BP.add,`binary`]};var Parser=class{src;base;tokens;i=0;constructor(src,base,tokens){this.src=src,this.base=base,this.tokens=tokens}peek(){return this.tokens[this.i]}next(){return this.tokens[this.i++]}span(start,end){return{start:advance(this.base,this.src,start),end:advance(this.base,this.src,end)}}isPunct(v){let t=this.peek();return t.t===`punct`&&t.v===v}expect(v){let t=this.peek();if(t.t!==`punct`||t.v!==v)throw new Fail(t.start,t.end,t.t===`eof`?`expected '${v}'`:`expected '${v}', found '${this.text(t)}'`);return this.next()}text(t){return this.src.slice(t.start,t.end)}forbidden(t){let what=t.t===`punct`?FORBIDDEN_PUNCT[t.v]:KEYWORDS$1[t.v];throw new Fail(t.start,t.end,`${what??`'${this.text(t)}'`} aren't allowed in templates`)}parseAll(){let e=this.expr(0),t=this.peek();if(t.t!==`eof`)throw(t.t===`punct`&&Object.hasOwn(FORBIDDEN_PUNCT,t.v)||t.t===`ident`&&Object.hasOwn(KEYWORDS$1,t.v))&&this.forbidden(t),new Fail(t.start,t.end,`unexpected '${this.text(t)}'`);return e}expr(min){let left=this.prefix();for(;;){let t=this.peek();if(t.t===`ident`&&(t.v===`in`||t.v===`instanceof`)&&this.forbidden(t),t.t!==`punct`)break;if(t.v===`|`){if(min>BP.pipe)break;this.next();let name=this.next();if(name.t!==`ident`)throw new Fail(name.start,name.end,`expected a pipe name after |`);let args=[];for(;this.isPunct(`:`);)if(this.next(),args.push(this.expr(BP.nullish)),this.isPunct(`?`)){let q=this.peek();throw new Fail(q.start,q.end,`wrap a conditional pipe argument in parentheses`)}left={kind:`pipe`,input:left,name:name.v,args,span:this.join(left,name.end)};continue}if(t.v===`?`){if(min>BP.ternary)break;this.next();let whenTrue=this.expr(BP.ternary);this.expect(`:`);let whenFalse=this.expr(BP.ternary);left={kind:`conditional`,test:left,whenTrue,whenFalse,span:this.joinExpr(left,whenFalse)};continue}let bin=BINARY[t.v];if(bin){let[bp,kind]=bin;if(bp<min)break;this.next();let right=this.expr(bp+1);left={kind,op:t.v,left,right,span:this.joinExpr(left,right)}}else{Object.hasOwn(FORBIDDEN_PUNCT,t.v)&&this.forbidden(t);break}}return left}join(left,end){return{start:left.span.start,end:advance(this.base,this.src,end)}}joinExpr(left,right){return{start:left.span.start,end:right.span.end}}prefix(){let t=this.next();switch(t.t){case`num`:return{kind:`literal`,value:t.v,span:this.span(t.start,t.end)};case`str`:return{kind:`literal`,value:t.v,span:this.span(t.start,t.end)};case`ident`:if(Object.hasOwn(KEYWORDS$1,t.v)&&this.forbidden(t),t.v===`true`||t.v===`false`)return{kind:`literal`,value:t.v===`true`,span:this.span(t.start,t.end)};if(t.v===`null`)return{kind:`literal`,value:null,span:this.span(t.start,t.end)};if(FORBIDDEN_NAMES.has(t.v))throw new Fail(t.start,t.end,`'${t.v}' is not allowed in templates`);return this.path(t);case`punct`:if(t.v===`!`||t.v===`-`){let arg=this.expr(BP.unary);return{kind:`unary`,op:t.v,arg,span:{start:this.span(t.start,t.end).start,end:arg.span.end}}}if(t.v===`(`){let e=this.expr(0);if(this.expect(`)`),this.isPunct(`.`)||this.isPunct(`[`)||this.isPunct(`?.`)){let m=this.peek();throw new Fail(m.start,m.end,`property access needs a name in front: (…).x is not allowed`)}return e}throw t.v===`[`?new Fail(t.start,t.end,`array literals aren't allowed in templates`):(Object.hasOwn(FORBIDDEN_PUNCT,t.v)&&this.forbidden(t),new Fail(t.start,t.end,`unexpected '${t.v}'`));case`eof`:throw new Fail(t.start,t.end,`expected an expression`)}}path(root){let segments=[],end=root.end;for(;;){let t=this.peek();if(t.t!==`punct`)break;if(t.v===`.`||t.v===`?.`){this.next();let optional=t.v===`?.`;if(optional&&this.isPunct(`[`)){this.next();let computed=this.expr(0);end=this.expect(`]`).end,segments.push({computed,optional});continue}optional&&this.isPunct(`(`)&&this.forbidden(this.peek());let name=this.next();if(name.t!==`ident`)throw new Fail(name.start,name.end,`expected a property name`);if(FORBIDDEN_NAMES.has(name.v))throw new Fail(name.start,name.end,`'${name.v}' is not allowed in templates`);segments.push({name:name.v,optional}),end=name.end}else if(t.v===`[`){this.next();let computed=this.expr(0);end=this.expect(`]`).end,segments.push({computed,optional:!1})}else{t.v===`(`&&this.forbidden(t);break}}return{kind:`path`,root:root.v,segments,span:this.span(root.start,end)}}};function parseExpression(src,file,base){try{return{expr:new Parser(src,base,tokenize(src)).parseAll(),diagnostics:[]}}catch(err){if(!(err instanceof Fail))throw err;let span={start:advance(base,src,err.start),end:advance(base,src,err.end)};return{diagnostics:[error(file,span,err.message)]}}}const VOID=new Set(`area base br col embed hr img input link meta param source track wbr`.split(` `)),RAW_TEXT=/* @__PURE__ */ new Set([`script`,`style`]),RCDATA=/* @__PURE__ */ new Set([`title`,`textarea`]),INTEGRATION_POINTS=/* @__PURE__ */ new Set([`foreignobject`,`desc`,`title`,`annotation-xml`]),KEYWORDS=/^@(if|else|for|empty|switch|case|default|let|layout|input|slot|error|verbatim|assets)(?![\w$-])/,LOOP_VARS=/* @__PURE__ */ new Set([`$index`,`$first`,`$last`,`$even`,`$odd`,`$count`]);var Fatal=class extends Error{},TemplateParser=class{s;file;i=0;text=``;textStart=0;seenOutput=!1;lineStarts=[0];diagnostics=[];ast;frames;constructor(s,file){this.s=s,this.file=file;for(let j=0;j<s.length;j++){let c=s.charCodeAt(j);(c===10||c===13&&s.charCodeAt(j+1)!==10)&&this.lineStarts.push(j+1)}this.ast={file,inputs:[],body:[]},this.frames=[{type:`root`,children:this.ast.body}]}pos(offset){let lo=0,hi=this.lineStarts.length-1;for(;lo<hi;){let mid=lo+hi+1>>1;this.lineStarts[mid]<=offset?lo=mid:hi=mid-1}return{offset,line:lo+1,column:offset-this.lineStarts[lo]+1}}span(start,end){return{start:this.pos(start),end:this.pos(end)}}err(start,end,message){this.diagnostics.push(error(this.file,this.span(start,end),message))}warn(start,end,message){this.diagnostics.push(warning(this.file,this.span(start,end),message))}fatal(start,end,message){throw this.err(start,end,message),new Fatal}expr(src,start){let r=parseExpression(src,this.file,this.pos(start));return this.diagnostics.push(...r.diagnostics),r.expr}get top(){return this.frames[this.frames.length-1]}children(){let f=this.top;return f.type===`switch`?[]:f.children}flush(){this.text&&=(/\S/.test(this.text)&&(this.seenOutput=!0),this.children().push({k:`text`,value:this.text,span:this.span(this.textStart,this.i)}),``)}emit(node){this.flush(),node.k!==`let`&&node.k!==`slot-fill`&&(this.seenOutput=!0),this.children().push(node)}addText(chunk,at){this.text||(this.textStart=at),this.text+=chunk}inPre(){return this.frames.some(f=>f.type===`element`&&f.name===`pre`)}standalone(tokenStart,tokenEnd){if(this.inPre())return tokenEnd;let a=tokenStart;for(;a>0&&(this.s[a-1]===` `||this.s[a-1]===` `);)a--;if(a>0&&this.s[a-1]!==`
15
15
  `&&this.s[a-1]!==`\r`)return tokenEnd;let b=tokenEnd;for(;b<this.s.length&&(this.s[b]===` `||this.s[b]===` `);)b++;if(b<this.s.length&&this.s[b]!==`
16
16
  `&&this.s[b]!==`\r`)return tokenEnd;let lead=tokenStart-a;return lead&&this.text.endsWith(this.s.slice(a,tokenStart))&&(this.text=this.text.slice(0,-lead)),this.s[b]===`\r`&&b++,this.s[b]===`
17
17
  `&&b++,b}run(){try{for(;this.i<this.s.length;)this.top.type===`switch`?this.switchBody():this.step();this.flush();for(let f of this.frames)(f.type===`block`||f.type===`switch`)&&this.err(f.start,f.start+1,`@${f.type===`block`?f.keyword:`switch`} is never closed — expected }`)}catch(e){if(!(e instanceof Fatal))throw e}}step(){let s=this.s,i=this.i,c=s[i];if(c===`{`&&s.startsWith(`{{--`,i)){let end=s.indexOf(`--}}`,i+4);end<0&&this.fatal(i,i+4,`template comment is never closed — expected --}}`),this.i=end+4}else if(c===`{`&&s[i+1]===`{`)this.interp(i,`text`);else{if(c===`<`){if(s.startsWith(`<!--`,i))return this.htmlComment(i);if(s[i+1]===`/`&&/[A-Za-z]/.test(s[i+2]??``))return this.closeTag(i);if(/[A-Za-z]/.test(s[i+1]??``))return this.openTag(i);s[i+1]===`{`&&this.fatal(i,i+2,`tag names can't be interpolated`)}if(c===`@`){let m=KEYWORDS.exec(s.slice(i,i+12));if(m&&this.block(m[1],i))return}c===`}`&&this.frames.some(f=>f.type===`block`)?this.closeBlock(i):(this.addText(c,i),this.i++)}}interpSource(i){let at=i+2,j=at,quote=``;for(;j<this.s.length;){let ch=this.s[j];if(quote)ch===`\\`?j++:ch===quote&&(quote=``);else if(ch===`'`||ch===`"`)quote=ch;else if(ch===`}`&&this.s[j+1]===`}`)return{src:this.s.slice(at,j),at,end:j+2};j++}this.fatal(i,i+2,`interpolation is never closed — expected }}`)}interp(i,ctx){let{src,at,end}=this.interpSource(i),expr=this.expr(src,at);expr&&this.emit({k:`interp`,expr,ctx,span:this.span(i,end)}),this.i=end}textWithInterps(from,stop,ctx){let out=[],j=from,start=from;for(;j<stop;)if(this.s.startsWith(`{{--`,j)){j>start&&out.push({k:`text`,value:this.s.slice(start,j),span:this.span(start,j)});let end=this.s.indexOf(`--}}`,j+4);j=start=end<0||end>stop?stop:end+4}else if(this.s.startsWith(`{{`,j)){j>start&&out.push({k:`text`,value:this.s.slice(start,j),span:this.span(start,j)});let{src,at,end}=this.interpSource(j),expr=this.expr(src,at);expr&&out.push({k:`interp`,expr,ctx,span:this.span(j,end)}),j=start=end}else j++;return stop>start&&out.push({k:`text`,value:this.s.slice(start,stop),span:this.span(start,stop)}),out}htmlComment(i){let end=this.s.indexOf(`-->`,i+4);end<0&&this.fatal(i,i+4,`HTML comment is never closed — expected -->`),this.emit({k:`comment`,children:this.textWithInterps(i+4,end,`comment`),span:this.span(i,end+3)}),this.i=end+3}openTag(i){let s=this.s,m=/^<([A-Za-z][\w:.-]*)/.exec(s.slice(i,i+200)),tag=m[1],j=i+m[0].length,parent=this.frames.findLast(f=>f.type===`element`),lower=tag.toLowerCase(),foreign=!!parent?.el.foreign&&!INTEGRATION_POINTS.has(parent.el.tag.toLowerCase())||lower===`svg`||lower===`math`,attrs=[],selfClosing=!1;for(;;){for(;j<s.length&&/\s/.test(s[j]);)j++;if(j>=s.length&&this.fatal(i,i+m[0].length,`<${tag}> is never closed — expected >`),s[j]===`>`){j++;break}if(s[j]===`/`&&s[j+1]===`>`){selfClosing=!0,j+=2;break}j=this.attr(j,attrs)}this.i=j;let name=foreign?tag:lower,el={k:`element`,tag,attrs,children:[],foreign,void:!foreign&&VOID.has(lower),selfClosing,span:this.span(i,j)};if(!foreign&&lower===`slot`){let nameAttr=attrs.find(a=>a.name===`name`),slotName=nameAttr?.kind===`static`?String(nameAttr.parts?.[0]??``):void 0;nameAttr&&nameAttr.kind!==`static`&&this.err(nameAttr.span.start.offset,nameAttr.span.end.offset,`a slot name must be plain text`);let node={k:`slot-outlet`,name:slotName,fallback:[],span:el.span};this.emit(node),selfClosing||this.frames.push({type:`slot`,children:node.fallback})}else if(this.emit(el),!(el.void||selfClosing)){if(RAW_TEXT.has(lower)||!foreign&&RCDATA.has(lower)){let end=RegExp(`</${lower}(?=[\\t\\n\\f\\r />])`,`i`);end.lastIndex=0;let found=end.exec(s.slice(j)),close=found?j+found.index:-1,stop=close<0?s.length:close;if(RAW_TEXT.has(lower)){let content=s.slice(j,stop);foreign&&content.includes(`<`)?this.err(j,stop,`markup isn't allowed inside <${lower}> in SVG or MathML — move the code to a separate file`):(/<!\[CDATA\[/i.test(content)||lower===`script`&&/<!--/.test(content)&&/<script/i.test(content))&&this.err(j,stop,`this <${lower}> content (<!-- with <script, or CDATA) is read differently by browsers — move it to a separate file`)}if(el.children=this.textWithInterps(j,stop,RAW_TEXT.has(lower)?`raw-text`:`rcdata`),close<0){this.i=s.length;return}let gt=s.indexOf(`>`,close);this.i=gt<0?s.length:gt+1}else this.frames.push({type:`element`,el,children:el.children,name})}}attr(j,attrs){let s=this.s,start=j;for(s.startsWith(`{{`,j)&&this.fatal(j,j+2,`attribute names can't be interpolated`);j<s.length&&!/[\s"'>/=]/.test(s[j]);)s.startsWith(`{{`,j)&&this.fatal(j,j+2,`attribute names can't be interpolated`),j++;j===start&&this.fatal(j,j+1,`unexpected '${s[j]}' in a tag`);let raw=s.slice(start,j),k=j;for(;k<s.length&&/[ \t]/.test(s[k]);)k++;if(s[k]!==`=`)return attrs.push(this.classify(raw,void 0,start,j,!1)),j;for(k++;k<s.length&&/\s/.test(s[k]);)k++;let q=s[k];if(q===`"`||q===`'`){let close=s.indexOf(q,k+1);return close<0&&this.fatal(start,k+1,`the value of ${raw} is never closed — expected ${q}`),attrs.push(this.classify(raw,{from:k+1,to:close},start,close+1,!0)),close+1}let e=k;for(;e<s.length&&!/[\s>]/.test(s[e]);)e++;return s.slice(k,e).includes(`{{`)&&this.fatal(k,e,`quote the value of ${raw}: an interpolated attribute must be in quotes`),attrs.push(this.classify(raw,{from:k,to:e},start,e,!1)),e}classify(raw,value,start,end,quoted){let span=this.span(start,end),binding=/^\[(?:(class)\.([\w-]+)|(style)\.([\w-]+)(?:\.(\w+))?|([^\]]+))\]$/.exec(raw);if(binding){value||this.fatal(start,end,`${raw} needs a value: ${raw}="expression"`);let src=this.s.slice(value.from,value.to);src.includes(`{{`)&&this.fatal(value.from,value.to,`${raw} takes an expression, without {{ }}`);let expr=this.expr(src,value.from),[,cls,clsName,sty,styName,unit,bind]=binding;return cls?{name:clsName,kind:`class`,expr,quoted,span}:sty?{name:styName,kind:`style`,expr,unit,quoted,span}:{name:bind,kind:`bind`,expr,quoted,span}}if(!value)return{name:raw,kind:`bare`,quoted:!1,span};let parts=[];for(let n of this.textWithInterps(value.from,value.to,`text`))n.k===`text`?parts.push(n.value):n.k===`interp`&&parts.push(n.expr);return{name:raw,kind:parts.some(p=>typeof p!=`string`)?`interp`:`static`,parts:parts.length?parts:[``],quoted,span}}closeTag(i){let m=/^<\/([A-Za-z][\w:.-]*)\s*>/.exec(this.s.slice(i,i+200));m||this.fatal(i,i+2,`malformed closing tag`),this.i=i+m[0].length;let tag=m[1];for(let f=this.frames.length-1;f>0;f--){let frame=this.frames[f];if(frame.type===`block`||frame.type===`switch`||frame.type===`root`)break;if(frame.type===`slot`?tag.toLowerCase()===`slot`:frame.el.foreign?frame.name===tag:frame.name===tag.toLowerCase()){this.flush(),this.frames.length=f;return}}this.warn(i,this.i,`</${tag}> closes nothing`)}parens(j,what){this.s[j]!==`(`&&this.fatal(j,j+1,`expected ( after ${what}`);let depth=0,quote=``;for(let k=j;k<this.s.length;k++){let ch=this.s[k];if(quote)ch===`\\`?k++:ch===quote&&(quote=``);else if(ch===`'`||ch===`"`)quote=ch;else if(ch===`(`)depth++;else if(ch===`)`&&--depth===0)return{src:this.s.slice(j+1,k),at:j+1,end:k+1}}this.fatal(j,j+1,`${what} ( is never closed — expected )`)}skipWs(j){for(;j<this.s.length&&/\s/.test(this.s[j]);)j++;return j}openBrace(j,what){return j=this.skipWs(j),this.s[j]!==`{`&&this.fatal(j,j+1,`expected { after ${what}`),j+1}splitHeader(src,at){let out=[],quote=``,start=0;for(let k=0;k<src.length;k++){let ch=src[k];quote?ch===`\\`?k++:ch===quote&&(quote=``):ch===`'`||ch===`"`?quote=ch:ch===`;`&&(out.push({src:src.slice(start,k),at:at+start}),start=k+1)}return out.push({src:src.slice(start),at:at+start}),out}stringArg(src,at,what){let m=/^\s*(['"])([^'"]*)\1\s*$/.exec(src);return m||this.fatal(at,at+src.length,`${what} takes a quoted name`),m[2]}block(kw,i){let s=this.s,after=i+1+kw.length,next=this.skipWs(after),ch=s[next];switch(kw){case`if`:case`for`:case`switch`:case`slot`:case`error`:case`layout`:case`assets`:if(ch!==`(`)return!1;break;case`verbatim`:if(ch!==`{`)return!1;break;case`let`:case`input`:if(!/[ \t]/.test(s[after]??``))return!1;break;case`else`:case`empty`:case`case`:case`default`:if(ch!==`{`&&ch!==`(`&&!(kw===`else`&&s.startsWith(`if`,next)))return!1;this.fatal(i,after,`@${kw} has no matching ${kw===`else`?`@if`:kw===`empty`?`@for`:`@switch`}`)}if(kw===`layout`)return this.layout(i,next);if(kw===`input`)return this.input(i,after);if(kw===`let`)return this.let(i,after);if(kw===`verbatim`)return this.verbatim(i,next);if(kw===`assets`){let p=this.parens(next,`@assets`),entry=this.stringArg(p.src,p.at,`@assets`);return this.i=this.standalone(i,p.end),this.emit({k:`assets`,entry,span:this.span(i,p.end)}),!0}let p=this.parens(next,`@${kw}`),brace=this.openBrace(p.end,`@${kw} (…)`),resume=this.standalone(i,brace);if(kw===`if`){let branch=this.ifBranch(p.src,p.at),node={k:`if`,branches:[branch],span:this.span(i,brace)};this.emit(node),this.frames.push({type:`block`,children:branch.body,next:`else`,node,keyword:`if`,start:i})}else if(kw===`for`){let node=this.forHeader(p.src,p.at,i,brace);this.emit(node),this.frames.push({type:`block`,children:node.body,next:`empty`,node,keyword:`for`,start:i})}else if(kw===`error`){let field=this.stringArg(p.src,p.at,`@error`),at=this.span(p.at,p.at+p.src.length),cond={kind:`path`,root:`$errors`,segments:[{computed:{kind:`literal`,value:field,span:at},optional:!0}],span:at},spot=hidden=>({k:`element`,tag:`span`,attrs:[{name:`data-kick-error`,kind:`static`,parts:[field],quoted:!0,span:at},{name:`aria-live`,kind:`static`,parts:[`polite`],quoted:!0,span:at},...hidden?[{name:`hidden`,kind:`bare`,quoted:!1,span:at}]:[]],children:[],foreign:!1,void:!1,selfClosing:!1,span:at}),shown=spot(!1),node={k:`if`,branches:[{cond,alias:`$message`,body:[shown]},{body:[spot(!0)]}],span:this.span(i,brace)};this.emit(node),this.frames.push({type:`block`,children:shown.children,node,keyword:`error`,start:i})}else if(kw===`switch`){let node={k:`switch`,on:this.expr(p.src,p.at)??{kind:`literal`,value:null,span:this.span(p.at,p.at)},cases:[],span:this.span(i,brace)};this.emit(node),this.frames.push({type:`switch`,node,start:i})}else{let node={k:`slot-fill`,name:this.stringArg(p.src,p.at,`@slot`),body:[],span:this.span(i,brace)};this.emit(node),this.frames.push({type:`block`,children:node.body,node,keyword:`slot`,start:i})}return this.i=resume,!0}ifBranch(src,at){let parts=this.splitHeader(src,at),branch={cond:this.expr(parts[0].src,parts[0].at),body:[]};if(parts[1]){let m=/^\s*as\s+([A-Za-z_$][\w$]*)\s*$/.exec(parts[1].src);m?branch.alias=m[1]:this.err(parts[1].at,parts[1].at+parts[1].src.length,"expected `as name` after ;")}return parts.length>2&&this.err(parts[2].at,parts[2].at+1,`unexpected ; in @if`),branch}forHeader(src,at,start,end){let parts=this.splitHeader(src,at),head=parts[0],m=/^(\s*)(?:\[\s*([A-Za-z_$][\w$]*)\s*,\s*([A-Za-z_$][\w$]*)\s*\]|([A-Za-z_$][\w$]*))\s+of\s+/.exec(head.src);m||this.fatal(head.at,head.at+head.src.length,"@for needs `item of items`");let item=m[4]??[m[2],m[3]],ofSrc=head.src.slice(m[0].length),of=this.expr(ofSrc,head.at+m[0].length)??{kind:`literal`,value:null,span:this.span(at,at)},lets={};for(let part of parts.slice(1)){let t=part.src.trim();if(t.startsWith(`track`))this.expr(part.src.replace(/^\s*track\b/,w=>` `.repeat(w.length)),part.at);else if(t.startsWith(`let`)){let body=part.src.replace(/^\s*let\b/,``);for(let decl of body.split(`,`)){let d=/^\s*([A-Za-z_$][\w$]*)\s*=\s*(\$\w+)\s*$/.exec(decl);!d||!LOOP_VARS.has(d[2])?this.err(part.at,part.at+part.src.length,`unknown loop variable in \`${decl.trim()}\` — use ${[...LOOP_VARS].join(`, `)}`):lets[d[1]]=d[2]}}else this.err(part.at,part.at+part.src.length,`expected track or let in @for, found \`${t}\``)}return{k:`for`,item,of,lets,body:[],span:this.span(start,end)}}closeBlock(i){let f=this.frames.length-1;for(;this.frames[f].type!==`block`;)f--;let frame=this.frames[f],s=this.s,j=this.skipWs(i+1),branch,empty=!1,end=i+1;if(frame.next===`else`&&/^@else(?![\w$-])/.test(s.slice(j,j+6))){let k=this.skipWs(j+5);if(s.startsWith(`if`,k)&&/[\s(]/.test(s[k+2]??``)){let p=this.parens(this.skipWs(k+2),`@else if`);branch=this.ifBranch(p.src,p.at),k=p.end}else branch={body:[]};end=this.openBrace(k,`@else`)}else frame.next===`empty`&&/^@empty(?![\w$-])/.test(s.slice(j,j+7))&&(empty=!0,end=this.openBrace(j+6,`@empty`));let resume=this.standalone(i,end);if(this.flush(),this.frames.length=f,this.i=resume,branch){let node=frame.node;node.branches.push(branch),this.frames.push({type:`block`,children:branch.body,next:branch.cond?`else`:void 0,node,keyword:`else`,start:j})}else if(empty){let node=frame.node;node.empty=[],this.frames.push({type:`block`,children:node.empty,node,keyword:`empty`,start:j})}}switchBody(){let s=this.s,j=this.skipWs(this.i),frame=this.top;if(j>=s.length){this.i=j;return}if(s[j]===`}`){this.frames.pop(),this.i=this.standalone(j,j+1);return}let m=/^@(case|default)(?![\w$-])/.exec(s.slice(j,j+9));m||this.fatal(j,j+1,`only @case and @default go inside @switch`);let k=j+m[0].length,value;if(m[1]===`case`){let p=this.parens(this.skipWs(k),`@case`);value=this.expr(p.src,p.at),k=p.end}else frame.node.default&&this.err(j,k,`@switch has two @default blocks`);let brace=this.openBrace(k,`@${m[1]}`),body=[];m[1]===`case`?frame.node.cases.push({value:value??frame.node.on,body}):frame.node.default=body,this.i=this.standalone(j,brace),this.frames.push({type:`block`,children:body,node:frame.node,keyword:m[1],start:j})}verbatim(i,brace){let depth=0,quote=``;for(let k=brace;k<this.s.length;k++){let ch=this.s[k];if(quote)ch===`\\`?k++:ch===quote&&(quote=``);else if(ch===`'`||ch===`"`||ch==="`")quote=ch;else if(ch===`{`)depth++;else if(ch===`}`&&--depth===0){let resume=this.standalone(i,k+1);return this.emit({k:`verbatim`,text:this.s.slice(brace+1,k),span:this.span(i,k+1)}),this.i=resume,!0}}this.fatal(i,brace+1,`@verbatim is never closed — expected }`)}layout(i,at){let p=this.parens(at,`@layout`);return this.seenOutput||this.frames.length>1?this.err(i,p.end,`@layout must come before any output`):this.ast.layout===void 0?(this.ast.layout=/^\s*none\s*$/.test(p.src)?null:this.stringArg(p.src,p.at,`@layout`),this.ast.layoutSpan=this.span(i,p.end)):this.err(i,p.end,`a template has one @layout`),this.i=this.standalone(i,p.end),!0}input(i,after){let eol=this.s.indexOf(`
18
18
  `,after);eol<0&&(eol=this.s.length);let line=this.s.slice(after,eol).replace(/\r$/,``),m=/^\s*([A-Za-z_$][\w$]*)\s*(?:=\s*(.*?))?\s*$/.exec(line);m||this.fatal(i,eol,`@input needs a name: @input name or @input name = value`),this.frames.length>1&&this.err(i,eol,`@input goes at the top of a component, outside any block`);let decl={name:m[1],span:this.span(i,after+line.length)};if(m[2]!==void 0){let at=after+line.indexOf(m[2],m[0].indexOf(`=`)+1),e=this.expr(m[2],at),literal=e?.kind===`literal`?e.value:e?.kind===`unary`&&e.op===`-`&&e.arg.kind===`literal`&&typeof e.arg.value==`number`?-e.arg.value:void 0;e&&literal===void 0?this.err(at,at+m[2].length,`an @input default must be a literal: a string, number, true, false or null`):e&&(decl.default={value:literal,span:e.span})}return this.ast.inputs.some(d=>d.name===decl.name)&&this.err(i,eol,`@input ${decl.name} is declared twice`),this.ast.inputs.push(decl),this.i=this.standalone(i,after+line.length),!0}let(i,after){let quote=``;for(let k=after;k<this.s.length;k++){let ch=this.s[k];if(quote)ch===`\\`?k++:ch===quote&&(quote=``);else if(ch===`'`||ch===`"`)quote=ch;else if(ch===`;`){let src=this.s.slice(after,k),m=/^\s*([A-Za-z_$][\w$]*)\s*=/.exec(src);m||this.fatal(i,k,`@let needs a name: @let name = expression;`);let expr=this.expr(src.slice(m[0].length),after+m[0].length),resume=this.standalone(i,k+1);return expr&&this.emit({k:`let`,name:m[1],expr,span:this.span(i,k+1)}),this.i=resume,!0}}this.fatal(i,after,`@let is never ended — expected ;`)}};function parse(source,file){let p=new TemplateParser(source.charCodeAt(0)===65279?source.slice(1):source,file);return p.run(),{ast:p.ast,diagnostics:p.diagnostics}}var SafeHtml=class{html;constructor(html){this.html=html}toString(){return this.html}};function read(obj,key){if(obj==null)return;let k=typeof key==`number`||typeof key==`symbol`?key:String(key);if(typeof k==`string`&&FORBIDDEN_NAMES.has(k))return;let value=obj[k];return typeof value==`function`?void 0:value}const HTML_ESCAPES={"&":`&amp;`,"<":`&lt;`,">":`&gt;`,'"':`&quot;`,"'":`&#39;`},escapeHtml=s=>s.replace(/[&<>"']/g,c=>HTML_ESCAPES[c]),SAFE_SCHEMES=/* @__PURE__ */ new Set([`http`,`https`,`mailto`,`tel`]);function sanitizeUrl(value){let normalised=value.replace(/[\u0000- \u007F]/g,``),m=/^([a-z][a-z0-9+.-]*):/i.exec(normalised);return m?SAFE_SCHEMES.has(m[1].toLowerCase())?value:`#`:value}function sanitizeSrcset(value){return value.split(`,`).map(candidate=>{let m=/^(\s*)(\S+)([\s\S]*)$/.exec(candidate);return m?`${m[1]}${sanitizeUrl(m[2])}${m[3]}`:candidate}).join(`,`)}const JSON_ESCAPES={"<":`\\u003c`,">":`\\u003e`,"&":`\\u0026`,"\u2028":`\\u2028`,"\u2029":`\\u2029`},escapeJsonForScript=json=>json.replace(/[<>&\u2028\u2029]/g,c=>JSON_ESCAPES[c]);function safeStyleValue(v){return/[;{}\\"'<]|\/\*|url\s*\(|expression\s*\(/i.test(v)?void 0:v}const BUILTIN_PIPES=[`uppercase`,`lowercase`,`titlecase`,`json`,`slice`,`keys`,`entries`,`url`,`raw`,`date`,`number`,`currency`,`percent`],DATE_FORMATS={short:{dateStyle:`short`,timeStyle:`short`},medium:{dateStyle:`medium`,timeStyle:`medium`},long:{dateStyle:`long`,timeStyle:`long`},full:{dateStyle:`full`,timeStyle:`long`},shortDate:{dateStyle:`short`},mediumDate:{dateStyle:`medium`},longDate:{dateStyle:`long`},fullDate:{dateStyle:`full`},shortTime:{timeStyle:`short`},mediumTime:{timeStyle:`medium`}};function digits(info){if(info==null)return{};let m=/^(\d+)\.(\d+)-(\d+)$/.exec(String(info));if(!m)throw Error(`digits must look like '1.0-2', got '${String(info)}'`);return{minimumIntegerDigits:Number(m[1]),minimumFractionDigits:Number(m[2]),maximumFractionDigits:Number(m[3])}}const formatters=/* @__PURE__ */ new Map;function remember(key,f){return formatters.size>=256&&formatters.clear(),formatters.set(key,f),f}function cached(key,make){return formatters.get(key)??remember(key,make())}const toNumber=v=>{if(v==null||v===``)return;let n=Number(v);return Number.isNaN(n)?void 0:n},str=v=>v==null?``:String(v);function builtinPipes(env){return{uppercase:v=>str(v).toUpperCase(),lowercase:v=>str(v).toLowerCase(),titlecase:v=>str(v).replace(/\b\p{L}/gu,c=>c.toUpperCase()),json:v=>JSON.stringify(v,null,2),slice:(v,start,end)=>typeof v==`string`||Array.isArray(v)?v.slice(start,end):v,keys:v=>v instanceof Map?[...v.keys()]:v&&typeof v==`object`?Object.keys(v):[],entries:v=>v instanceof Map?[...v.entries()]:v&&typeof v==`object`?Object.entries(v):[],url:v=>encodeURIComponent(str(v)),raw:v=>v instanceof SafeHtml?v:new SafeHtml(str(v)),date:(v,format=`medium`,timeZone)=>{if(v==null||v===``)return``;let d=v instanceof Date?v:new Date(v);if(Number.isNaN(d.getTime()))return``;let preset=DATE_FORMATS[String(format)];if(!preset)throw Error(`unknown date format '${String(format)}'`);let tz=timeZone??env.timeZone;return cached(`d|${env.locale}|${String(format)}|${tz}`,()=>new Intl.DateTimeFormat(env.locale,{...preset,timeZone:tz})).format(d)},number:(v,info)=>{let n=toNumber(v);return n===void 0?``:cached(`n|${env.locale}|${String(info)}`,()=>new Intl.NumberFormat(env.locale,digits(info))).format(n)},currency:(v,code,info)=>{let n=toNumber(v);return n===void 0?``:cached(`c|${env.locale}|${String(code)}|${String(info)}`,()=>new Intl.NumberFormat(env.locale,{style:`currency`,currency:String(code??`USD`),...digits(info)})).format(n)},percent:(v,info)=>{let n=toNumber(v);return n===void 0?``:cached(`p|${env.locale}|${String(info)}`,()=>new Intl.NumberFormat(env.locale,{style:`percent`,...digits(info)})).format(n)}}}function topLevelTokens(src){let out=[],depth=0,i=0;for(;i<src.length;){let c=src[i];if(c===`/`&&src[i+1]===`/`){let nl=src.indexOf(`
19
19
  `,i);i=nl<0?src.length:nl}else if(c===`/`&&src[i+1]===`*`){let end=src.indexOf(`*/`,i+2);i=end<0?src.length:end+2}else if(c===`'`||c===`"`||c==="`"){for(i++;i<src.length&&src[i]!==c;)i+=src[i]===`\\`?2:1;i++}else if(c===`{`||c===`(`||c===`[`)depth===0&&out.push({v:c,start:i}),depth++,i++;else if(c===`}`||c===`)`||c===`]`)depth--,depth===0&&out.push({v:c,start:i}),i++;else if(depth===0&&/[A-Za-z_$*]/.test(c)){let m=/^[A-Za-z_$][\w$]*|^\*/.exec(src.slice(i));out.push({v:m[0],start:i}),i+=m[0].length}else depth===0&&(c===`,`||c===`;`||c===`=`)&&out.push({v:c,start:i}),i++}return out}function braceNames(src,open){let close=src.indexOf(`}`,open),body=src.slice(open+1,close<0?src.length:close),names=[],offset=open+1;for(let part of body.split(`,`)){let m=/^(\s*)([A-Za-z_$][\w$]*)(?:\s+as\s+([A-Za-z_$][\w$]*))?\s*$/.exec(part);m&&names.push({local:m[2],exported:m[3]??m[2],at:offset+m[1].length}),offset+=part.length+1}return{names,end:close<0?src.length:close+1}}function readPipeExports(source,file){let names=[],diagnostics=[],fail=(at,len,message)=>diagnostics.push(error(file,{start:positionAt(source,at),end:positionAt(source,at+len)},message)),add=(name,at)=>{BUILTIN_PIPES.includes(name)?fail(at,name.length,`'${name}' is a built-in pipe — choose another name`):names.push(name)},tokens=topLevelTokens(source);for(let t=0;t<tokens.length;t++){if(tokens[t].v!==`export`)continue;let at=tokens[t].start,next=tokens[t+1],after=tokens[t+2];switch(next?.v){case`const`:case`let`:case`var`:after&&/^[A-Za-z_$]/.test(after.v)?add(after.v,after.start):fail(at,6,`export one pipe per name: export const name = …`);break;case`function`:after&&/^[A-Za-z_$]/.test(after.v)&&add(after.v,after.start);break;case`async`:fail(at,6,`pipes must be synchronous — an async function returns a Promise`);break;case`{`:{let{names:list,end}=braceNames(source,next.start);if(tokens.find(x=>x.start>=end)?.v===`from`){fail(at,6,`re-exports aren't read — import the pipe and export it from this file`);break}for(let n of list)add(n.exported,n.at);break}case`type`:case`interface`:break;case`*`:fail(at,6,`export * isn't read — export each pipe by name`);break;case`default`:fail(at,6,`a default export is not a pipe — export each pipe by name`);break;default:fail(at,6,`export ${next?.v??``} is not a pipe — export const or function`)}}return{names,diagnostics}}export{positionAt as C,emptyFormState as D,FORM_GLOBALS as E,rememberPage as O,error as S,AST_FORMAT_VERSION as T,templateName as _,escapeHtml as a,decodeEntities as b,safeStyleValue as c,parse as d,FORBIDDEN_NAMES as f,layoutResolver as g,componentTag as h,SafeHtml as i,takeFormState as k,sanitizeSrcset as l,check as m,BUILTIN_PIPES as n,escapeJsonForScript as o,parseExpression as p,builtinPipes as r,read as s,readPipeExports as t,sanitizeUrl as u,URL_ATTRS as v,warning as w,VALIDATORS as x,isUrlAttr as y};
20
- //# sourceMappingURL=read-exports-DhS-hmBe.js.map
20
+ //# sourceMappingURL=read-exports-CvEZWKzJ.js.map