@visulima/api-platform
Version:
Visulima API platform is a set of tools to build and consume web APIs
6 lines • 11.1 kB
JavaScript
import {execSync}from'child_process';import {existsSync,statSync,rmSync,readdirSync,readFileSync}from'fs';import te,{cwd}from'process';import {normalize,basename,join,extname,resolve,parse,toNamespacedPath}from'@visulima/path';import h from'chalk';import {parseFile,jsDocumentCommentsToOpenApi,swaggerJsDocumentCommentsToOpenApi}from'@visulima/jsdoc-open-api';import {stat,readdir,realpath}from'fs/promises';import {toPath}from'@visulima/path/utils';var se=(e,t)=>{if(t.length===0)throw new Error("must include at least one key to map");let r=e.toString();return t.forEach(o=>{r=o.optional?r.replace("(?:\\/([^\\/]+?))?\\",`/:${o.name}?`):r.replace("(?:([^\\/]+?))",`:${o.name}`);}),r.replace("/?(?=\\/|$)/i","").replace("/^","").replaceAll("\\","").replaceAll(/\/{2,}/gu,"/")},O=se;var ne=(e,t)=>{if(typeof e=="string")return e;if(e.fast_slash)return "";if(e.fast_star)return "*";let r="";t.length>0&&(r=O(e,t));let o=/^\/\^((?:\\[$()*+./?[\\\]^{|}]|[^$()*+./?[\\\]^{|}])*)\$\//u.exec(e.toString().replace("\\/?","").replace("(?=\\/|$)","$"));return Array.isArray(o)&&o.length>1?o[1].replaceAll(/\\(.)/gu,"$1").slice(1):r?r.slice(1):e.toString()},j=ne;var ae=(e,t,r)=>{let o=e.route.stack.at(-1),s=t.map(i=>({in:"path",name:i.name,required:!i.optional})),n=e.route.stack.filter(i=>i.handle.metadata);if(n.length>1)throw new Error("Only one metadata middleware is allowed per route");let l=(r+e.route.path).replaceAll(/\/{2,}/gu,"/");return n.length===0?{method:o.method,path:l,pathParams:s}:{metadata:n[0].handle.metadata,method:o.method,path:l,pathParams:s}},$=(e,t,r,o)=>{if(o=[...o,...r.keys],r.name==="router"&&r.handle?.stack!==void 0)for(let s of r.handle.stack)t=t||"",$(e,`${t}/${j(r.regexp,r.keys)}`,s,o);!r.route||r.route.stack.length===0||e.push(ae(r,o,t));},ie=e=>{let t=e._router||e.router,r=[];for(let o of t.stack)$(r,"",o,[]);return r},L=ie;var pe=e=>{let t=[];return L(e).forEach(r=>{t.push({file:"unknown",method:r.method.toUpperCase(),path:r.path,tags:[]});}),t},I=pe;var C=e=>e.replaceAll(/ \(.*\)/gu,"").trim(),N=e=>e.trim().split(" ")[1].slice(1,-1),le=e=>{let r=e.printRoutes().replaceAll(/[─│└├]/gu," ").trimEnd().split(`
`),o=r.reduce((n,l,i)=>{let p=C(l);if(C(r[i-1]??"")===p){let k=n.filter(_=>_.index<i&&_.segment===p),{methods:F}=k.at(-1);return F!==null&&F.push(N(l)),n}let c=l.replaceAll(/ \(.*\)/gu,"").match(/ /gu);if(c===null)throw new Error("Invalid spaces");let a=c.length/4,d=l.includes("("),R=d?[N(l)]:null;return n.push({depth:a,index:i,isRoute:d,methods:R,segment:p}),n},[]),s=[];return o.filter(n=>n.isRoute).forEach(n=>{let i=[...o.filter(p=>p.index<n.index&&p.depth<n.depth).filter((p,f,c)=>!c.find(a=>a.depth===p.depth&&a.index>p.index)).map(p=>p.segment),n.segment].join("");if(n.methods===null)throw new Error("Invalid methods");n.methods.forEach(p=>{s.push({file:"unknown",method:p.toUpperCase(),path:i,tags:[]});});}),s},M=le;var ce=e=>{let r=e._core.router.routes,o=[];return [...r.keys()].forEach(s=>{r.get(s).routes.forEach(n=>{o.push({file:"unknown",method:n.route.method.toUpperCase(),path:n.path,tags:[]});});}),o},K=ce;var fe=e=>{let t=[];return e.middleware.filter(r=>r.router).flatMap(r=>r.router.stack).forEach(r=>{t.push({file:"unknown",method:r.methods.join("|").toUpperCase(),path:r.path,tags:[]});}),t},W=fe;var J=/\.(js|ts|mjs|cjs)$/u,ge=(e,t,r=false)=>{e=toNamespacedPath(e);let o=toNamespacedPath(cwd()),s=[],n=parseFile(e,jsDocumentCommentsToOpenApi,r);s=[...s,...n.map(p=>p.spec)];let l=parseFile(e,swaggerJsDocumentCommentsToOpenApi,r);s=[...s,...l.map(p=>p.spec)];let i=[];return s.length===0?(readFileSync(e,"utf8").split(/\r?\n/u).forEach(f=>{let c=/[=aces|]+\s["'|](GET|POST|PUT|PATCH|HEAD|DELETE|OPTIONS)["'|]/u.exec(f);if(c){let[,a]=c;a==="GET"&&(a="GET|HEAD"),i.push({file:e.replace(`${o}/`,""),method:a,path:toNamespacedPath(e.replace(t,"").replace(J,"")),tags:[]});}}),i.length===0&&i.push({file:e.replace(`${o}/`,""),method:"GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS",path:toNamespacedPath(e.replace(t,"").replace(J,"")),tags:[]}),i):(s.forEach(p=>{Object.entries(p?.paths??{}).forEach(([c,a])=>{Object.entries(a).forEach(([R,k])=>{i.push({file:e.replace(`${o}/`,""),method:R.toUpperCase(),path:toNamespacedPath(c),tags:k.tags});});});}),i)},U=ge;var ye=Object.defineProperty,xe=(e,t)=>ye(e,"name",{value:t,configurable:true}),G=xe(e=>{if(!e||!(e instanceof URL)&&typeof e!="string")throw new TypeError("Path must be a non-empty string or URL.")},"assertValidFileOrDirectoryPath");var Ee=Object.defineProperty,we=(e,t)=>Ee(e,"name",{value:t,configurable:true}),x=class extends Error{static{we(this,"WalkError");}root;constructor(t,r){super(`${t instanceof Error?t.message:t} for path "${r}"`),this.cause=t,this.root=r;}get name(){return "WalkError"}set name(t){throw new Error("Cannot overwrite name of WalkError")}};var Se=Object.defineProperty,Pe=(e,t)=>Se(e,"name",{value:t,configurable:true}),A=Pe(e=>{let t=e.replace(/\.\*/g,".([^/]*)").replace(/\*\*/g,"(.*)").replace(/(?<!\.)\*(?!\*)/g,"([^/]*)").replace(/\?/g,"[^/]").replace(/\.(?!\*)/g,"\\.").replace(/\{/g,"(").replace(/\}/g,")").replace(/,/g,"|").replace(/\[!(.*?)\]/g,"[^$1]");return new RegExp(`^${t}$`)},"globToRegExp"),Re=Object.defineProperty,ke=(e,t)=>Re(e,"name",{value:t,configurable:true}),E=ke((e,t,r,o)=>Array.isArray(t)&&t.length>0&&!t.some(s=>e.endsWith(s))||r&&!r.some(s=>s.test(e))?false:!o?.some(s=>s.test(e)),"walkInclude");var je=Object.defineProperty,g=(e,t)=>je(e,"name",{value:t,configurable:true}),$e=g(async e=>{let t=normalize(e),r=basename(t),o=await stat(t);return {isDirectory:g(()=>o.isDirectory(),"isDirectory"),isFile:g(()=>o.isFile(),"isFile"),isSymbolicLink:g(()=>o.isSymbolicLink(),"isSymbolicLink"),name:r,path:t}},"_createWalkEntry");async function*S(e,{extensions:t,followSymlinks:r=false,includeDirs:o=true,includeFiles:s=true,includeSymlinks:n=true,match:l,maxDepth:i=Number.POSITIVE_INFINITY,skip:p}={}){if(G(e),i<0)return;let f=l?l.map(a=>typeof a=="string"?A(a):a):void 0,c=p?p.map(a=>typeof a=="string"?A(a):a):void 0;if(e=resolve(toPath(e)),o&&E(e,t,f,c)&&(yield await $e(e)),!(i<1||!E(e,void 0,void 0,c)))try{for await(let a of await readdir(e,{withFileTypes:!0})){let d=join(e,a.name);if(a.isSymbolicLink())if(r)d=await realpath(d);else if(n&&E(d,t,f,c))yield {isDirectory:a.isDirectory,isFile:a.isFile,isSymbolicLink:a.isSymbolicLink,name:a.name,path:d};else continue;a.isSymbolicLink()||a.isDirectory()?yield*S(d,{extensions:t,followSymlinks:r,includeDirs:o,includeFiles:s,includeSymlinks:n,match:f,maxDepth:i-1,skip:c}):a.isFile()&&s&&E(d,t,f,c)&&(yield {isDirectory:g(()=>a.isDirectory(),"isDirectory"),isFile:g(()=>a.isFile(),"isFile"),isSymbolicLink:g(()=>a.isSymbolicLink(),"isSymbolicLink"),name:a.name,path:d});}}catch(a){throw a instanceof x?a:new x(a,e)}}g(S,"walk");var Le=Object.defineProperty,Ie=(e,t)=>Le(e,"name",{value:t,configurable:true}),b=Ie(async(e,t={})=>{Array.isArray(t.extensions)||(t.extensions=["js","mjs","cjs","ts"]);let r=[];for await(let o of S(e,t))r.push(o.path);return r},"collect");var P=[".js",".ts",".mjs",".cjs"],q=e=>{let t=parse(e);for(;t.base&&t.root!==t.dir;){if(readdirSync(t.dir).find(s=>s==="package.json"))return t.dir;t=parse(t.dir);}return null},V=e=>{let t=`${e}/package.json`,{dependencies:r}=JSON.parse(readFileSync(t).toString());return r?.express?"express":r?.koa&&(r["@koa/router"]||r["koa-router"])?"koa":r?.next?"next":r?.["@hapi/hapi"]?"hapi":r?.fastify?"fastify":null},X=(e,t)=>Object.keys(e).length===0?null:t==="hapi"?typeof e.app.app=="string"?e.app:e:e.app??e;var Y=e=>{try{return statSync(e).isDirectory()}catch{return false}},Ke=async(e="")=>{let t=join(e,"pages/api");return !Y(t)&&(t=join(e,"src/pages/api"),!Y(t))?[]:b(t,{extensions:P,includeDirs:false})},Q=Ke;var D=async(e,t,r)=>{if(t==="express")return I(e);if(t==="koa")return W(e);if(t==="hapi")return K(e);if(t==="fastify")return M(e);if(t==="next"){let o=await Q(e);if(o.length===0)throw new Error(`No API routes found, in "${e}".`);return o.flatMap(s=>U(s,e,r))}return null};var We=(e,t)=>{let r=new Map;return e.forEach(o=>{let s=t(o),n=r.get(s);n?n.push(o):r.set(s,[o]);}),r},Z=We;var He=(e,t)=>{let r={ANY:h.redBright,DELETE:h.redBright,GET:h.blue,HEAD:h.hex("#6C7280"),OPTIONS:h.hex("#6C7280"),PATCH:h.yellow,POST:h.yellow,PUT:h.yellow},o;if(e==="GET|HEAD")o=`${h.blue("GET")}${h.grey("|HEAD")}`;else {let f=r[e](e);o=e==="GET"?`${f}${h.grey("|HEAD")}`:f;}let s=e==="GET"?6:14-e.length,n=Array.from({length:s}).fill(" ").join(""),l=process.stdout.columns-16-t.length-4,i=l>0?Array.from({length:l}).fill(".").join(""):"",p=t.split("/").map(f=>[":","["].includes(f[0]??"")?h.yellowBright(f):f).join("/");return ` ${o}${n}${p}${h.grey(i)}`},Je=(e,t={})=>e.map(r=>{if(!(Array.isArray(t.methods)&&t.methods.includes(r.method)))return r.method==="GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS"&&(r.method="ANY"),He(r.method,r.path.replace("/pages",""))}).filter(Boolean),T=Je;var Be=async(e,t,r={})=>{let o=join(te.cwd(),t);if(!existsSync(o))throw new Error("No such file, invalid path provided.");let s=q(o);if(!s)throw new Error("Please initialize local package.json.");if(e===void 0){let l=V(s);if(!l)throw new Error("Couldn't detect supported back-end framework.");e=l;}let n=null;if(e==="next")n=await D(o,"next",r.verbose??false);else {if(!statSync(o).isFile())throw new Error(`${o} is directory, but file expected.`);if(!P.includes(extname(o)))throw new Error("Please specify application .ts/.js/.mjs/.cjs file.");let l=`${s}/.env`;existsSync(l)&&(await import(`${s}/node_modules/dotenv/lib/main.js`)).config({path:l});let i=extname(o)===".ts",p=join(s,"node_modules/.bin/tsc");if(i&&!existsSync(p))throw new Error(`Please install typescript in ${s}`);try{if(i)try{execSync(`${p} --outDir framework-list >&2`,{cwd:s});}catch(a){console.log(`TSC compilation failed. Please resolve issues in your project.
`),console.log(a),rmSync(join(s,"framework-list"),{recursive:!0});}let f=i?join(s,"framework-list",o.replace(s,"").replace(".ts",".js")):o,{default:c}=await import(f);n=await D(["AsyncFunction","Function"].includes(c.constructor.name)?await c():X(c,e),e,r.verbose??!1);}finally{i&&rmSync(join(s,"framework-list"),{recursive:true});}}if(n===null)throw new Error(`Framework "${e}" is not supported.`);if(Array.isArray(r.includePaths)&&r.includePaths.length>0&&(n=r.includePaths.flatMap(l=>n.filter(i=>i.path.startsWith(l)))),Array.isArray(r.excludePaths)&&r.excludePaths.length>0&&(n=r.excludePaths.flatMap(l=>n.filter(i=>!i.path.startsWith(l)))),typeof r.group=="string"&&r.group!==""){console.log();let l=Z(n,p=>r.group==="path"?p.path.replace("/pages","").split("/")[1]:p.tags[0]??"unsorted"),i=0;l.forEach((p,f)=>{i>0&&console.log();let c=(te.stdout.columns-16-f.length)/2,a=c>0?Array.from({length:c}).fill(" ").join(""):"";console.log(a+h.bold.underline(f)),T(p,r).forEach(d=>{console.log(d);}),i+=1;});}else console.log(),T(n,r).forEach(l=>{console.log(l);});console.log(`
Listed ${h.greenBright(String(n.length))} HTTP ${n.length===1?"route":"routes"}.
`);},dr=Be;export{dr as a};//# sourceMappingURL=chunk-4WOECARR.mjs.map
//# sourceMappingURL=chunk-4WOECARR.mjs.map