@muten/core 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/engine/ir/print.js +27 -0
- package/dist/engine/ir/validate.js +1 -1
- package/dist/lint.js +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import{Ek as $,StOp as o,Nt as y,Mod as c}from"#engine/shared/vocab.js";const a=" ",B=t=>!!t&&typeof t=="object"&&"kind"in t&&t.kind===$.Interp,A=t=>typeof t=="object"&&"$param"in t,O=t=>/^[A-Za-z_][A-Za-z0-9_.]*$/.test(t);function s(t){switch(t.kind){case $.Lit:return j(t.value);case $.Ref:return t.name;case $.Un:return`${t.op} ${f(t.operand)}`;case $.Bin:return`${f(t.left)} ${t.op} ${f(t.right)}`;case $.Tern:return`${f(t.cond)} ? ${f(t.then)} : ${f(t.else)}`}}const f=t=>t.kind===$.Bin||t.kind===$.Tern?`(${s(t)})`:s(t),j=t=>typeof t=="string"?JSON.stringify(t):String(t);function l(t){return t===null?"null":Array.isArray(t)?`[${t.map(l).join(", ")}]`:typeof t=="object"?`{ ${Object.entries(t).map(([r,e])=>`${r}: ${l(e)}`).join(", ")} }`:j(t)}const S=t=>'"'+t.parts.map(r=>typeof r=="string"?r:`{${s(r)}}`).join("")+'"',R=t=>typeof t=="string"?t:t.parts.map(r=>typeof r=="string"?r:`{${s(r)}}`).join("");function I(t){return typeof t=="string"?JSON.stringify(t):A(t)?"$"+t.$param:S(t)}const E=t=>typeof t=="number"?String(t):typeof t=="string"?t:"$"+t.$param,h=t=>Object.entries(t).map(([r,e])=>`${r}: ${E(e)}`).join(", "),N=t=>typeof t=="string"?O(t)?t:JSON.stringify(t):`${O(t.name)?t.name:JSON.stringify(t.name)} when ${s(t.cond)}`;function m(t,r){switch(t.op){case o.Push:return`${t.target}.push(${s(t.arg)})`;case o.Set:return`${t.target}.set(${s(t.arg)})`;case o.Reset:return`${t.target}.reset()`;case o.Remove:return`${t.target}.remove(${t.param} => ${s(t.pred)})`;case o.Create:return`${t.target}.create(${s(t.arg)})`;case o.Update:return`${t.target}.update(${s(t.arg)})`;case o.Delete:return`${t.target}.delete(${s(t.arg)})`;case o.Refetch:return`${t.target}.refetch(${Object.entries(t.params).map(([e,n])=>`${e}: ${s(n)}`).join(", ")})`;case o.Request:return`${t.method.toLowerCase()} ${typeof t.url=="string"?JSON.stringify(t.url):S(t.url)}${t.body?` body ${s(t.body)}`:""}`;case o.If:{const e=t.then.map(i=>r+a+m(i,r+a)).join(`
|
|
2
|
+
`),n=t.else?` else {
|
|
3
|
+
${t.else.map(i=>r+a+m(i,r+a)).join(`
|
|
4
|
+
`)}
|
|
5
|
+
${r}}`:"";return`if ${s(t.cond)} {
|
|
6
|
+
${e}
|
|
7
|
+
${r}}${n}`}}}function d(t,r){if(t.args)return`${r}${t.type}(${h(t.args)})`;if(t.type===y.Slot)return`${r}slot`;const e=t.props||{};let n;if(t.type===y.When)n=`when ${s(e.cond)}`;else if(t.type===y.Each)n=`each ${s(e.list)} as ${e.as}`;else{n=t.type;const i=e.value??e.label??e.src??e.placeholder??e.submitLabel;i!==void 0&&(n+=` ${I(i)}`),e.to!==void 0?n+=` -> ${R(e.to)}`:e.action&&(n+=` -> ${e.action}${e.arg!==void 0?`(${s(e.arg)})`:""}`),e.data&&(n+=` @${e.data}`),e.bind&&(n+=` bind ${e.bind.includes(".")?e.bind:"@"+e.bind}`),e.where&&(n+=` ${c.Where}(${e.where.join(", ")})`),e.columns&&(n+=` ${c.Columns}(${e.columns.join(", ")})`),e.style&&(n+=` ${c.Style}(${e.style.join(", ")})`),e.class&&(n+=` ${c.Class}(${e.class.map(N).join(", ")})`),e.alt!==void 0&&(n+=` ${c.Alt} ${I(e.alt)}`),e.inputs&&(n+=` ${c.Inputs}(${h(e.inputs)})`),e.on&&(n+=` ${c.On}(${h(e.on)})`),e.submit&&(n+=` ${c.Submit} ${e.submit}`)}return t.children&&t.children.length?`${r}${n} {
|
|
8
|
+
${t.children.map(i=>d(i,r+a)).join(`
|
|
9
|
+
`)}
|
|
10
|
+
${r}}`:`${r}${n}`}const C=(t,r,e)=>{const n=Object.entries(r).map(([i,k])=>{let g=`${i} ${k}`;const u=e?.[i];return u?.required&&(g+=" required"),u?.min!==void 0&&(g+=` min:${u.min}`),u?.max!==void 0&&(g+=` max:${u.max}`),g});return`entity ${t} { ${n.join(" ")} }`},P=(t,r)=>{const e=r.source?.startsWith("query:")?`query ${r.source.slice(6)}`:l(r.initial??null);return`${t} = ${e} : ${r.type}`},V=(t,r)=>`${`action ${t}${r.mutates.length?` mutates ${r.mutates.join(", ")}`:""}${r.input?` <- ${r.input}`:""}`} {
|
|
11
|
+
${r.body.map(n=>a+m(n,a)).join(`
|
|
12
|
+
`)}
|
|
13
|
+
}`,x=t=>`${t.url} -> ${t.page}${t.guard?` guard ${t.guardNeg?"not ":""}${t.guard}${t.redirect?` else ${t.redirect}`:""}`:""}`,w=(t,r)=>`part ${t}(${r.params.map(e=>`${e.name}: ${e.type}`).join(", ")}) {
|
|
14
|
+
${d(r.tree,a)}
|
|
15
|
+
}`,p=(t,r)=>`${t} {
|
|
16
|
+
${r}
|
|
17
|
+
}`,b=(t,r,e)=>p(t,r.map(([n,i])=>`${a}${n}${e} ${l(i)}`).join(`
|
|
18
|
+
`));function M(t){const r=[],e=n=>{n&&r.push(n)};if(t.consts)for(const[n,i]of Object.entries(t.consts))e(`const ${n} = ${j(i)}`);if(e(t.screen?`screen ${t.screen}`:void 0),t.meta&&e(p("meta",Object.entries(t.meta).map(([n,i])=>`${a}${n} ${JSON.stringify(i)}`).join(`
|
|
19
|
+
`))),t.params)for(const n of t.params)e(`param ${n}`);if(t.api&&e(b("api",Object.entries(t.api),":")),t.entities)for(const[n,i]of Object.entries(t.entities))e(C(n,i,t.constraints?.[n]));if(t.state&&Object.keys(t.state).length&&e(p("state",Object.entries(t.state).map(([n,i])=>a+P(n,i)).join(`
|
|
20
|
+
`))),t.store&&Object.keys(t.store).length&&e(p("store",Object.entries(t.store).map(([n,i])=>a+P(n,i)).join(`
|
|
21
|
+
`))),t.gets)for(const[n,i]of Object.entries(t.gets))e(`get ${n} = ${s(i)}`);if(t.sources&&e(b("sources",Object.entries(t.sources),":")),t.mock&&e(b("mock",Object.entries(t.mock),":")),t.actions)for(const[n,i]of Object.entries(t.actions))e(V(n,i));if(t.effects)for(const n of t.effects)e(p("effect",n.map(i=>a+m(i,a)).join(`
|
|
22
|
+
`)));if(t.parts)for(const[n,i]of Object.entries(t.parts))e(w(n,i));return t.shell&&e(p("shell",(t.shell.children||[]).map(n=>d(n,a)).join(`
|
|
23
|
+
`))),t.tree&&e(d(t.tree,"")),t.routes&&e(p("routes",t.routes.map(n=>a+x(n)).join(`
|
|
24
|
+
`))),r.join(`
|
|
25
|
+
|
|
26
|
+
`)+`
|
|
27
|
+
`}export{M as print};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveToken as
|
|
1
|
+
import{resolveToken as F,SUGGESTED as I,defaultTheme as q,isKnownTokenShape as _}from"#engine/style/tokens.js";import{diag as a,closest as p}from"#engine/shared/diagnostics.js";import{PRIMITIVE_NAMES as C,ACTION_OPS as K,PRIMITIVES as U}from"#engine/lang/manifest.js";import{Nt as g,Ek as y,StOp as v}from"#engine/shared/vocab.js";const T=new Set([...C,g.Shell]),G=["bind","data"],M=new Set(K),R=["text","number","bool","uuid","email","string"];function d(i,c=[]){return i.kind===y.Ref?c.push(i.name):i.kind===y.Un?d(i.operand,c):i.kind===y.Bin?(d(i.left,c),d(i.right,c)):i.kind===y.Tern&&(d(i.cond,c),d(i.then,c),d(i.else,c)),c}function H(i,c={}){const r=[],h=new Set(Object.keys(i.state||{})),P=new Set(c.stores||[]),V=new Set(Object.keys(i.consts||{})),x=new Set(i.params||[]),k=new Set(Object.keys(i.actions||{})),A=i.nodes||{},w=new Map;for(const[s,n]of Object.entries(i.state||{})){if(!n.source?.startsWith("query:"))continue;const t=new Set(["loading","error","data"]),e=i.entities?.[n.type];if(e){t.add("id");for(const o of Object.keys(e))t.add(o)}w.set(s,t)}const b=new Map;for(const[s,n]of Object.entries(c.storeMembers||{}))b.set(s,new Set(n));const $=(s,n,t)=>{const e=w.get(s);if(e){e.has(n)||r.push(a("unknown-member",`"${n}" is not a member of query "${s}"`,{loc:t.loc,suggestion:p(n,[...e])}));return}const o=b.get(s);o&&!o.has(n)&&r.push(a("unknown-member",`"${n}" is not a member of store "${s}"`,{loc:t.loc,suggestion:p(n,[...o])}))},O=Object.keys(i.entities||{});for(const[s,n]of Object.entries(i.state||{})){const t=n.type;if(t==="list")r.push(a("untyped-list",`state "${s}" is an untyped "list" \u2014 declare the element type, e.g. list<uuid> or list<User>`,{loc:n.loc,suggestion:"list<uuid>"}));else if(t.startsWith("list<")){const e=t.slice(5,-1);!R.includes(e)&&!O.includes(e)&&r.push(a("unknown-type",`list element "${e}" is not a known entity or scalar type`,{loc:n.loc,suggestion:p(e,[...O,...R])}))}}const W=(s,n)=>{if(typeof s=="string"&&s.startsWith("@")){const t=s.slice(1).split(".")[0];if(!h.has(t)){const e=p(t,[...h]);r.push(a("unknown-ref",`"@${t}" is not a declared state`,{loc:n.loc,suggestion:e?"@"+e:null}))}}},N=(s,n)=>{if(!(!s||s.startsWith("$"))){if(s.includes(".")){const t=s.indexOf(".");$(s.slice(0,t),s.slice(t+1).split(".")[0],n);return}k.has(s)||r.push(a("unknown-action",`"${s}" is not a declared action`,{loc:n.loc,suggestion:p(s,[...k])}))}},S=(s,n,t)=>{for(const e of d(s)){const o=e.indexOf("."),f=o===-1?e:e.slice(0,o);if(!(t.has(f)||h.has(f)||P.has(f)||V.has(f)||x.has(f)||k.has(f))){r.push(a("unknown-ref",`"${f}" is not a known state or item variable here`,{loc:n.loc,suggestion:p(f,[...h,...t])}));continue}o!==-1&&$(f,e.slice(o+1).split(".")[0],n)}},j=new Set,E=(s,n)=>{const t=A[s];if(!t){r.push(a("missing-node",`node ${s} does not exist`));return}if(j.has(s)){r.push(a("dup-node",`${s} is referenced twice`,{loc:t.loc}));return}if(j.add(s),!T.has(t.type))t.args?r.push(a("unknown-part",`"${t.type}" is not a known part`,{loc:t.loc,suggestion:p(t.type,c.parts||[])})):r.push(a("unknown-type",`"${t.type}" is not a known primitive`,{loc:t.loc,suggestion:p(t.type,[...T])}));else{const l=U[t.type],m=l?l.props:{};for(const[u,D]of Object.entries(m))!D.endsWith("?")&&!(u in(t.props||{}))&&r.push(a("missing-prop",`${t.type} is missing the required "${u}"`,{loc:t.loc}))}const e=t.props||{};for(const l of G)l in e&&W(e[l],t);if(e.action&&N(e.action,t),e.submit&&N(e.submit,t),Array.isArray(e.style)){const l=c.theme||q,m=Object.keys(l.space||{}).length>0;for(const u of e.style)_(u)?m&&F(u,l)===null&&r.push(a("unknown-token",`"${u}": that step isn't in your theme scale`,{loc:t.loc,suggestion:p(u,I)})):r.push(a("unknown-token",`"${u}" is not an accepted style token`,{loc:t.loc,suggestion:p(u,I)}))}t.type===g.When&&e.cond&&S(e.cond,t,n),t.type===g.Each&&e.list&&S(e.list,t,n);const o=[];(t.type===g.Text||t.type===g.Title||t.type===g.Span)&&e.value&&o.push(e.value),t.type===g.Image&&(e.src&&o.push(e.src),e.alt&&o.push(e.alt)),t.type===g.Link&&e.to&&o.push(e.to),e.label&&o.push(e.label);for(const l of o)if(typeof l=="object"&&"kind"in l&&l.kind===y.Interp)for(const m of l.parts)typeof m!="string"&&S(m,t,n);const f=t.type===g.Each&&e.as?new Set([...n,e.as]):n;for(const l of t.children||[])E(l,f)};if(i.rootId?E(i.rootId,new Set):c.kind!=="store"&&r.push(a("no-root","the doc is missing a rootId")),c.kind==="store")for(const[s,n]of Object.entries(i.gets||{}))for(const t of d(n)){const e=t.split(".")[0];h.has(e)||r.push(a("unknown-ref",`get "${s}": "${e}" is not a state of this store`,{suggestion:p(e,[...h])}))}for(const[s,n]of Object.entries(i.actions||{})){const t=new Set(n.mutates||[]),e=o=>{if(o.op===v.If){for(const f of o.then||[])e(f);for(const f of o.else||[])e(f);return}M.has(o.op)||r.push(a("unknown-op",`action "${s}" uses unknown op "${o.op}"`,{suggestion:p(o.op,[...M])})),"target"in o&&o.target&&!t.has(o.target)&&r.push(a("undeclared-mutation",`action "${s}" mutates "${o.target}" but only declares mutates(${[...t].join(", ")||"\u2205"})`,{suggestion:p(o.target,[...t])}))};for(const o of n.body||[])e(o)}return{ok:r.length===0,diagnostics:r}}export{H as validate};
|
package/dist/lint.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{join as
|
|
2
|
-
\u2716 ${
|
|
1
|
+
import{join as l,relative as b}from"node:path";import{readFileSync as P,existsSync as O}from"node:fs";import{readRoutes as j}from"#engine/project/routes.js";import{load as D,loadParts as k,findStores as w}from"#engine/project/load.js";import{parse as S}from"#engine/lang/parse.js";import{toDoc as v}from"#engine/ir/flatten.js";import{validate as m}from"#engine/ir/validate.js";import{formatDiagnostic as p,ParseError as d}from"#engine/shared/diagnostics.js";async function $(r,c=!1){const i=s=>b(r,s),h=await k(l(r,"src","parts")),f=w(l(r,"src")),g=Object.keys(f),a={};for(const[s,e]of Object.entries(f))a[s]=[...Object.keys(e.state||{}),...Object.keys(e.gets||{}),...Object.keys(e.actions||{})];const y=j(r),o=[];for(const s of y){let e=[];try{const{doc:t,partNames:u}=await D(s.screenPath,h);e=m(t,{parts:u,stores:g,storeMembers:a}).diagnostics}catch(t){if(!(t instanceof d))throw t;e=[{code:t.code,severity:"error",message:t.message,loc:t.loc,suggestion:null}]}for(const t of e)c||console.log(p(t,i(s.screenPath))),o.push({file:i(s.screenPath),...t})}const n=l(r,"src","app.muten");if(O(n))try{const s=S(P(n,"utf8"));if(s.shell)for(const e of m(v({...s,tree:s.shell}),{stores:g,storeMembers:a}).diagnostics)c||console.log(p(e,i(n))),o.push({file:i(n),...e})}catch(s){if(!(s instanceof d))throw s}return console.log(c?JSON.stringify(o,null,2):o.length?`
|
|
2
|
+
\u2716 ${o.length} problem(s)`:"\u2713 no problems"),o.length}export{$ as lintApp};
|