UNPKG

pranx

Version:

The next of preact. A light-weight, Next.js-like fullstack metaframework

29 lines (28 loc) 14.2 kB
#!/usr/bin/env node var Pe=Object.defineProperty;var i=(t,o)=>Pe(t,"name",{value:o,configurable:!0});function D(t,o=!0){let r=t,s=t.split("/").filter(a=>a!=="/"&&a!=="");if(s.length===0)return r="/",r;let n=[];for(let a of s){if(a.length>=3){if(a.startsWith("(")&&a.endsWith(")"))continue;let u=/^\[\[\.\.\.(.+)\]\]$/.exec(a);if(u){let p=u[1];n.push(`(:${p}*)?`);continue}if(/^\[\.\.\.(.+)\]$/.exec(a)){n.push("*");continue}let c=/^\[(.+)\]$/.exec(a);if(c){n.push(`:${c[1]}`);continue}}n.push(a)}return r=`/${n.join("/")}${o?"/":""}`,r}i(D,"filePathToRoutingPath");import{loadConfig as be}from"c12";var Re={esbuild:{alias:{},define:{},plugins:[]},csr:!0},M=null;async function J(){M=(await be({configFile:"pranx.config",configFileRequired:!0,defaultConfig:Re})).config}i(J,"load_user_pranx_config");async function q(){return M!==null&&await J(),M}i(q,"get_user_pranx_config");import we from"consola";var m=we.create({formatOptions:{}});var re=new Map;function j(t){let o=performance?.now?performance.now():Date.now(),r=re.get(t);return re.set(t,o),r!==void 0?Math.trunc(o-r):void 0}i(j,"measureTime");import R from"fs-extra";import w from"kleur";import{join as y}from"pathe";import{Fragment as me,h as B}from"preact";import{renderToStringAsync as _e}from"preact-render-to-string";import ve from"@mdx-js/esbuild";var $=i(()=>ve({jsx:!1,jsxRuntime:"automatic",jsxImportSource:"preact"}),"mdx_pranx_plugin");import De from"fs-extra";import{basename as je}from"pathe";import{parse as Ae}from"@babel/parser";import Ue from"magic-string";function ne(t,o){let r=Ae(t,{sourceType:"module",plugins:["jsx","classProperties","optionalChaining","typescript"]}),s=new Ue(t),n=[],a=r.program?.body?r.program.body:[];for(let e of a){if(e.type==="ExportNamedDeclaration"&&e.declaration&&e.declaration.type==="FunctionDeclaration"&&e.declaration.id&&o.includes(e.declaration.id.name)){e.start!=null&&e.end!=null&&n.push(e);continue}if(e.type==="FunctionDeclaration"&&e.id&&o.includes(e.id.name)){e.start!=null&&e.end!=null&&n.push(e);continue}if(e.type==="ExportNamedDeclaration"&&e.declaration&&e.declaration.type==="VariableDeclaration"&&o.includes(e.declaration.declarations[0].id.name)){e.start!=null&&e.end!=null&&n.push(e);continue}e.type==="VariableDeclaration"&&o.includes(e.declarations[0].id.name)&&e.start!=null&&e.end!=null&&n.push(e)}let u=n.sort((e,c)=>(c.start??0)-(e.start??0));for(let e of u)e.start!=null&&e.end!=null&&s.remove(e.start,e.end);return s.toString().trim()}i(ne,"remove_top_level_functions");var oe=i(t=>({name:"pranx-strip-server-only-function",setup(o){o.onLoad({filter:/\.[jt]sx?$/},async r=>{if(!["page.js","page.jsx","page.ts","page.tsx"].includes(je(r.path)))return{};let s=await De.readFile(r.path,"utf-8");return{contents:ne(s,t),loader:"tsx"}})}}),"strip_server_only_from_pages_plugin");import{tailwindPlugin as Ie}from"esbuild-plugin-tailwindcss";var F=i((t={})=>Ie({cssModules:{enabled:!0},...t}),"tailwindcss_plugin");import Oe from"esbuild";import{glob as Ne}from"glob";import{join as se}from"pathe";import{join as x}from"pathe";var Y=process.cwd(),X=x(Y,".pranx"),ie=x(X),f=x(ie,"browser"),S=x(ie,"server"),I=x(Y,"src"),N=x(I,"pages"),K=x(Y,"public"),k=x(S,"server.manifest.json"),H=x(f,"site.manifest.json");async function ae(t){let o=await Ne([se(N,"**/*page.{js,ts,tsx,jsx}"),se(I,"entry-client.{tsx,jsx}")],{nodir:!0,absolute:!0});return await Oe.build({entryPoints:o,bundle:!0,outdir:f,format:"esm",sourcemap:!t.optimize,target:"esnext",platform:"browser",keepNames:!0,minify:t.optimize,metafile:!0,chunkNames:"_chunks/[name]-[hash]",assetNames:"_assets/[name]-[hash]",treeShaking:!0,splitting:!0,packages:"bundle",jsx:"automatic",jsxImportSource:"preact",outbase:N,mainFields:["module","main"],conditions:["import","module","require"],alias:{...t.user_config.esbuild?.alias||{},react:"preact/compat","react-dom":"preact/compat"},loader:{".js":"jsx",".jsx":"jsx",".ts":"tsx",".tsx":"tsx",".json":"json",".scss":"css"},define:{...t.user_config.esbuild?.define||{},"window.pranx.csr_enabled":String(t.user_config.csr)},plugins:[oe(["getServerSideProps","getStaticProps","getStaticPaths","getInitialProps","meta"]),$(),F(),...t.user_config.esbuild?.plugins||[]]})}i(ae,"bundle_browser");import Ce from"esbuild";import{glob as Me}from"glob";import{join as le}from"pathe";async function pe(t){let o=await Me([le(N,"**/*{page,route}.{js,ts,tsx,jsx}"),le(I,"entry-server.{tsx,jsx}")],{nodir:!0,absolute:!0});return await Ce.build({entryPoints:o,bundle:!0,outdir:S,format:"esm",sourcemap:!t.optimize,target:"esnext",platform:"node",keepNames:!0,minify:t.optimize,metafile:!0,chunkNames:"_chunks/[name]-[hash]",assetNames:"_assets/[name]-[hash]",treeShaking:!0,splitting:!0,packages:"external",jsx:"automatic",jsxImportSource:"preact",mainFields:["module","main"],conditions:["import","module","require"],outbase:I,alias:{...t.user_config.esbuild?.alias||{},react:"preact/compat","react-dom":"preact/compat"},define:{...t.user_config.esbuild?.define||{}},loader:{".js":"jsx",".jsx":"jsx",".ts":"tsx",".tsx":"tsx",".module.css":"empty",".css":"empty",".json":"json"},plugins:[$(),F(),...t.user_config.esbuild?.plugins||[]]})}i(pe,"bundle_server");var ce="__PRANX_METADATA__";var ue="__PRANX_SCRIPTS__";import{minifySync as $e}from"@swc/html";import{readFileSync as Fe}from"node:fs";var Q="",C=i(({hydrate_data_as_string:t,page_prerendered:o,minify:r=!1,css_links:s=[],critical_css_filepath:n=""})=>{Q||(Q=Fe(n,{encoding:"utf8"}));let a=` <!DOCTYPE html> ${o.replace(ce,`<style>${Q}</style> ${s.map(u=>`<link rel="stylesheet" href="${u}" />`).join(` `)}`).replace(ue,`<script>window.__PRANX_HYDRATE_DATA__=${t}</script> <script type="module" src="/_.._/entry-client.js"></script>`)}`;return r?$e(a,{collapseBooleanAttributes:!0,collapseWhitespaces:"smart",normalizeAttributes:!0,sortAttributes:!0,removeRedundantAttributes:"smart",quotes:!0,selfClosingVoidElements:!1,tagOmission:"keep-head-and-body"}).code:a},"generate_html_template");async function de(){m.log(w.bold().magenta(`Pranx Build `)),j("build_measure_time"),await J(),await R.emptyDir(X);let t=!0,o=await pe({optimize:t,user_config:await q()}),r=await ae({optimize:t,user_config:await q()}),s={entry_server:y(S,"entry-server.js"),global_css_filepath:"",routes:[],api:[]},n=null;n=await import(s.entry_server);let a=y(".pranx","server");for(let[l,h]of Object.entries(o.metafile.outputs)){if(!l.endsWith("route.js"))continue;let d=l.replace(a,""),_=`/${d.replace("route.js","").replace("pages","").split("/").filter(Boolean).join("/")}`,g=y(S,d);s.api.push({path:_,module:d,absolute_module_path:g})}let u=y(".pranx","browser"),e={entry:""};for(let[l,h]of Object.entries(r.metafile.outputs)){if(!l.endsWith(".css"))continue;if(l.endsWith("entry-client.css")){e.entry=y(f,l.replace(u,""));continue}let d=l.replace(u,""),_=`/${d.replace("page.css","").split("/").filter(Boolean).join("/")}`;e[_]=d}s.global_css_filepath=e.entry;for(let[l,h]of Object.entries(r.metafile.outputs)){if(!l.endsWith("page.js"))continue;let d=l.replace(u,""),_=`/${d.replace("page.js","").split("/").filter(Boolean).join("/")}`,g=y(S,"pages",d),{getServerSideProps:T=void 0,getStaticProps:E=void 0,getStaticPaths:A=void 0}=await import(g);T&&(E||A)&&(m.error(` msg: "Only one can be present: getServerSideProps or getStaticProps/getStaticPaths" file: ${g} path: ${_} `),process.exit(1));let P=!T,b=_.includes("["),Z=b?_.split("/").filter(U=>U.startsWith("[")&&U.endsWith("]")).map(U=>U.replace("[","").replace("]","")):[];P&&b&&!A&&(m.error(` msg: "getStaticPaths must be present on static pages with dynamic params" file: ${g} path: ${_} `),process.exit(1)),!P&&b&&!T&&(m.error(` msg: "getServerSideProps must be present on server pages with dynamic params" file: ${g} path: ${_} `),process.exit(1));let O={props:{},revalidate:-1};if(P&&b&&A){let U=await A(),Te=_;s.routes.push({path:_,module:d,props:O.props,rendering_kind:"static",revalidate:O.revalidate||-1,is_dynamic:b,dynamic_params:Z,css:[e[_]||""].filter(Boolean),static_generated_routes:[],absolute_module_path:g});for(let z of U.paths){let Ee=z.params||{},ee=Te.replace(/\[([^\]]+)\]/g,(rt,te)=>Ee[te]||`[${te}]`);ee.includes("[")&&(m.error(` msg: "getStaticPaths did not return all the necessary params" file: ${g} path: ${_} params returned by getStaticPaths: ${JSON.stringify(z.params)} `),process.exit(1));let W={props:{},revalidate:-1};E&&(W=await E({params:z.params})),s.routes.at(-1)?.static_generated_routes.push({path:ee,props:W.props||{},revalidate:W.revalidate||-1})}continue}P&&!b&&E&&(O=await E({params:{}})),s.routes.push({path:_,module:d,props:O.props,rendering_kind:P?"static":"server-side",revalidate:O.revalidate||-1,static_generated_routes:[],is_dynamic:b,dynamic_params:Z,css:[e[_]||""].filter(Boolean),absolute_module_path:g})}let c={entry_css:e.entry,routes:s.routes.map(l=>({module:l.module,path:l.path,props:l.props,rendering_kind:l.rendering_kind,css:l.css,is_dynamic:l.is_dynamic,path_parsed_for_routing:D(l.path,!1),static_generated_routes:l.static_generated_routes.map(h=>({path:h.path,props:h.props}))}))},p=JSON.stringify(c);for(let l of s.routes){if(l.rendering_kind==="server-side")continue;let h=await import(l.absolute_module_path);if(l.static_generated_routes.length>0){for(let T of l.static_generated_routes){let E=await _e(B(n?.default||me,{},B(h.default,T.props,null))),A=C({page_prerendered:E,hydrate_data_as_string:p,minify:t,css_links:l.css,critical_css_filepath:s.global_css_filepath}),P=y(f,T.path,"index.html");await R.ensureDir(y(f,T.path)),await R.writeFile(P,A)}continue}let d=await _e(B(n?.default||me,{},B(h.default,l.props,null))),_=C({page_prerendered:d,hydrate_data_as_string:p,minify:!0,css_links:l.css,critical_css_filepath:s.global_css_filepath}),g=y(f,l.path,"index.html");await R.ensureDir(y(f,l.path)),await R.writeFile(g,_)}await R.writeFile(k,JSON.stringify(s)),await R.writeFile(H,JSON.stringify(c));let v=j("build_measure_time");ke(s.routes),m.success(`Project builded in ${v} ms `)}i(de,"build");function ke(t){m.log(w.bold().blue().underline("Routes"));function o(n){let a={};for(let u of n){let e=u.path==="/"?[]:u.path.split("/").filter(Boolean),c=a;for(let p of e)c.children=c.children||{},c.children[p]=c.children[p]||{},c=c.children[p];c.route=u}return a}i(o,"buildTree");function r(n,a="",u=!0){if(n.route){let e=n.route.rendering_kind==="static"?"\u25CF":w.yellow("\u03BB"),c="";n.route.rendering_kind==="static"&&n.route.static_generated_routes&&n.route.static_generated_routes.length>0&&(c=` (${w.cyan(`${n.route.static_generated_routes.length}`)})`),m.log(`${a}${u?"\u2514-":"\u251C-"} ${e} ${w.white(n.route.path)}${c}`)}if(n.children){let e=Object.keys(n.children);e.forEach((c,p)=>{r(n.children[c],a+(n.route?"| ":""),p===e.length-1)})}}i(r,"printTree");let s=o(t);m.log("."),r(s,"",!0),m.log(` ${w.yellow("\u03BB")} Server-side Page`),m.log("\u25CF Static Page"),m.log(`${w.cyan("(#)")} Count of Static Generated Routes `)}i(ke,"printRoutesTreeForUser");import He from"kleur";async function fe(){m.log(He.bold().magenta("Pranx Dev"))}i(fe,"dev");var ge=i(async(t,o)=>{for(let r of t.api){let{DELETE:s=void 0,GET:n=void 0,HEAD:a=void 0,PATCH:u=void 0,POST:e=void 0,PUT:c=void 0}=await import(r.absolute_module_path),p=D(r.path,!1);n&&o.on("GET",p,n),e&&o.on("POST",p,e),a&&o.on("HEAD",p,a),s&&o.on("DELETE",p,s),u&&o.on("PATCH",p,u),c&&o.on("PUT",p,c)}},"define_api_handlers");import V from"fs-extra";import{serveStatic as Be}from"h3";import{join as G}from"pathe";var L=i(t=>Be(t,{indexNames:["/index.html"],getContents:i(async o=>{let r=G(f,o),s=G(K,o),n=await V.exists(r),a=await V.readFile(n?r:s);return new Uint8Array(a)},"getContents"),getMeta:i(async o=>{let r=G(f,o),s=G(K,o),n=await V.exists(r),a=await V.stat(n?r:s).catch(()=>{});if(a?.isFile())return a},"getMeta"),headers:{"Cache-Control":"public, max-age=2592000, immutable",Expires:new Date(Date.now()+2592e6).toUTCString()}}),"defineServeStaticHandler");import Ve from"fs-extra";import{defineHandler as Ge,html as he}from"h3";import{extname as Le,join as ze,resolve as We}from"pathe";import{Fragment as Je,h as ye}from"preact";import{renderToStringAsync as qe}from"preact-render-to-string";var xe=i(async(t,o)=>{for(let r of t.routes){if(r.rendering_kind!=="server-side")continue;let s=null;s=await import(t.entry_server);let n=We(ze(S,"pages",r.module)),{default:a,getServerSideProps:u}=await import(n),e=await Ve.readJSON(H),c=D(r.path,!1);o.on("GET",c,Ge(async p=>{if(Le(p.url.pathname.split("/").at(-1)||""))return L(p);let v={};if(u&&(v=await u({event:p})),p.res.headers.set("Cache-Control","private, no-cache, no-store, must-revalidate"),p.url.searchParams.get("props")==="only")return{props:v};let l=e.routes.findIndex(_=>_.path===r.path);if(l===-1||!e.routes[l])return m.error(`Route not found in hydrate data: ${r.path}`),p.res.status=500,he(p,"Internal Server Error");e.routes[l].props=v;let h=await qe(ye(s?.default||Je,{},ye(a,v,null))),d=C({page_prerendered:h,hydrate_data_as_string:JSON.stringify(e),minify:!0,css_links:r.css,critical_css_filepath:t.global_css_filepath});return he(p,d)}))}},"define_ssr_handlers");import Ye from"fs-extra";import{H3 as Xe,serve as Ke}from"h3";import Qe from"kleur";async function Se(){j("pranx-start"),m.log(Qe.bold().magenta("Pranx Start"));let t=await Ye.readJSON(k),o=Number(process.env.PORT)||3030,r=new Xe;await xe(t,r),await ge(t,r),r.on("GET","**",n=>L(n)),await Ke(r,{port:o}).ready(),m.success(`Start in ${j("pranx-start")} ms`)}i(Se,"start");import{defineCommand as Ze,runMain as et}from"citty";var tt=Ze({meta:{name:"pranx",version:"0.0.1",description:"The next of preact"},args:{dev:{type:"boolean",description:"run pranx in dev mode"},start:{type:"boolean",description:"start pranx in production mode"},build:{type:"boolean",description:"build and bundle optimize your pranx project"}},async run({args:t}){t._.length>1&&m.error("The args must be only one",`current args: ${t}`),t.build&&await de(),t.dev&&await fe(),t.start&&await Se()}});et(tt);