diff --git a/app/srv/api/nova-load.ts b/app/srv/api/nova-load.ts index d56ae88b..1ce9d7b8 100644 --- a/app/srv/api/nova-load.ts +++ b/app/srv/api/nova-load.ts @@ -1,5 +1,5 @@ import { apiContext } from "../../../pkgs/core/server/api/api-ctx"; -import { code } from "../ws/sync/editor/code/util-code"; +import { code } from "../ws/sync/code/code"; export const _ = { url: "/nova-load/:mode/:id_site/**", diff --git a/app/srv/api/prod-zip.ts b/app/srv/api/prod-zip.ts index d32b7740..0c2b0aa7 100644 --- a/app/srv/api/prod-zip.ts +++ b/app/srv/api/prod-zip.ts @@ -1,11 +1,11 @@ import { apiContext } from "service-srv"; -import { code } from "../ws/sync/editor/code/util-code"; import fs from "fs"; import path from "path"; import { gzipAsync } from "../ws/sync/entity/zlib"; import { validate } from "uuid"; import { dir } from "dir"; import { existsAsync, readAsync } from "fs-jetpack"; +import { code } from "../ws/sync/code/code"; export const _ = { url: "/prod-zip/:site_id", diff --git a/app/srv/api/prod.ts b/app/srv/api/prod.ts index a896896f..2a6d4604 100644 --- a/app/srv/api/prod.ts +++ b/app/srv/api/prod.ts @@ -2,8 +2,8 @@ import { dir } from "dir"; import { apiContext } from "service-srv"; import { validate } from "uuid"; import { prodIndex } from "../util/prod-index"; -import { code } from "../ws/sync/editor/code/util-code"; import { gzipAsync } from "../ws/sync/entity/zlib"; +import { code } from "../ws/sync/code/code"; export const _ = { url: "/prod/:site_id/**", @@ -20,6 +20,8 @@ export const _ = { if (!validate(site_id)) return new Response("site not found", { status: 403 }); + code; + if (pathname.startsWith("_prasi")) { const action = pathname.split("/")[1]; diff --git a/app/srv/core/main.js b/app/srv/core/main.js index b8e05bab..e6b154be 100644 --- a/app/srv/core/main.js +++ b/app/srv/core/main.js @@ -95,7 +95,11 @@ Error generating stack: `+o.message+` ${uy(e,t.mode)} ${Kg(e,t.mode)} ${dy(e,t.mode)} +<<<<<<< HEAD `,(t?.hover||t?.active)&&fy({item:e,hover:t?.hover,active:t?.active}),Gg(e,t.mode)])}catch(n){console.log(n)}return cx([])};var hy=A(Z()),Eo=(e,t,n,r,i)=>{let o=t.item,l;o.component?.style&&(l={style:o.component.style,className:Ef(o.component.style,{mode:e.mode})});let s={className:Ef(o,{mode:e.mode}),inherit:l},c={},a=t.item.childs,u;return t.item.type==="text"?(u=null,s.dangerouslySetInnerHTML={__html:t.item.html||""}):u=Array.isArray(a)&&a?.map(d=>{if(!d)return null;let{id:f}=d,p=n?e.layout?.meta[f]:e.meta[f];return p?(0,hy.jsx)(qt,{meta:p,is_layout:n,passprop:{...r},depth:i+1},f):null}),s.children=u,t.item.adv?.html?(delete s.children,s.dangerouslySetInnerHTML={__html:t.item.adv?.html}):t.item.adv?.js&&!t.item.adv.js.includes("children")&&(delete s.children,delete s.dangerouslySetInnerHTML),{props:s,text_props:c}};var My=A(ge());var Ay=A(Rf());var Jr={NORMAL:0,WILDCARD:1,PLACEHOLDER:2};function Us(e={}){let t={options:e,rootNode:by(),staticRoutesMap:{}},n=r=>e.strictTrailingSlash?r:r.replace(/\/$/,"")||"/";if(e.routes)for(let r in e.routes)Ey(t,n(r),e.routes[r]);return{ctx:t,lookup:r=>i2(t,n(r)),insert:(r,i)=>Ey(t,n(r),i),remove:r=>o2(t,n(r))}}function i2(e,t){let n=e.staticRoutesMap[t];if(n)return n.data;let r=t.split("/"),i={},o=!1,l=null,s=e.rootNode,c=null;for(let a=0;a{},db:null,api:null,init_local_effect:{}},page:{history:{id:"",show:!1},root_id:"root",cur:a2,doc:null,list:{},building:!1,meta:{},entry:[],tree:[],render:()=>{}},comp:{doc:null,item:null,loaded:{},list:{},group:{}},code:{},global_prop:[],ui:{comp_editable:localStorage.getItem("prasi-comp-editable")==="yes",zoom:localStorage.zoom||"100%",side:{prop:!0},layout:{left:parseInt(localStorage.getItem("prasi-layout-left")||"250"),right:parseInt(localStorage.getItem("prasi-layout-right")||"250")},prevent_indent_hook:!1,syncing:!1,tree:{item_loading:[],search:"",search_ref:null,search_mode:{Name:!0,JS:!1,HTML:!1,CSS:!1},rename_id:"",open:{}},popup:{file:{enabled:!1,open:!1,picker:{value:"",on_pick:!1,multi:!1},path:"/",expanded:JSON.parse(localStorage.getItem("panel-file-expanded")||"{}"),entry:{},selected:new Set,action:null,file_renaming:"",file_ctx_menu_event:null,tree:[],tree_renaming:"",tree_ctx_path:"",tree_ctx_menu_event:null,preview:!0,upload:{started:!1,progress:{}}},code:{init:!1,open:!1,name:"site",log:"",loading:!1,startup_status:"init",error:!1,show_log:!1,list:{}},page:{open:null,form:null},script:{open:!1,mode:"js",lastMode:"js",type:"item",prop_kind:"",prop_name:"",on_close:()=>{},typings:{status:"ok",err_msg:""},wb_render:()=>{}},site:null,site_form:null,comp:{preview_id:"",open:null,import:!1},comp_group:null,api:{open:!1}}}};var Bs=e=>({isMobile:e.mode==="mobile",isDesktop:e.mode==="desktop",isEditor:location.pathname.startsWith("/ed/")});var ur=A(ge());var at=window;var u2=pf("prasi-cache","prasi-cache-store"),Co={timeout:null,store:u2},$s=(e,t)=>{let n=[...Ry(t,"navigate(",")"),...Ry(t,"href = ",";")],r=e.page.cur.id;e.page.navs[r]||(e.page.navs[r]=new Set);for(let i of n)e.page.navs[r].add(i);clearTimeout(Co.timeout),Co.timeout=setTimeout(()=>{e.on_nav_loaded&&e.on_nav_loaded({urls:Array.from(e.page.navs[r])})},100)},Ry=(e,t,n)=>{let r=0,i=0,o=[];for(;;){let l=e.indexOf(t,r);if(i=r,l>=0){let s=e[l+t.length];if(s==='"'||s==="'"||s==="`"){let c=e.indexOf(`${s}${n}`,l+t.length+1),a=e.substring(l+t.length+1,c);r=c+2+n.length,o.push(a)}}if(i===r)break}return o};var ar=A(ge());var Ty=A(Sf()),sr=A(ge());var Cf=A(Z()),Ny=(e,t,n,r)=>i=>{let[o,l]=(0,sr.useState)({}),s=i.internal_key;n.item.script||(n.item.script={});let c=n.item.script;s&&(n.item.script_keyed||(n.item.script_keyed={}),n.item.script_keyed[s]||(n.item.script_keyed[s]={}),c=n.item.script_keyed[s]),c.passprop||(c.passprop={});let a={};if(c.passprop){let f=!1;for(let[p,g]of Object.entries(i))["children","key"].includes(p)||(f=!0,a[p]=g,c.passprop[p]={end:0,start:0,value:g})}let u={...r,...a};if(!Array.isArray(i.children)&&(0,sr.isValidElement)(i.children)&&typeof i.children=="object"){let f=(0,Ty.default)(i.children,"props.meta.item.component.props.child.content.childs");if(Array.isArray(f)){let p=!0;for(let g of f)!(0,sr.isValidElement)(g)&&typeof g=="object"||(p=!1);if(p)return f.map(g=>{let _=e.meta[g.id];if(_||(e.meta[g.id]={item:g},_=e.meta[g.id]),_){if(Object.keys(_.item).length<=3&&_.mitem){let x={..._.item},h=_.mitem.toJSON();_.item={...h,...x}}return(0,Cf.jsx)(qt,{is_layout:t,meta:_,passprop:u,parent_key:i.internal_key},g.id)}return null})}}if(!Array.isArray(i.children)&&!(0,sr.isValidElement)(i.children)&&typeof i.children=="object"){let f=i.children.id;if(f){let p=e.meta[f];if(!p){e.meta[f]={item:i.children},p=e.meta[f];let g=p.item.component?.id;g&&e.comp.load(g).then(_=>{if(_){for(let[h,m]of Object.entries(_)){let y=p.item;y[h]||(y[h]=m)}let x=p.item.component?.props;for(let[h,m]of Object.entries(_.component?.props||{}))x[h]||(x[h]=m);l({})}})}return(0,Cf.jsx)(qt,{is_layout:t,meta:p,passprop:u,parent_key:i.internal_key})}}return Tf(i,u,s?{parent_key:s}:void 0)},Tf=(e,t,n)=>{let r={};if(Array.isArray(e)?r.children=e:r=e,Array.isArray(r.children)){let i=[];for(let o of r.children)i.push(Cy(o,r,t,n));return i}return Cy(r.children,r,t,n)},Cy=(e,t,n,r)=>{if((0,sr.isValidElement)(e)){let i={...t};return delete i.children,{...e,props:{...e.props,...r,passprop:{...n,...i}}}}return e};var ti={},Iy=(e,t,n)=>r=>{let i=["localhost","prasi.avolut.com"].includes(location.hostname)&&location.pathname.startsWith("/ed/"),o=n.item.id,{children:l,parent_key:s}=r,c=e.script?.init_local_effect,a=t?e.layout?.meta:e.meta,u=(0,ar.useRef)(ti[o]?ti[o]:r.value),[d,f]=(0,ar.useState)({}),p=u.current;return p.render=()=>{window.prasiContext?.render?window.prasiContext?.render():f({})},Hs(e,n,n.script?.scope,s),r.hook&&r.hook(p),(0,ar.useEffect)(()=>{if(n.parent?.instance_id&&a){let _=a[n.parent?.instance_id];if(_&&_.instances){for(let[x,h]of Object.entries(_.instances[n.parent.instance_id]))if(h===n.item.id){o=x;break}}}return!c[o]&&(typeof c=="object"&&(c[o]=!0),(async()=>r.effect&&(await r.effect(p),i&&(ti[o]=p)))()),()=>{}},[...r.deps||[],location.pathname]),(0,ar.useEffect)(()=>{i&&ti[o]===null&&(async()=>r.effect&&(await r.effect(p),i&&(ti[o]=p)))()},[ti[o]]),Tf(l,{...n.script?.scope,[r.name]:p})};var Nf=A(Z()),Oy=(e,t,n,r,i,o)=>{let l=Eo(e,t,n,r,i);e.visit&&e.visit(t,l),t.script?t.script.scope=r:t.script={scope:r,result:null,Local:Iy(e,n,t),PassProp:Ny(e,n,t,r)};let s=t.script,c=window.exports,a={useEffect:ur.useEffect,children:l.props.children,props:l.props,Local:s?.Local,db:e.site.db,api:e.site.api,PassProp:s?.PassProp,ErrorBox:Nt,newElement:()=>{},render:f=>{let p=f;if((0,ur.isValidElement)(f)&&f.props.children){let g=x=>{let h=!1,m=[];if((0,ur.isValidElement)(x)){if(x.type===t.script?.PassProp)return{should_replace:!0,el:{...x,props:{...x.props,internal_key:x.key}}};if(Array.isArray(x.props?.children))for(let y of x.props?.children)if(Array.isArray(y)){let v=[],S=!1;for(let R of y){let k=g(R);k.should_replace?(v.push(k.el),S=!0):v.push(R)}S?(h=!0,m.push(v)):m.push(y)}else typeof y=="object"?y.type===t.script?.PassProp&&(h=!0,m.push({...y,props:{...y.props,internal_key:y.props.key}})):m.push(y)}return{should_replace:h,el:{...x,props:{...x.props,children:m}}}},_=g(f);_.should_replace&&(p=_.el)}s&&(s.result=(0,Nf.jsx)(ur.Suspense,{children:p}))},params,...Bs(e),...c,...r,_meta:e.meta,_item:t.item,_syncm:typeof syncronize<"u"?syncronize:void 0};if(typeof r=="object"){for(let[f,p]of Object.entries(r))if(typeof p=="object"&&p&&p._jsx){let g=p;a[f]=(0,Nf.jsx)(c2,{fn:g.fn,passprop:{...r},meta:t})}}!at.isEditor&&t.item.adv?.js&&$s(e,t.item.adv.js);let u=t.item.adv?.jsBuilt||"",d=Of(u,If)||"";try{new Function(...Object.keys(a),"___js",`// ${t.item.name}: ${t.item.id} +======= + `,(t?.hover||t?.active)&&fy({item:e,hover:t?.hover,active:t?.active}),Gg(e,t.mode)])}catch(n){console.log(n)}return cx([])};var hy=A(Z()),Eo=(e,t,n,r,i)=>{let o=t.item,l;o.component?.style&&(l={style:o.component.style,className:Ef(o.component.style,{mode:e.mode})});let s={className:Ef(o,{mode:e.mode}),inherit:l},c={},a=t.item.childs,u;return t.item.type==="text"?(u=null,s.dangerouslySetInnerHTML={__html:t.item.html||""}):u=Array.isArray(a)&&a?.map(d=>{if(!d)return null;let{id:f}=d,p=n?e.layout?.meta[f]:e.meta[f];return p?(0,hy.jsx)(qt,{meta:p,is_layout:n,passprop:{...r},depth:i+1},f):null}),s.children=u,t.item.adv?.html?(delete s.children,s.dangerouslySetInnerHTML={__html:t.item.adv?.html}):t.item.adv?.js&&!t.item.adv.js.includes("children")&&(delete s.children,delete s.dangerouslySetInnerHTML),{props:s,text_props:c}};var My=A(ge());var Ay=A(Rf());var Jr={NORMAL:0,WILDCARD:1,PLACEHOLDER:2};function Us(e={}){let t={options:e,rootNode:by(),staticRoutesMap:{}},n=r=>e.strictTrailingSlash?r:r.replace(/\/$/,"")||"/";if(e.routes)for(let r in e.routes)Ey(t,n(r),e.routes[r]);return{ctx:t,lookup:r=>i2(t,n(r)),insert:(r,i)=>Ey(t,n(r),i),remove:r=>o2(t,n(r))}}function i2(e,t){let n=e.staticRoutesMap[t];if(n)return n.data;let r=t.split("/"),i={},o=!1,l=null,s=e.rootNode,c=null;for(let a=0;a{},db:null,api:null,init_local_effect:{}},page:{history:{id:"",show:!1},root_id:"root",cur:a2,doc:null,list:{},building:!1,meta:{},entry:[],tree:[],render:()=>{}},comp:{doc:null,item:null,loaded:{},list:{},group:{}},code:{},global_prop:[],ui:{comp_editable:localStorage.getItem("prasi-comp-editable")==="yes",zoom:localStorage.zoom||"100%",side:{prop:!0},layout:{left:parseInt(localStorage.getItem("prasi-layout-left")||"250"),right:parseInt(localStorage.getItem("prasi-layout-right")||"250")},prevent_indent_hook:!1,syncing:!1,tree:{item_loading:[],search:"",search_ref:null,search_mode:{Name:!0,JS:!1,HTML:!1,CSS:!1},rename_id:"",open:{}},popup:{file:{enabled:!1,open:!1,picker:{value:"",on_pick:!1,multi:!1},path:"/",expanded:JSON.parse(localStorage.getItem("panel-file-expanded")||"{}"),entry:{},selected:new Set,action:null,file_renaming:"",file_ctx_menu_event:null,tree:[],tree_renaming:"",tree_ctx_path:"",tree_ctx_menu_event:null,preview:!0,upload:{started:!1,progress:{}}},code:{init:!1,open:!1,name:"site",log:"",loading:!1,startup_status:"init",error:!1,show_log:!1,list:{}},page:{open:null,form:null},script:{open:!1,mode:"js",lastMode:"js",type:"item",prop_kind:"",prop_name:"",on_close:()=>{},typings:{status:"ok",err_msg:""},wb_render:()=>{}},site:null,site_form:null,comp:{preview_id:"",open:null,import:!1},comp_group:null,api:{open:!1}}}};var Bs=e=>({isMobile:e.mode==="mobile",isDesktop:e.mode==="desktop",isEditor:location.pathname.startsWith("/ed/")});var ur=A(ge());var at=window;var u2=pf("prasi-cache","prasi-cache-store"),Co={timeout:null,store:u2},$s=(e,t)=>{let n=[...Ry(t,"navigate(",")"),...Ry(t,"href = ",";")],r=e.page.cur.id;e.page.navs[r]||(e.page.navs[r]=new Set);for(let i of n)e.page.navs[r].add(i);clearTimeout(Co.timeout),Co.timeout=setTimeout(()=>{e.on_nav_loaded&&e.on_nav_loaded({urls:Array.from(e.page.navs[r])})},100)},Ry=(e,t,n)=>{let r=0,i=0,o=[];for(;;){let l=e.indexOf(t,r);if(i=r,l>=0){let s=e[l+t.length];if(s==='"'||s==="'"||s==="`"){let c=e.indexOf(`${s}${n}`,l+t.length+1),a=e.substring(l+t.length+1,c);r=c+2+n.length,o.push(a)}}if(i===r)break}return o};var ar=A(ge());var Ty=A(Sf()),sr=A(ge());var Cf=A(Z()),Ny=(e,t,n,r)=>i=>{let[o,l]=(0,sr.useState)({}),s=i.internal_key;n.item.script||(n.item.script={});let c=n.item.script;s&&(n.item.script_keyed||(n.item.script_keyed={}),n.item.script_keyed[s]||(n.item.script_keyed[s]={}),c=n.item.script_keyed[s]),c.passprop||(c.passprop={});let a={};if(c.passprop){let f=!1;for(let[p,g]of Object.entries(i))["children","key"].includes(p)||(f=!0,a[p]=g,c.passprop[p]={end:0,start:0,value:g})}let u={...r,...a};if(!Array.isArray(i.children)&&(0,sr.isValidElement)(i.children)&&typeof i.children=="object"){let f=(0,Ty.default)(i.children,"props.meta.item.component.props.child.content.childs");if(Array.isArray(f)){let p=!0;for(let g of f)!(0,sr.isValidElement)(g)&&typeof g=="object"||(p=!1);if(p)return f.map(g=>{let _=e.meta[g.id];if(_||(e.meta[g.id]={item:g},_=e.meta[g.id]),_){if(Object.keys(_.item).length<=3&&_.mitem){let x={..._.item},h=_.mitem.toJSON();_.item={...h,...x}}return(0,Cf.jsx)(qt,{is_layout:t,meta:_,passprop:u,parent_key:i.internal_key},g.id)}return null})}}if(!Array.isArray(i.children)&&!(0,sr.isValidElement)(i.children)&&typeof i.children=="object"){let f=i.children.id;if(f){let p=e.meta[f];if(!p){e.meta[f]={item:i.children},p=e.meta[f];let g=p.item.component?.id;g&&e.comp.load(g).then(_=>{if(_){for(let[h,m]of Object.entries(_)){let y=p.item;y[h]||(y[h]=m)}let x=p.item.component?.props;for(let[h,m]of Object.entries(_.component?.props||{}))x[h]||(x[h]=m);l({})}})}return(0,Cf.jsx)(qt,{is_layout:t,meta:p,passprop:u,parent_key:i.internal_key})}}return Tf(i,u,s?{parent_key:s}:void 0)},Tf=(e,t,n)=>{let r={};if(Array.isArray(e)?r.children=e:r=e,Array.isArray(r.children)){let i=[];for(let o of r.children)i.push(Cy(o,r,t,n));return i}return Cy(r.children,r,t,n)},Cy=(e,t,n,r)=>{if((0,sr.isValidElement)(e)){let i={...t};return delete i.children,{...e,props:{...e.props,...r,passprop:{...n,...i}}}}return e};var ti={},Iy=(e,t,n)=>r=>{let i=["localhost","prasi.avolut.com"].includes(location.hostname)&&location.pathname.startsWith("/ed/"),o=n.item.id,{children:l,parent_key:s}=r,c=e.script?.init_local_effect,a=t?e.layout?.meta:e.meta,u=(0,ar.useRef)(ti[o]?ti[o]:r.value),[d,f]=(0,ar.useState)({}),p=u.current;return p.render=()=>{window.prasiContext.render?window.prasiContext.render():f({})},Hs(e,n,n.script?.scope,s),r.hook&&r.hook(p),(0,ar.useEffect)(()=>{if(n.parent?.instance_id&&a){let _=a[n.parent?.instance_id];if(_&&_.instances){for(let[x,h]of Object.entries(_.instances[n.parent.instance_id]))if(h===n.item.id){o=x;break}}}return!c[o]&&(typeof c=="object"&&(c[o]=!0),(async()=>r.effect&&(await r.effect(p),i&&(ti[o]=p)))()),()=>{}},[...r.deps||[],location.pathname]),(0,ar.useEffect)(()=>{i&&ti[o]===null&&(async()=>r.effect&&(await r.effect(p),i&&(ti[o]=p)))()},[ti[o]]),Tf(l,{...n.script?.scope,[r.name]:p})};var Nf=A(Z()),Oy=(e,t,n,r,i,o)=>{let l=Eo(e,t,n,r,i);e.visit&&e.visit(t,l),t.script?t.script.scope=r:t.script={scope:r,result:null,Local:Iy(e,n,t),PassProp:Ny(e,n,t,r)};let s=t.script,c=window.exports,a={useEffect:ur.useEffect,children:l.props.children,props:l.props,Local:s?.Local,db:e.site.db,api:e.site.api,PassProp:s?.PassProp,ErrorBox:Nt,newElement:()=>{},render:f=>{let p=f;if((0,ur.isValidElement)(f)&&f.props.children){let g=x=>{let h=!1,m=[];if((0,ur.isValidElement)(x)){if(x.type===t.script?.PassProp)return{should_replace:!0,el:{...x,props:{...x.props,internal_key:x.key}}};if(Array.isArray(x.props?.children))for(let y of x.props?.children)if(Array.isArray(y)){let v=[],S=!1;for(let R of y){let k=g(R);k.should_replace?(v.push(k.el),S=!0):v.push(R)}S?(h=!0,m.push(v)):m.push(y)}else typeof y=="object"?y.type===t.script?.PassProp&&(h=!0,m.push({...y,props:{...y.props,internal_key:y.props.key}})):m.push(y)}return{should_replace:h,el:{...x,props:{...x.props,children:m}}}},_=g(f);_.should_replace&&(p=_.el)}s&&(s.result=(0,Nf.jsx)(ur.Suspense,{children:p}))},params,...Bs(e),...c,...r,_meta:e.meta,_item:t.item,_syncm:typeof syncronize<"u"?syncronize:void 0};if(typeof r=="object"){for(let[f,p]of Object.entries(r))if(typeof p=="object"&&p&&p._jsx){let g=p;a[f]=(0,Nf.jsx)(c2,{fn:g.fn,passprop:{...r},meta:t})}}!at.isEditor&&t.item.adv?.js&&$s(e,t.item.adv.js);let u=t.item.adv?.jsBuilt||"",d=Of(u,If)||"";try{new Function(...Object.keys(a),"___js",`// ${t.item.name}: ${t.item.id} +>>>>>>> 4c06cb6b (wip fix code) try { ${d} } catch(e) { diff --git a/app/srv/ws/sync/actions.ts b/app/srv/ws/sync/actions.ts index edfba7c6..65345877 100644 --- a/app/srv/ws/sync/actions.ts +++ b/app/srv/ws/sync/actions.ts @@ -119,7 +119,7 @@ export const SyncActions = { | { type: "startup-check" } | { type: "startup-run" } | { type: "startup-stop" } - | { type: "push-typings"; body: Uint8Array; hash: number } + // | { type: "push-typings"; body: Uint8Array; hash: number } | { type: "check-typings"; hash: number } )) | { type: "flush-page-cache"; page_id: string } diff --git a/app/srv/ws/sync/actions/code_action.ts b/app/srv/ws/sync/actions/code_action.ts index 9fa49173..1760c13e 100644 --- a/app/srv/ws/sync/actions/code_action.ts +++ b/app/srv/ws/sync/actions/code_action.ts @@ -1,7 +1,6 @@ import { Subprocess, spawn } from "bun"; import { waitUntil } from "web-utils"; import { SAction } from "../actions"; -import { code, codeGlobalTypings } from "../editor/code/util-code"; import { docs } from "../entity/docs"; import { snapshot } from "../entity/snapshot"; import { SyncConnection } from "../type"; @@ -9,6 +8,7 @@ import { dirAsync } from "fs-jetpack"; import path from "path"; import { gunzipAsync } from "../entity/zlib"; import { prismaExtendType } from "../../../../web/src/utils/script/prisma-extend"; +import { code } from "../code/code"; const decoder = new TextDecoder(); const code_startup = { @@ -84,41 +84,41 @@ export const code_action: SAction["code"]["action"] = async function ( } catch (e) {} return { type: "check-typings", hash: false }; } - case "push-typings": { - const dir = code.path(arg.site_id, "site", "src", "typings"); - await dirAsync(dir); - await dirAsync(path.join(dir, "runtime")); - Bun.write(Bun.file(path.join(dir, "hash")), arg.hash.toString()); - const res = JSON.parse(decoder.decode(await gunzipAsync(arg.body))); - await Bun.write(Bun.file(path.join(dir, "api.d.ts")), res.api); - await Bun.write( - Bun.file(path.join(dir, "prisma.d.ts")), - res.prisma["prisma.d.ts"] - ); - await Bun.write( - Bun.file(path.join(dir, "runtime/index.d.ts")), - res.prisma["runtime/index.d.ts"] - ); - await Bun.write( - Bun.file(path.join(dir, "runtime/library.d.ts")), - res.prisma["runtime/library.d.ts"] - ); - await Bun.write( - Bun.file(path.join(dir, "global.d.ts")), - codeGlobalTypings.replace( - `declare global {`, - `declare global { - const db: prisma.PrismaClient & ${prismaExtendType}; - ` - ) - ); + // case "push-typings": { + // const dir = code.path(arg.site_id, "site", "src", "typings"); + // await dirAsync(dir); + // await dirAsync(path.join(dir, "runtime")); + // Bun.write(Bun.file(path.join(dir, "hash")), arg.hash.toString()); + // const res = JSON.parse(decoder.decode(await gunzipAsync(arg.body))); + // await Bun.write(Bun.file(path.join(dir, "api.d.ts")), res.api); + // await Bun.write( + // Bun.file(path.join(dir, "prisma.d.ts")), + // res.prisma["prisma.d.ts"] + // ); + // await Bun.write( + // Bun.file(path.join(dir, "runtime/index.d.ts")), + // res.prisma["runtime/index.d.ts"] + // ); + // await Bun.write( + // Bun.file(path.join(dir, "runtime/library.d.ts")), + // res.prisma["runtime/library.d.ts"] + // ); + // await Bun.write( + // Bun.file(path.join(dir, "global.d.ts")), + // codeGlobalTypings.replace( + // `declare global {`, + // `declare global { + // const db: prisma.PrismaClient & ${prismaExtendType}; + // ` + // ) + // ); - Bun.spawn({ - cmd: ["chmod", "-R", "777", "."], - cwd: code.path(arg.site_id, "site", "src"), - }); + // Bun.spawn({ + // cmd: ["chmod", "-R", "777", "."], + // cwd: code.path(arg.site_id, "site", "src"), + // }); - break; - } + // break; + // } } }; diff --git a/app/srv/ws/sync/actions/code_load.ts b/app/srv/ws/sync/actions/code_load.ts index 3699aa0e..b1eeb03e 100644 --- a/app/srv/ws/sync/actions/code_load.ts +++ b/app/srv/ws/sync/actions/code_load.ts @@ -1,5 +1,4 @@ import { SAction } from "../actions"; -import { prepCodeSnapshot } from "../editor/code/prep-code"; import { SyncConnection } from "../type"; export const code_load: SAction["code"]["load"] = async function ( @@ -7,7 +6,5 @@ export const code_load: SAction["code"]["load"] = async function ( site_id, type ) { - const snap = await prepCodeSnapshot(site_id, "site"); - return { id: site_id }; }; diff --git a/app/srv/ws/sync/actions/site_load.ts b/app/srv/ws/sync/actions/site_load.ts index 9e920510..39e557a6 100644 --- a/app/srv/ws/sync/actions/site_load.ts +++ b/app/srv/ws/sync/actions/site_load.ts @@ -2,7 +2,7 @@ import { validate } from "uuid"; import { ESite } from "../../../../web/src/nova/ed/logic/ed-global"; import { SAction } from "../actions"; import { SyncConnection } from "../type"; -import { prepCodeSnapshot } from "../editor/code/prep-code"; +import { code } from "../code/code"; export const site_load: SAction["site"]["load"] = async function ( this: SyncConnection, @@ -13,6 +13,8 @@ export const site_load: SAction["site"]["load"] = async function ( if (site) { if (this.conf) this.conf.site_id = site.id; + code.init(site.id, "init site_load"); + const config = typeof site.config === "object" && site.config ? { api_url: (site.config as any).api_url || "" } @@ -43,8 +45,6 @@ export const site_load: SAction["site"]["load"] = async function ( } } - const code = await prepCodeSnapshot(site.id, "site"); - return { id: site.id, name: site.name, @@ -58,7 +58,6 @@ export const site_load: SAction["site"]["load"] = async function ( meta: undefined, entry: [], }, - code_ts: code.ts, }; } } diff --git a/app/srv/ws/sync/code/code.ts b/app/srv/ws/sync/code/code.ts new file mode 100644 index 00000000..7a7fca75 --- /dev/null +++ b/app/srv/ws/sync/code/code.ts @@ -0,0 +1,37 @@ +import { dir } from "dir"; +import { initFrontEnd } from "./parts/init/frontend"; +import { initServer } from "./parts/init/server"; +import { initTypings } from "./parts/init/typings"; +import { codeInternal } from "./parts/internal"; +import { ensureLib } from "./utlis/ensure-lib"; +import { ensure } from "./utlis/ensure"; + +export const code = { + internal: codeInternal, + async init(id_site: string, note: string) { + const { frontend, server, typings } = this.internal; + if (!frontend[id_site] || !server[id_site] || !typings[id_site]) { + const root = `/code/${id_site}/site/src`; + + if (!frontend[id_site]) await initFrontEnd(root, id_site); + if (!server[id_site]) await initServer(root, id_site); + if (!typings[id_site]) await initTypings(root, id_site); + + await ensureLib(root, id_site); + await ensure(`${root}/index.tsx`, ``); + } + }, + path( + id_site: string, + mode: "site" | "server", + type: "src" | "build" | "build_cache", + path?: string + ) { + let file_path = ""; + if (path) { + file_path = path[0] === "/" ? path : `/${path}`; + } + + return dir.data(`/code/${id_site}/${mode}/${type}${file_path}`); + }, +}; diff --git a/app/srv/ws/sync/code/parts/init/frontend.ts b/app/srv/ws/sync/code/parts/init/frontend.ts new file mode 100644 index 00000000..e87d2bad --- /dev/null +++ b/app/srv/ws/sync/code/parts/init/frontend.ts @@ -0,0 +1,10 @@ +import { code } from "../../code"; + +export const initFrontEnd = async (root: string, id_site: string) => { + const existing = code.internal.frontend[id_site]; + if (existing) { + await existing.dispose(); + } + + code.internal.frontend[id_site] = true as any; +}; diff --git a/app/srv/ws/sync/code/parts/init/server.ts b/app/srv/ws/sync/code/parts/init/server.ts new file mode 100644 index 00000000..a8fb6dfc --- /dev/null +++ b/app/srv/ws/sync/code/parts/init/server.ts @@ -0,0 +1,88 @@ +import { context } from "esbuild"; +import { code } from "../../code"; +import { ensure } from "../../utlis/ensure"; +import { dir } from "dir"; +import { dirAsync, removeAsync, existsAsync, writeAsync } from "fs-jetpack"; + +export const initServer = async (root: string, id_site: string) => { + const existing = code.internal.server[id_site]; + if (existing) { + await existing.dispose(); + } + + const build_path = code.path(id_site, "server", "build"); + await removeAsync(build_path); + await dirAsync(build_path); + + const build_file = `${build_path}/index.js`; + if (!(await existsAsync(build_file))) { + await writeAsync(build_file, ""); + } + + code.internal.server[id_site] = await context({ + absWorkingDir: dir.data(root), + entryPoints: ["server.ts"], + bundle: true, + outfile: build_file, + platform: "node", + treeShaking: true, + format: "cjs", + logLevel: "silent", + banner: { + js: `\ + const _fs = require('node:fs/promises'); + const console = + typeof global.server_hook === "function" + ? { ...global.console } + : global.console; + + let db = new Proxy({}, { + get(_, key) { + const runtime = global.server_runtime["${id_site}"]; + if (runtime && runtime.db) { + return runtime.db[key]; + } + } + }); + let api = {}; + if (typeof global.server_hook === "function") { + const log = global.console.log; + console.log = function (...arg) { + const out = "${code.path(id_site, "site", "src", "server.log")}"; + _fs.appendFile(out, arg.map((e)=>{ + const ancestors = []; + if (typeof e === 'object') return JSON.stringify(e, function (key, val) { + if (val) { + if (typeof val === 'function') { + return '[function]'; + } + if (typeof val === 'object') { + while (ancestors.length > 0 && ancestors.at(-1) !== this) { + ancestors.pop(); + } + if (ancestors.includes(val)) { + return "[circular]"; + } + ancestors.push(val); + + if (val.constructor && + !['Object', 'Array'].includes(val.constructor.name)) { + if (val.constructor.name === 'Error') { + return '[Error] ' + val.message; + } + return '[Class] ' + val.constructor.name; + } + } + } + return val; + }, 2); + return e; + }).join(" ") + "\\n"); + }.bind(console); + } else { + db = global.db; + api = global.api; + }`, + }, + }); +}; diff --git a/app/srv/ws/sync/code/parts/init/typings.ts b/app/srv/ws/sync/code/parts/init/typings.ts new file mode 100644 index 00000000..8e9f4658 --- /dev/null +++ b/app/srv/ws/sync/code/parts/init/typings.ts @@ -0,0 +1,5 @@ +import { code } from "../../code"; + +export const initTypings = async (root_dir: string, id_site: string) => { + code.internal.typings[id_site] = true as any; +}; diff --git a/app/srv/ws/sync/code/parts/internal.ts b/app/srv/ws/sync/code/parts/internal.ts new file mode 100644 index 00000000..3cecbaf2 --- /dev/null +++ b/app/srv/ws/sync/code/parts/internal.ts @@ -0,0 +1,25 @@ +import { Subprocess } from "bun"; +import type { BuildContext } from "esbuild"; + +const g = global as unknown as { + prasi_code: any; +}; +type SITE_ID = string; + +export const codeInternal = { + get server() { + if (!g.prasi_code) g.prasi_code = {}; + if (!g.prasi_code.server) g.prasi_code.server = {}; + return g.prasi_code.server as Record; + }, + get frontend() { + if (!g.prasi_code) g.prasi_code = {}; + if (!g.prasi_code.frontend) g.prasi_code.frontend = {}; + return g.prasi_code.frontend as Record; + }, + get typings() { + if (!g.prasi_code) g.prasi_code = {}; + if (!g.prasi_code.typings) g.prasi_code.typings = {}; + return g.prasi_code.typings as Record; + }, +}; diff --git a/app/srv/ws/sync/code/templates/index_tsx b/app/srv/ws/sync/code/templates/index_tsx new file mode 100644 index 00000000..3c425e48 --- /dev/null +++ b/app/srv/ws/sync/code/templates/index_tsx @@ -0,0 +1,2 @@ +export * from "@/data"; +export * from "@/exports"; \ No newline at end of file diff --git a/app/srv/ws/sync/code/templates/server_ts b/app/srv/ws/sync/code/templates/server_ts new file mode 100644 index 00000000..82676817 --- /dev/null +++ b/app/srv/ws/sync/code/templates/server_ts @@ -0,0 +1,7 @@ +import type {} from "./typings/global"; + +export const server: PrasiServer = { + async http({ req, handle, mode, url, index, server }) { + return await handle(req); + } +}; \ No newline at end of file diff --git a/app/srv/ws/sync/code/templates/tsconfig_json b/app/srv/ws/sync/code/templates/tsconfig_json new file mode 100644 index 00000000..21a4c9cc --- /dev/null +++ b/app/srv/ws/sync/code/templates/tsconfig_json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "paths": { + "@/*": ["./lib/*"], + "app/*": ["./app/*"], + "server/*": ["./server/*"] + }, + "lib": ["ESNext", "DOM"], + "module": "esnext", + "target": "esnext", + "moduleResolution": "bundler", + "moduleDetection": "force", + "declaration": true, + "outFile": "types.d.ts", + "emitDeclarationOnly": true, + "composite": true, + "strict": true, + "downlevelIteration": true, + "skipLibCheck": true, + "jsx": "react-jsx", + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "allowJs": true, + "typeRoots": ["./node_modules/@types", "./lib/types"] + } +} diff --git a/app/srv/ws/sync/code/templates/typings/global_d_ts b/app/srv/ws/sync/code/templates/typings/global_d_ts new file mode 100644 index 00000000..8f5eb0af --- /dev/null +++ b/app/srv/ws/sync/code/templates/typings/global_d_ts @@ -0,0 +1,68 @@ +//@ts-ignore +import type * as SRVAPI from "gen/srv/api/srv"; +import { Server, WebSocketHandler } from "bun"; +import prisma from "./prisma"; + +declare global { + type Api = typeof SRVAPI; + type ApiName = keyof Api; + const api: { [k in ApiName]: Awaited["_"]["api"] } & { + _raw: any; + }; + const db: prisma.PrismaClient & { + _batch: { + update: ( + batch: { + table: string; + data: any; + where: any; + }[] + ) => Promise; + }; + _schema: { + tables: () => Promise; + columns: (table: string) => Promise< + Record< + string, + { + is_pk: boolean; + type: string; + optional: boolean; + db_type: string; + default?: any; + } + > + >; + rels: (table: string) => Promise< + Record< + string, + { + type: "has-many" | "has-one"; + to: { + table: string; + fields: string[]; + }; + from: { + table: string; + fields: string[]; + }; + } + > + >; + }; + }; + + type PrasiServer = { + ws?: WebSocketHandler<{ url: string }>; + http: (arg: { + url: { raw: URL; pathname: string }; + req: Request; + server: Server; + mode: "dev" | "prod"; + handle: (req: Request) => Promise; + index: { head: string[]; body: string[]; render: () => string }; + prasi: { page_id?: string; params?: Record }; + }) => Promise; + init: (arg: { port?: number }) => Promise; + }; +} diff --git a/app/srv/ws/sync/code/utlis/ensure-lib.ts b/app/srv/ws/sync/code/utlis/ensure-lib.ts new file mode 100644 index 00000000..015cb95b --- /dev/null +++ b/app/srv/ws/sync/code/utlis/ensure-lib.ts @@ -0,0 +1,15 @@ +import { $ } from "bun"; +import { dir } from "dir"; +import { exists, dirAsync } from "fs-jetpack"; + +export const ensureLib = async (src_dir: string, id_site: string) => { + if (!exists(dir.data(src_dir))) { + await dirAsync(dir.data(src_dir)); + } + + if (!exists(dir.data(`${src_dir}/lib`))) { + console.log(`${src_dir}/lib not found.`); + const _ = $.cwd(dir.data(src_dir)); + await _`git clone https://github.com/avolut/prasi-lib lib`; + } +}; diff --git a/app/srv/ws/sync/code/utlis/ensure.ts b/app/srv/ws/sync/code/utlis/ensure.ts new file mode 100644 index 00000000..e4e88054 --- /dev/null +++ b/app/srv/ws/sync/code/utlis/ensure.ts @@ -0,0 +1,14 @@ +import { exists, dirAsync } from "fs-jetpack"; +import { dirname } from "path"; +import { dir } from "dir"; + +export const ensure = async (path: string, content: string) => { + const _path = dir.data(path); + const _dir = dirname(_path); + if (!exists(_dir)) { + await dirAsync(_dir); + } + if (!exists(_path)) { + await Bun.write(_path, content); + } +}; diff --git a/app/srv/ws/sync/editor/code/build-code.ts b/app/srv/ws/sync/editor/code/build-code.ts index c68fc49c..36eb5c99 100644 --- a/app/srv/ws/sync/editor/code/build-code.ts +++ b/app/srv/ws/sync/editor/code/build-code.ts @@ -1,22 +1,7 @@ -import globalExternals from "@fal-works/esbuild-plugin-global-externals"; -import { style } from "@hyrious/esbuild-plugin-style"; -import { context } from "esbuild"; -import { - dirAsync, - existsAsync, - removeAsync, - writeAsync, - moveAsync, -} from "fs-jetpack"; -import { server } from "./server-main"; -import { code } from "./util-code"; -import { user } from "../../entity/user"; -import { conns } from "../../entity/conn"; -import { SyncType } from "../../type"; -import { Packr } from "msgpackr"; import { ServerWebSocket } from "bun"; +import { Packr } from "msgpackr"; import { WSData } from "../../../../../../pkgs/core/server/create"; -import { g } from "utils/global"; +import { code } from "../../code/code"; const packr = new Packr({ structuredClone: true }); @@ -25,267 +10,240 @@ const sendWS = (ws: ServerWebSocket, msg: any) => { }; export const codeBuild = async (id_site: any) => { - const src_path = code.path(id_site, "site", "src"); - if (!(await existsAsync(src_path))) return; - if (!code.esbuild[id_site]) { - code.esbuild[id_site] = { site: null, server: null, site_ts: Date.now() }; - } + return; + // const src_path = code.path(id_site, "site", "src"); + // if (!(await existsAsync(src_path))) return; + // if (!code.esbuild[id_site]) { + // code.esbuild[id_site] = { site: null, server: null, site_ts: Date.now() }; + // } - if (!code.esbuild[id_site].server) { - const server_main = code.path(id_site, "site", "src", "server.ts"); - if (!(await existsAsync(server_main))) { - await writeAsync( - server_main, - `\ -import type {} from "./typings/global"; + // if (!code.esbuild[id_site].server) { + // const server_main = code.path(id_site, "site", "src", "server.ts"); + // if (!(await existsAsync(server_main))) { + // await writeAsync( + // server_main, + // `\ + // import type {} from "./typings/global"; -export const server: PrasiServer = { - async http({ req, handle, mode, url, index, server }) { - return await handle(req); - } + // export const server: PrasiServer = { + // async http({ req, handle, mode, url, index, server }) { + // return await handle(req); + // } + // }; + // ` + // ); + // const bun_types = Bun.spawn({ + // cmd: ["npm", "i", "-D", "@types/bun"], + // cwd: code.path(id_site, "site", "src"), + // }); + // await bun_types.exited; + // } + + // const build_path = code.path(id_site, "server", "build"); + // await removeAsync(build_path); + // await dirAsync(build_path); + + // const build_file = `${build_path}/index.js`; + // if (!(await existsAsync(build_file))) { + // await writeAsync(build_file, ""); + // } + + // code.esbuild[id_site].server = await context({ + // absWorkingDir: src_path, + // entryPoints: ["server.ts"], + // bundle: true, + // outfile: build_file, + // platform: "node", + // treeShaking: true, + // format: "cjs", + // logLevel: "silent", + // banner: { + // js: `\ + // const _fs = require('node:fs/promises'); + // const console = + // typeof global.server_hook === "function" + // ? { ...global.console } + // : global.console; + + // let db = new Proxy({}, { + // get(_, key) { + // const runtime = global.server_runtime["${id_site}"]; + // if (runtime && runtime.db) { + // return runtime.db[key]; + // } + // } + // }); + // let api = {}; + // if (typeof global.server_hook === "function") { + // const log = global.console.log; + // console.log = function (...arg) { + // const out = "${code.path(id_site, "site", "src", "server.log")}"; + // _fs.appendFile(out, arg.map((e)=>{ + // const ancestors = []; + // if (typeof e === 'object') return JSON.stringify(e, function (key, val) { + // if (val) { + // if (typeof val === 'function') { + // return '[function]'; + // } + // if (typeof val === 'object') { + // while (ancestors.length > 0 && ancestors.at(-1) !== this) { + // ancestors.pop(); + // } + // if (ancestors.includes(val)) { + // return "[circular]"; + // } + // ancestors.push(val); + + // if (val.constructor && + // !['Object', 'Array'].includes(val.constructor.name)) { + // if (val.constructor.name === 'Error') { + // return '[Error] ' + val.message; + // } + // return '[Class] ' + val.constructor.name; + // } + // } + // } + // return val; + // }, 2); + // return e; + // }).join(" ") + "\\n"); + // }.bind(console); + // } else { + // db = global.db; + // api = global.api; + // }`, + // }, + // plugins: [ + // style(), + // globalExternals({ + // react: { + // varName: "window.React", + // type: "cjs", + // }, + // "react-dom": { + // varName: "window.ReactDOM", + // type: "cjs", + // }, + // }), + // { + // name: "prasi", + // setup(setup) { + // const reinit = () => { + // setup.onEnd((res) => { + // if (res.errors.length > 0) { + // Bun.write( + // Bun.file(code.path(id_site, "site", "src", "server.log")), + // JSON.stringify(res.errors, null, 2) + // ); + // } + // server.init(id_site); + // }); + // }; + // reinit(); + // }, + // }, + // ], + // }); + + // const esbuild = code.esbuild[id_site].server; + // esbuild?.watch(); + // } + + // if (!code.esbuild[id_site].site) { + // const build_path = code.path(id_site, "site", "build_cache"); + // await removeAsync(build_path); + // await dirAsync(build_path); + // const build_file = `${build_path}/index.js`; + // await writeAsync(build_file, ""); + + // const index_path = code.path(id_site, "site", "src", "index.tsx"); + // if (!(await existsAsync(index_path))) { + // await writeAsync(index_path, 'export const hello = "world"'); + // } + // console.log("running "); + + // code.esbuild[id_site].site = await context({ + // absWorkingDir: src_path, + // entryPoints: ["index.tsx"], + // bundle: true, + // outdir: build_path, + // minify: true, + // treeShaking: true, + // format: "esm", + // splitting: true, + // logLevel: "silent", + // sourcemap: true, + // plugins: [ + // style(), + // globalExternals({ + // react: { + // varName: "window.React", + // type: "cjs", + // }, + // "react-dom": { + // varName: "window.ReactDOM", + // type: "cjs", + // }, + // }), + // { + // name: "prasi", + // setup(setup) { + // setup.onEnd(async (res) => { + // if (res.errors.length > 0) { + // await codeError( + // id_site, + // res.errors.map((e) => e.text).join("\n\n"), + // "site" + // ); + // } else { + // codeBuildType(id_site); + // await removeAsync(code.path(id_site, "site", "build")); + // await moveAsync( + // code.path(id_site, "site", "build_cache"), + // code.path(id_site, "site", "build") + // ); + // await removeAsync( + // code.path(id_site, "site", "src", "index.log") + // ); + + // code.esbuild[id_site].site_ts = Date.now(); + // const client_ids = new Set(); + // user.active.findAll({ site_id: id_site }).forEach((e) => { + // client_ids.add(e.client_id); + // }); + + // client_ids.forEach((client_id) => { + // const ws = conns.get(client_id)?.ws; + // if (ws) { + // sendWS(ws, { + // type: SyncType.Event, + // event: "code_changes", + // data: { ts: code.esbuild[id_site].site_ts }, + // }); + // } + // }); + // } + // }); + // }, + // }, + // ], + // }); + // const esbuild = code.esbuild[id_site].site; + // esbuild?.watch(); + // } + // for (const _mode of ["site", "server"]) { + // const mode = _mode as "site" | "server"; + + // const esbuild = code.esbuild[id_site][mode]; + // if (esbuild) { + // try { + // await esbuild.rebuild(); + // } catch (e: any) { + // await codeError(id_site, e.message, mode); + // } + // } + // } }; -` - ); - const bun_types = Bun.spawn({ - cmd: ["npm", "i", "-D", "@types/bun"], - cwd: code.path(id_site, "site", "src"), - }); - await bun_types.exited; - } - - const build_path = code.path(id_site, "server", "build"); - await removeAsync(build_path); - await dirAsync(build_path); - - const build_file = `${build_path}/index.js`; - if (!(await existsAsync(build_file))) { - await writeAsync(build_file, ""); - } - - code.esbuild[id_site].server = await context({ - absWorkingDir: src_path, - entryPoints: ["server.ts"], - bundle: true, - outfile: build_file, - platform: "node", - treeShaking: true, - format: "cjs", - logLevel: "silent", - banner: { - js: `\ -const _fs = require('node:fs/promises'); -const console = -typeof global.server_hook === "function" - ? { ...global.console } - : global.console; - -let db = new Proxy({}, { - get(_, key) { - const runtime = global.server_runtime["${id_site}"]; - if (runtime && runtime.db) { - return runtime.db[key]; - } - } -}); -let api = {}; -if (typeof global.server_hook === "function") { - const log = global.console.log; - console.log = function (...arg) { - const out = "${code.path(id_site, "site", "src", "server.log")}"; - _fs.appendFile(out, arg.map((e)=>{ - const ancestors = []; - if (typeof e === 'object') return JSON.stringify(e, function (key, val) { - if (val) { - if (typeof val === 'function') { - return '[function]'; - } - if (typeof val === 'object') { - while (ancestors.length > 0 && ancestors.at(-1) !== this) { - ancestors.pop(); - } - if (ancestors.includes(val)) { - return "[circular]"; - } - ancestors.push(val); - - if (val.constructor && - !['Object', 'Array'].includes(val.constructor.name)) { - if (val.constructor.name === 'Error') { - return '[Error] ' + val.message; - } - return '[Class] ' + val.constructor.name; - } - } - } - return val; - }, 2); - return e; - }).join(" ") + "\\n"); - }.bind(console); -} else { - db = global.db; - api = global.api; -}`, - }, - plugins: [ - style(), - globalExternals({ - react: { - varName: "window.React", - type: "cjs", - }, - "react-dom": { - varName: "window.ReactDOM", - type: "cjs", - }, - }), - { - name: "prasi", - setup(setup) { - const reinit = () => { - setup.onEnd((res) => { - if (res.errors.length > 0) { - Bun.write( - Bun.file(code.path(id_site, "site", "src", "server.log")), - JSON.stringify(res.errors, null, 2) - ); - } - server.init(id_site); - }); - }; - reinit(); - }, - }, - ], - }); - - const esbuild = code.esbuild[id_site].server; - esbuild?.watch(); - } - - if (!code.esbuild[id_site].site) { - const build_path = code.path(id_site, "site", "build_cache"); - await removeAsync(build_path); - await dirAsync(build_path); - const build_file = `${build_path}/index.js`; - await writeAsync(build_file, ""); - - const index_path = code.path(id_site, "site", "src", "index.tsx"); - if (!(await existsAsync(index_path))) { - await writeAsync(index_path, 'export const hello = "world"'); - } - - code.esbuild[id_site].site = await context({ - absWorkingDir: src_path, - entryPoints: ["index.tsx"], - bundle: true, - outdir: build_path, - minify: true, - treeShaking: true, - format: "esm", - splitting: true, - logLevel: "silent", - sourcemap: true, - plugins: [ - style(), - globalExternals({ - react: { - varName: "window.React", - type: "cjs", - }, - "react-dom": { - varName: "window.ReactDOM", - type: "cjs", - }, - }), - { - name: "prasi", - setup(setup) { - setup.onEnd(async (res) => { - if (res.errors.length > 0) { - await codeError( - id_site, - res.errors.map((e) => e.text).join("\n\n"), - "site" - ); - } else { - await removeAsync(code.path(id_site, "site", "build")); - await moveAsync( - code.path(id_site, "site", "build_cache"), - code.path(id_site, "site", "build") - ); - await removeAsync( - code.path(id_site, "site", "src", "index.log") - ); - - code.esbuild[id_site].site_ts = Date.now(); - const client_ids = new Set(); - user.active.findAll({ site_id: id_site }).forEach((e) => { - client_ids.add(e.client_id); - }); - - client_ids.forEach((client_id) => { - const ws = conns.get(client_id)?.ws; - if (ws) { - sendWS(ws, { - type: SyncType.Event, - event: "code_changes", - data: { ts: code.esbuild[id_site].site_ts }, - }); - } - }); - } - }); - }, - }, - ], - }); - const esbuild = code.esbuild[id_site].site; - esbuild?.watch(); - } - for (const _mode of ["site", "server"]) { - const mode = _mode as "site" | "server"; - - const esbuild = code.esbuild[id_site][mode]; - if (esbuild) { - try { - await esbuild.rebuild(); - } catch (e: any) { - await codeError(id_site, e.message, mode); - } - } - - // const build_file = code.path(id_site, mode, "build", "index.js"); - // const out = Bun.file(build_file); - // const src = (await out.text()).replace( - // "//# sourceMappingURL=index.js.map", - // `//# sourceMappingURL=/nova-load/code/${id_site}/${mode}/index.js.map` - // ); - // // await Bun.write(out, src); - } -}; - -// const codeApplyChanges = (path: string, doc: DCode) => { -// const map = doc.getMap("map"); - -// const files = map.get("files"); - -// const dirs = readDirectoryRecursively(path); -// doc.transact(() => { -// files?.forEach((v, k) => { -// if (!dirs[k]) { -// files?.delete(k); -// } -// }); -// for (const [k, v] of Object.entries(dirs)) { -// if (files) { -// files.set(k, v); -// } -// } -// }); - -// return doc; -// }; const codeError = async ( id_site: string, diff --git a/app/srv/ws/sync/editor/code/prep-code.ts b/app/srv/ws/sync/editor/code/prep-code.ts deleted file mode 100644 index b5fde610..00000000 --- a/app/srv/ws/sync/editor/code/prep-code.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { g } from "utils/global"; -import { codeBuild } from "./build-code"; -import { CodeMode, code, codeGlobalTypings } from "./util-code"; - -export const prepCodeSnapshot = async (id_site: string, mode: CodeMode) => { - await code - .prep(id_site, mode) - .new_file("index.tsx", "export const sample = 'hello_world'") - .new_file( - "package.json", - JSON.stringify({ name: `${mode}-${id_site}`, dependencies: {} }, null, 2) - ) - .new_file("typings/global.d.ts", codeGlobalTypings) - .await(); - - await codeBuild(id_site); - - return { ts: code.esbuild[id_site].site_ts }; -}; diff --git a/app/srv/ws/sync/editor/code/server-main.ts b/app/srv/ws/sync/editor/code/server-main.ts index 5a113719..9993541a 100644 --- a/app/srv/ws/sync/editor/code/server-main.ts +++ b/app/srv/ws/sync/editor/code/server-main.ts @@ -1,13 +1,12 @@ -import type { Server, WebSocketHandler, ServerWebSocket } from "bun"; +import type { Server, WebSocketHandler } from "bun"; import { existsAsync } from "fs-jetpack"; import _fs from "node:fs/promises"; import { g } from "utils/global"; import { waitUntil } from "web-utils"; -import { code } from "./util-code"; import { WSData } from "../../../../../../pkgs/core/server/create"; -import { codeBuild } from "./build-code"; import { prodIndex } from "../../../../util/prod-index"; +import { code } from "../../code/code"; import "./server-runtime"; const serverMain = () => ({ @@ -61,10 +60,9 @@ const serverMain = () => ({ site_id: string, arg: Parameters>[0] ) { - if (!code.esbuild[site_id]) { - await codeBuild(site_id); + if (arg.url.pathname.endsWith("main.js")) { + code.init(site_id, "init http"); } - if (typeof this.handler[site_id] === "undefined") { if ( await existsAsync(code.path(site_id, "server", "build", "index.js")) @@ -130,13 +128,11 @@ type PrasiServer = { index: { head: string[]; body: string[]; render: () => string }; prasi: { page_id?: string; params?: Record }; }) => Promise; - init?: () => Promise; + init?: (arg: { port?: number }) => Promise; }; const glb = global as unknown as { _server: ReturnType; }; -if (!glb._server) { - glb._server = serverMain(); -} +glb._server = serverMain(); export const server = glb._server; diff --git a/app/srv/ws/sync/editor/code/util-code.ts b/app/srv/ws/sync/editor/code/util-code.ts index 2b46cbfc..16d6ca77 100644 --- a/app/srv/ws/sync/editor/code/util-code.ts +++ b/app/srv/ws/sync/editor/code/util-code.ts @@ -3,6 +3,7 @@ import { dir } from "dir"; import { BuildContext } from "esbuild"; import { dirAsync, exists, existsAsync, writeAsync } from "fs-jetpack"; import { dirname } from "path"; +import { prismaExtendType } from "../../../../../web/src/utils/script/prisma-extend"; export type CodeBuild = { server: BuildContext | null; @@ -11,13 +12,13 @@ export type CodeBuild = { }; export type CodeMode = keyof CodeBuild; -export const code = { +const code = { path( id_site: string, mode: CodeMode, type: "src" | "build" | "build_cache", path?: string - ) { + ) { let file_path = ""; if (path) { file_path = path[0] === "/" ? path : `/${path}`; @@ -69,6 +70,8 @@ declare global { type Api = typeof SRVAPI; type ApiName = keyof Api; const api: { [k in ApiName]: Awaited["_"]["api"] } & { _raw: any }; + const db: prisma.PrismaClient & ${prismaExtendType}; + type PrasiServer = { ws?: WebSocketHandler<{ url: string }>; @@ -81,7 +84,7 @@ declare global { index: { head: string[]; body: string[]; render: () => string }; prasi: { page_id?: string; params?: Record }; }) => Promise; - init: () => Promise; + init: (arg: { port?: number }) => Promise; }; } `; diff --git a/app/web/src/nova/ed/logic/ed-global.ts b/app/web/src/nova/ed/logic/ed-global.ts index 01986015..8e84c23c 100644 --- a/app/web/src/nova/ed/logic/ed-global.ts +++ b/app/web/src/nova/ed/logic/ed-global.ts @@ -24,7 +24,6 @@ export const EmptySite = { meta: undefined as void | Record, entry: [] as string[], }, - code_ts: 0, }; export type ESite = typeof EmptySite; @@ -150,6 +149,7 @@ export const EDGlobal = { sync: null as null | Awaited>, sync_assigned: false, site: deepClone(EmptySite), + site_tstamp: Date.now(), site_exports: {} as Record, site_dts: "", script: { diff --git a/app/web/src/nova/vi/load/load-snapshot.tsx b/app/web/src/nova/vi/load/load-snapshot.tsx index 3a29ffb2..be9fad1e 100644 --- a/app/web/src/nova/vi/load/load-snapshot.tsx +++ b/app/web/src/nova/vi/load/load-snapshot.tsx @@ -62,9 +62,9 @@ export const applyEnv = async (p: PG) => { w.api = apiProxy(p.site.config.api_url); } - const url = `/prod/${p.site.id}/_prasi/code/index.js?ts=${p.site.code_ts}`; + const url = `/prod/${p.site.id}/_prasi/code/index.js?ts=${p.site_tstamp}`; const fn = new Function("callback", `import("${url}").then(callback)`); - + try { await new Promise((resolve) => { fn((exports: any) => { diff --git a/app/web/src/nova/vi/render/script/local.tsx b/app/web/src/nova/vi/render/script/local.tsx index 202b8119..c8732293 100644 --- a/app/web/src/nova/vi/render/script/local.tsx +++ b/app/web/src/nova/vi/render/script/local.tsx @@ -41,8 +41,8 @@ export const createViLocal = ( const [_, set] = useState({}); const local = ref.current; local.render = () => { - if ((window as any).prasiContext?.render) { - (window as any).prasiContext?.render(); + if ((window as any).prasiContext.render) { + (window as any).prasiContext.render(); } else { set({}); } diff --git a/bun.lockb b/bun.lockb index ab8b61cb..34948720 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 58ce780d..55a7eaf6 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "module": "src/index.ts", "type": "module", "scripts": { - "dev": "bun run ./pkgs/core/index.ts dev", + "dev": "bun run --hot ./pkgs/core/index.ts dev", "clean": "rm -rf data/lmdb && rm -rf app/static && rm -rf app/web/.parcel-cache", "build": "bun run --silent ./pkgs/core/build.ts", "build-site": "bun run --silent ./pkgs/core/build-site.ts", @@ -19,9 +19,6 @@ "app/*", "pkgs/*" ], - "peerDependencies": { - "typescript": "^5.0.0" - }, "dependencies": { "brotli-wasm": "^2.0.1", "fdir": "^6.1.0", diff --git a/pkgs/core/package.json b/pkgs/core/package.json index 0a2835be..f8741137 100644 --- a/pkgs/core/package.json +++ b/pkgs/core/package.json @@ -17,7 +17,6 @@ "pino-pretty": "^10.2.3", "radash": "^11.0.0", "radix3": "^1.1.0", - "typescript": "^5.2.2", "unzipper": "^0.10.14" } } \ No newline at end of file