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) 6.44 kB
(()=>{var v=Object.create;var C=Object.defineProperty;var M=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var N=Object.getPrototypeOf,j=Object.prototype.hasOwnProperty;var m=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,r)=>(typeof require<"u"?require:t)[r]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw new Error('Dynamic require of "'+e+'" is not supported')});var B=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of U(t))!j.call(e,s)&&s!==r&&C(e,s,{get:()=>t[s],enumerable:!(n=M(t,s))||n.enumerable});return e};var W=(e,t,r)=>(r=e!=null?v(N(e)):{},B(t||!e||!e.__esModule?C(r,"default",{value:e,enumerable:!0}):r,e));var R=W(m("fast-glob"),1),h=m("path"),y=m("fs"),I=m("ts-morph"),$=m("chokidar"),O={apiDir:"./src/pages/api",baseUrl:"",outDir:"./src/pages/api-client",tsConfigPath:"./tsconfig.json",site:"http://localhost:3000"},D=["GET","POST","DELETE","PATCH","HEAD","PUT","OPTIONS","ALL"],b=["POST","DELETE","GET","PATCH","HEAD","PUT","OPTIONS"],T=e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}`,z=e=>`${e.charAt(0).toLowerCase()}${e.slice(1)}`,E=e=>e.join(` `).trim(),Q=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?b:t},L=e=>({...O,...e}),Z=(e=O)=>(e=L(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,$.watch)(e.apiDir,{ignoreInitial:!0,ignorePermissionErrors:!0,ignored:/(^|[\/\\])\../});n.on("change",s=>{console.log("\u{1F58A}\uFE0F Endpoint changed: ",s),P(e)}),n.on("add",(s,o)=>{o.ctimeMs!==o.mtimeMs&&(o.ctimeMs+100>Date.now()||(console.log("\u{1F58A}\uFE0F Endpoint added: ",s),P(e)))}),P(e)},"astro:build:setup":async()=>{P(e)}}}),G=(e,t)=>{let r=new I.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=D.indexOf(c.getName().toUpperCase());if(A>-1){let i=D[A];i==="DELETE"?d.push("DELETE"):i==="ALL"?d=b:d.push(i)}}),d.forEach(c=>{n.push({apiRoute:s,imports:a,method:c,requestInterface:p,responseInterface:l,genericInterfaces:u,genericTypes:f})})}),n},J=e=>{let t=[];return e.forEach(r=>{let n=(0,y.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 q=/^import (.*) from (.*)/.test(i.trim()),x=i.indexOf("interface ")>-1,S=/^\s*(?:export\s+)?type\s+\w+\s*=\s*[\w<>,\s|]+\s*;?\s*$/.test(i.trim()),H=i.indexOf(" ApiRequest ")>-1,w=i.indexOf(" ApiResponse ")>-1,k=i.indexOf("{")>-1,F=i.indexOf("}")>-1;if(q){o.push(i);return}if(S){u.push(i);return}x&&H&&(l=g),x&&w&&(f=g),x&&l!==g&&f!==g&&(p=g),(l||f||p)&&k&&d++,(l||f||p)&&F&&(d--,d===0&&(l&&(c=[l,g],l=void 0),f&&(A=[f,g],f=void 0),p&&(a.push(E(s.slice(p,g+1))),p=void 0)))}),Q(n).forEach(i=>{t.push({apiRoute:r,imports:o,method:i,requestInterface:E(s.slice(c[0],c[1]+1)),responseInterface:E(s.slice(A[0],A[1]+1)),genericInterfaces:a,genericTypes:u})})}),t},_=e=>e.reduce((t,r)=>{let{path:n}=r;return t[n]||(t[n]=[]),t[n].push(r),t},{}),P=e=>{console.log("\u2699\uFE0F Generating endpoint clients..."),e=L(e);let t=(0,h.resolve)(process.cwd(),e.apiDir),r=R.default.sync(`${t}/**/*.ts`),n;switch(e.parser){case"baseline":n=G(r,e.tsConfigPath);break;case"naive":default:n=J(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=>T(u)).join("").split(/[-_\ \.]/g).reduce((u,p)=>u+T(p),""),a));let s=_(n),o=[];Object.keys(s).forEach(a=>{let u=s[a],p=X(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 I.Project({tsConfigFilePath:e.tsConfigPath});(0,y.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.")},K=(e,t,r,n,s)=>{let o=s?T(e.method.toLowerCase()):"";return` /** return (await fetch('${t.baseUrl}/${e.path}', { method: '${e.method}', ... })).json() */ export const ${z(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}`,X=(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+=K(o,t,n,a,e.length>1)}),`${V(e[0],r)} ${s}`};})(); //# sourceMappingURL=index.iife.js.map