UNPKG

@jsheaven/astro-client-generator

Version:

Generates TypeScript API client code for your Astro endpoints. No manual `fetch()` code writing anymore.

39 lines (31 loc) 7.14 kB
var Q=Object.create;var P=Object.defineProperty;var G=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var _=Object.getPrototypeOf,K=Object.prototype.hasOwnProperty;var V=(e,t)=>{for(var r in t)P(e,r,{get:t[r],enumerable:!0})},O=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of J(t))!K.call(e,s)&&s!==r&&P(e,s,{get:()=>t[s],enumerable:!(n=G(t,s))||n.enumerable});return e};var X=(e,t,r)=>(r=e!=null?Q(_(e)):{},O(t||!e||!e.__esModule?P(r,"default",{value:e,enumerable:!0}):r,e)),Y=e=>O(P({},"__esModule",{value:!0}),e);var ee={};V(ee,{AstroHttpEndpointMethodNames:()=>I,HttpMethods:()=>R,analyzeHttpMethodsImplemented:()=>S,apiClientGenerator:()=>Z,apiGeneratorOptionsDefaults:()=>D,cleanupInterfce:()=>y,generateClientApis:()=>m,groupByApiRoute:()=>k,lowerCaseFirst:()=>q,parseApiRoutesBaseline:()=>H,parseApiRoutesNaive:()=>w,produceClientApiCode:()=>M,produceClientApiHeaderCode:()=>v,produceClientApiRequestImplCode:()=>F,upperCaseFirst:()=>x,validateConfig:()=>$});module.exports=Y(ee);var b=X(require("fast-glob"),1),h=require("path"),E=require("fs"),C=require("ts-morph"),L=require("chokidar"),D={apiDir:"./src/pages/api",baseUrl:"",outDir:"./src/pages/api-client",tsConfigPath:"./tsconfig.json",site:"http://localhost:3000"},I=["GET","POST","DELETE","PATCH","HEAD","PUT","OPTIONS","ALL"],R=["POST","DELETE","GET","PATCH","HEAD","PUT","OPTIONS"],x=e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}`,q=e=>`${e.charAt(0).toLowerCase()}${e.slice(1)}`,y=e=>e.join(` `).trim(),S=e=>{let t=[];return(e.indexOf("function GET")>-1||e.indexOf("export const GET")>-1)&&t.push("GET"),(e.indexOf("function POST")>-1||e.indexOf("export const POST")>-1)&&t.push("POST"),(e.indexOf("function DELETE")>-1||e.indexOf("export const DELETE")>-1)&&t.push("DELETE"),(e.indexOf("function PATCH")>-1||e.indexOf("export const PATCH")>-1)&&t.push("PATCH"),(e.indexOf("function HEAD")>-1||e.indexOf("export const HEAD")>-1)&&t.push("HEAD"),(e.indexOf("function PUT")>-1||e.indexOf("export const PUT")>-1)&&t.push("PUT"),(e.indexOf("function OPTIONS")>-1||e.indexOf("export const OPTIONS")>-1)&&t.push("OPTIONS"),e.indexOf("function ALL")>-1||e.indexOf("export const ALL")>-1?R:t},$=e=>({...D,...e}),Z=(e=D)=>(e=$(e),{name:"astro-client-generator",hooks:{"astro:server:start":async({address:t})=>{let r=t.family==="IPv4"?`http://${t.address}:${t.port}`:`http://[${t.address}]:${t.port}`;e.site&&e.site!==r&&!e.disableSiteAutoDiscovery&&(console.log(`\u{1F504} Endpoint host has changed to ${r}. Updating site accordingly.`),e.site=r);let n=(0,L.watch)(e.apiDir,{ignoreInitial:!0,ignorePermissionErrors:!0,ignored:/(^|[\/\\])\../});n.on("change",s=>{console.log("\u{1F58A}\uFE0F Endpoint changed: ",s),m(e)}),n.on("add",(s,o)=>{o.ctimeMs!==o.mtimeMs&&(o.ctimeMs+100>Date.now()||(console.log("\u{1F58A}\uFE0F Endpoint added: ",s),m(e)))}),m(e)},"astro:build:setup":async()=>{m(e)}}}),H=(e,t)=>{let r=new C.Project({tsConfigFilePath:t}),n=[];return e.forEach(s=>{let o=r.getSourceFile(s),a=o.getImportDeclarations().map(c=>c.getFullText()),u=[],p="",l="";o.getInterfaces().map(c=>{switch(c.getName()){case"ApiRequest":p=c.getText();break;case"ApiResponse":l=c.getText();break;default:u.push(c.getText())}});let f=o.getTypeAliases().map(c=>c.getText()),d=[];o.getExportSymbols().forEach(c=>{let A=I.indexOf(c.getName().toUpperCase());if(A>-1){let i=I[A];i==="DELETE"?d.push("DELETE"):i==="ALL"?d=R:d.push(i)}}),d.forEach(c=>{n.push({apiRoute:s,imports:a,method:c,requestInterface:p,responseInterface:l,genericInterfaces:u,genericTypes:f})})}),n},w=e=>{let t=[];return e.forEach(r=>{let n=(0,E.readFileSync)(r,{encoding:"utf-8"}),s=n.split(` `),o=[],a=[],u=[],p,l,f,d=0,c=[-1,-1],A=[-1,-1];s.forEach((i,g)=>{let U=/^import (.*) from (.*)/.test(i.trim()),T=i.indexOf("interface ")>-1,N=/^\s*(?:export\s+)?type\s+\w+\s*=\s*[\w<>,\s|]+\s*;?\s*$/.test(i.trim()),j=i.indexOf(" ApiRequest ")>-1,B=i.indexOf(" ApiResponse ")>-1,W=i.indexOf("{")>-1,z=i.indexOf("}")>-1;if(U){o.push(i);return}if(N){u.push(i);return}T&&j&&(l=g),T&&B&&(f=g),T&&l!==g&&f!==g&&(p=g),(l||f||p)&&W&&d++,(l||f||p)&&z&&(d--,d===0&&(l&&(c=[l,g],l=void 0),f&&(A=[f,g],f=void 0),p&&(a.push(y(s.slice(p,g+1))),p=void 0)))}),S(n).forEach(i=>{t.push({apiRoute:r,imports:o,method:i,requestInterface:y(s.slice(c[0],c[1]+1)),responseInterface:y(s.slice(A[0],A[1]+1)),genericInterfaces:a,genericTypes:u})})}),t},k=e=>e.reduce((t,r)=>{let{path:n}=r;return t[n]||(t[n]=[]),t[n].push(r),t},{}),m=e=>{console.log("\u2699\uFE0F Generating endpoint clients..."),e=$(e);let t=(0,h.resolve)(process.cwd(),e.apiDir),r=b.default.sync(`${t}/**/*.ts`),n;switch(e.parser){case"baseline":n=H(r,e.tsConfigPath);break;case"naive":default:n=w(r);break}n=n.filter(a=>a.responseInterface!==""),n.map(a=>(a.relativePath=`api${a.apiRoute.replace(t,"")}`.trim(),a.path=`${(0,h.parse)(a.relativePath).dir}/${(0,h.parse)(a.relativePath).name}`,a.camelCaseName=a.path.replace(/^api/i,"").split("/").map(u=>x(u)).join("").split(/[-_\ \.]/g).reduce((u,p)=>u+x(p),""),a));let s=k(n),o=[];Object.keys(s).forEach(a=>{let u=s[a],p=M(u,e),l=(0,h.resolve)(process.cwd(),e.outDir,u[0].relativePath.replace(/^api\//,"")),f=(0,h.parse)(l),d=f.dir,c=`${d}${h.sep}${f.name}-client.ts`.toLowerCase();o.push(c);let A=new C.Project({tsConfigFilePath:e.tsConfigPath});(0,E.mkdirSync)(d,{recursive:!0});let i=A.createSourceFile(c,p,{overwrite:!0}),g;do g=i.getFullWidth(),i.fixUnusedIdentifiers();while(g!==i.getFullWidth());i.fixMissingImports(),i.organizeImports(),i.formatText(),i.saveSync()}),console.log("\u{1F680} Finished building endpoint clients.")},F=(e,t,r,n,s)=>{let o=s?x(e.method.toLowerCase()):"";return` /** return (await fetch('${t.baseUrl}/${e.path}', { method: '${e.method}', ... })).json() */ export const ${q(e.camelCaseName)}${o} = async(${r}options: RequestOptions = {}): Promise<ApiResponse> => { let requestUrl = '${t.site}${t.baseUrl}/${e.path}' if (options && options.query) { requestUrl += '?' + Object.keys(options.query) .map((key) => key + '=' + options.query![key]) .join('&'); } delete options.query options.method = '${e.method}' ${n} return (await fetch(requestUrl, options)).json() }`},v=(e,t)=>`${e.imports.join(` `)} ${e.genericTypes.join(` `)} ${e.genericInterfaces.join(` `)} export interface QueryMap { [key: string]: string } export interface RequestOptions extends RequestInit { query?: QueryMap } ${t} ${e.responseInterface}`,M=(e,t)=>{let r=e[0].requestInterface?`${e[0].requestInterface}`:"",n=e[0].requestInterface?"payload: ApiRequest, ":"",s="";return e.forEach(o=>{let a=o.method!=="HEAD"&&o.method!=="GET"&&o.requestInterface?"options.body = JSON.stringify(payload)":"";s+=F(o,t,n,a,e.length>1)}),`${v(e[0],r)} ${s}`};0&&(module.exports={AstroHttpEndpointMethodNames,HttpMethods,analyzeHttpMethodsImplemented,apiClientGenerator,apiGeneratorOptionsDefaults,cleanupInterfce,generateClientApis,groupByApiRoute,lowerCaseFirst,parseApiRoutesBaseline,parseApiRoutesNaive,produceClientApiCode,produceClientApiHeaderCode,produceClientApiRequestImplCode,upperCaseFirst,validateConfig}); //# sourceMappingURL=index.cjs.js.map