@jsheaven/astro-client-generator
Advanced tools
Comparing version 1.0.10 to 1.1.0
#!/usr/bin/env node | ||
"use strict"; | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __export = (target, all) => { | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __copyProps = (to, from, except, desc) => { | ||
if (from && typeof from === "object" || typeof from === "function") { | ||
for (let key of __getOwnPropNames(from)) | ||
if (!__hasOwnProp.call(to, key) && key !== except) | ||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
} | ||
return to; | ||
}; | ||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
// If the importer is in node compatibility mode or this is not an ESM | ||
// file that has been converted to a CommonJS file using a Babel- | ||
// compatible transform (i.e. "__esModule" has not been set), then set | ||
// "default" to the CommonJS "module.exports" for node compatibility. | ||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||
mod | ||
)); | ||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
"use strict";var J=Object.create;var P=Object.defineProperty;var Q=Object.getOwnPropertyDescriptor;var _=Object.getOwnPropertyNames;var z=Object.getPrototypeOf,K=Object.prototype.hasOwnProperty;var W=(e,t)=>{for(var r in t)P(e,r,{get:t[r],enumerable:!0})},R=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of _(t))!K.call(e,s)&&s!==r&&P(e,s,{get:()=>t[s],enumerable:!(o=Q(t,s))||o.enumerable});return e};var I=(e,t,r)=>(r=e!=null?J(z(e)):{},R(t||!e||!e.__esModule?P(r,"default",{value:e,enumerable:!0}):r,e)),X=e=>R(P({},"__esModule",{value:!0}),e);var de={};W(de,{Commands:()=>k,cli:()=>N,resolveArgs:()=>U});module.exports=X(de);var i=I(require("kleur/colors"),1),H=I(require("yargs-parser"),1);var $=I(require("fast-glob"),1),m=require("path"),y=require("fs"),L=require("ts-morph"),x={apiDir:"./src/pages/api",baseUrl:"",outDir:"./src/pages/api-client",tsConfigPath:"./tsconfig.json",site:"http://localhost:3000"},b=["GET","POST","DELETE","PATCH","HEAD","PUT","OPTIONS","ALL"],w=["POST","DELETE","GET","PATCH","HEAD","PUT","OPTIONS"],C=e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}`,Y=e=>`${e.charAt(0).toLowerCase()}${e.slice(1)}`,O=e=>e.join(` | ||
`).trim(),Z=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?w:t},ee=e=>({...x,...e});var te=(e,t)=>{let r=new L.Project({tsConfigFilePath:t}),o=[];return e.forEach(s=>{let n=r.getSourceFile(s),c=n.getImportDeclarations().map(a=>a.getFullText()),g=[],f="",l="";n.getInterfaces().map(a=>{switch(a.getName()){case"ApiRequest":f=a.getText();break;case"ApiResponse":l=a.getText();break;default:g.push(a.getText())}});let u=n.getTypeAliases().map(a=>a.getText()),d=[];n.getExportSymbols().forEach(a=>{let A=b.indexOf(a.getName().toUpperCase());if(A>-1){let p=b[A];p==="DELETE"?d.push("DELETE"):p==="ALL"?d=w:d.push(p)}}),d.forEach(a=>{o.push({apiRoute:s,imports:c,method:a,requestInterface:f,responseInterface:l,genericInterfaces:g,genericTypes:u})})}),o},re=e=>{let t=[];return e.forEach(r=>{let o=(0,y.readFileSync)(r,{encoding:"utf-8"}),s=o.split(` | ||
`),n=[],c=[],g=[],f,l,u,d=0,a=[-1,-1],A=[-1,-1];s.forEach((p,h)=>{let F=/^import (.*) from (.*)/.test(p.trim()),T=p.indexOf("interface ")>-1,j=/^\s*(?:export\s+)?type\s+\w+\s*=\s*[\w<>,\s|]+\s*;?\s*$/.test(p.trim()),M=p.indexOf(" ApiRequest ")>-1,G=p.indexOf(" ApiResponse ")>-1,B=p.indexOf("{")>-1,V=p.indexOf("}")>-1;if(F){n.push(p);return}if(j){g.push(p);return}T&&M&&(l=h),T&&G&&(u=h),T&&l!==h&&u!==h&&(f=h),(l||u||f)&&B&&d++,(l||u||f)&&V&&(d--,d===0&&(l&&(a=[l,h],l=void 0),u&&(A=[u,h],u=void 0),f&&(c.push(O(s.slice(f,h+1))),f=void 0)))}),Z(o).forEach(p=>{t.push({apiRoute:r,imports:n,method:p,requestInterface:O(s.slice(a[0],a[1]+1)),responseInterface:O(s.slice(A[0],A[1]+1)),genericInterfaces:c,genericTypes:g})})}),t},oe=e=>e.reduce((t,r)=>{let{path:o}=r;return t[o]||(t[o]=[]),t[o].push(r),t},{}),S=e=>{e=ee(e);let t=(0,m.resolve)(process.cwd(),e.apiDir),r=$.default.sync(`${t}/**/*.ts`),o;switch(e.parser){case"baseline":o=te(r,e.tsConfigPath);break;case"naive":default:o=re(r);break}o=o.filter(n=>n.responseInterface!==""),o.map(n=>(n.relativePath=`api${n.apiRoute.replace(t,"")}`.trim(),n.path=`${(0,m.parse)(n.relativePath).dir}/${(0,m.parse)(n.relativePath).name}`,n.camelCaseName=n.path.replace(/^api/i,"").split("/").map(c=>C(c)).join("").split(/[-_\ \.]/g).reduce((c,g)=>c+C(g),""),n));let s=oe(o);Object.keys(s).forEach(n=>{let c=s[n],g=ie(c,e),f=(0,m.resolve)(process.cwd(),e.outDir,c[0].relativePath.replace(/^api\//,"")),l=(0,m.parse)(f),u=l.dir,d=`${u}${m.sep}${l.name}-client.ts`;(0,y.mkdirSync)(u,{recursive:!0}),(0,y.writeFileSync)(d.toLowerCase(),g,{encoding:"utf-8"})})},ne=(e,t,r,o,s)=>{let n=s?C(e.method.toLowerCase()):"";return` | ||
// src/cli.ts | ||
var cli_exports = {}; | ||
__export(cli_exports, { | ||
Commands: () => Commands, | ||
cli: () => cli, | ||
resolveArgs: () => resolveArgs | ||
}); | ||
module.exports = __toCommonJS(cli_exports); | ||
var colors = __toESM(require("kleur/colors"), 1); | ||
var import_yargs_parser = __toESM(require("yargs-parser"), 1); | ||
// src/index.ts | ||
var import_fast_glob = __toESM(require("fast-glob"), 1); | ||
var import_path = require("path"); | ||
var import_fs = require("fs"); | ||
var import_ts_morph = require("ts-morph"); | ||
var apiGeneratorOptionsDefaults = { | ||
apiDir: "./src/pages/api", | ||
baseUrl: "", | ||
outDir: "./src/pages/api-client", | ||
tsConfigPath: "./tsconfig.json", | ||
site: "http://localhost:3000" | ||
}; | ||
var AstroHttpEndpointMethodNames = ["GET", "POST", "DEL", "PATCH", "HEAD", "PUT", "OPTIONS", "ALL"]; | ||
var HttpMethods = ["POST", "DELETE", "GET", "PATCH", "HEAD", "PUT", "OPTIONS"]; | ||
var upperCaseFirst = (text) => `${text.charAt(0).toUpperCase()}${text.slice(1)}`; | ||
var lowerCaseFirst = (text) => `${text.charAt(0).toLowerCase()}${text.slice(1)}`; | ||
var cleanupInterfce = (codeLines) => codeLines.join("\n").trim(); | ||
var analyzeHttpMethodsImplemented = (code) => { | ||
const methods = []; | ||
if (code.indexOf("function get") > -1 || code.indexOf("export const get") > -1) { | ||
methods.push("GET"); | ||
} | ||
if (code.indexOf("function post") > -1 || code.indexOf("export const post") > -1) { | ||
methods.push("POST"); | ||
} | ||
if (code.indexOf("function del") > -1 || code.indexOf("export const del") > -1) { | ||
methods.push("DELETE"); | ||
} | ||
if (code.indexOf("function patch") > -1 || code.indexOf("export const patch") > -1) { | ||
methods.push("PATCH"); | ||
} | ||
if (code.indexOf("function head") > -1 || code.indexOf("export const head") > -1) { | ||
methods.push("HEAD"); | ||
} | ||
if (code.indexOf("function put") > -1 || code.indexOf("export const put") > -1) { | ||
methods.push("PUT"); | ||
} | ||
if (code.indexOf("function options") > -1 || code.indexOf("export const options") > -1) { | ||
methods.push("OPTIONS"); | ||
} | ||
if (code.indexOf("function all") > -1 || code.indexOf("export const all") > -1) { | ||
return HttpMethods; | ||
} | ||
return methods; | ||
}; | ||
var validateConfig = (apiGeneratorOptions) => ({ | ||
...apiGeneratorOptionsDefaults, | ||
...apiGeneratorOptions | ||
}); | ||
var parseApiRoutesBaseline = (apiRoutes, tsConfigPath) => { | ||
const project = new import_ts_morph.Project({ | ||
tsConfigFilePath: tsConfigPath | ||
}); | ||
const apiRouteParseResults = []; | ||
apiRoutes.forEach((apiRoute) => { | ||
const sourceFile = project.getSourceFile(apiRoute); | ||
const imports = sourceFile.getImportDeclarations().map((d) => d.getFullText()); | ||
const genericInterfaces = []; | ||
let requestInterface = ""; | ||
let responseInterface = ""; | ||
sourceFile.getInterfaces().map((d) => { | ||
switch (d.getName()) { | ||
case "ApiRequest": | ||
requestInterface = d.getText(); | ||
break; | ||
case "ApiResponse": | ||
responseInterface = d.getText(); | ||
break; | ||
default: | ||
genericInterfaces.push(d.getText()); | ||
} | ||
}); | ||
const genericTypes = sourceFile.getTypeAliases().map((d) => d.getText()); | ||
let methods = []; | ||
sourceFile.getExportSymbols().forEach((d) => { | ||
const methodIndex = AstroHttpEndpointMethodNames.indexOf(d.getName().toUpperCase()); | ||
if (methodIndex > -1) { | ||
const astroMethod = AstroHttpEndpointMethodNames[methodIndex]; | ||
if (astroMethod === "DEL") { | ||
methods.push("DELETE"); | ||
} else if (astroMethod === "ALL") { | ||
methods = HttpMethods; | ||
} else { | ||
methods.push(astroMethod); | ||
} | ||
} | ||
}); | ||
methods.forEach((method) => { | ||
apiRouteParseResults.push({ | ||
apiRoute, | ||
imports, | ||
method, | ||
requestInterface, | ||
responseInterface, | ||
genericInterfaces, | ||
genericTypes | ||
}); | ||
}); | ||
}); | ||
return apiRouteParseResults; | ||
}; | ||
var parseApiRoutesNaive = (apiRoutes) => { | ||
const apiRouteParseResults = []; | ||
apiRoutes.forEach((apiRoute) => { | ||
const impl = (0, import_fs.readFileSync)(apiRoute, { encoding: "utf-8" }); | ||
const codeLines = impl.split("\n"); | ||
const imports = []; | ||
const genericInterfaces = []; | ||
const genericTypes = []; | ||
let genericInterfaceDeclBlockStartLine; | ||
let apiRequestDeclBlockStartLine; | ||
let apiResponseDeclBlockStartLine; | ||
let interfaceDeclBlockIndent = 0; | ||
let requestInterfaceCode = [-1, -1]; | ||
let responseInterfaceCode = [-1, -1]; | ||
codeLines.forEach((line, i) => { | ||
const isImport = /^import (.*) from (.*)/.test(line.trim()); | ||
const isInterfaceDeclLine = line.indexOf("interface ") > -1; | ||
const isTypeDeclLine = /^\s*(?:export\s+)?type\s+\w+\s*=\s*[\w<>,\s|]+\s*;?\s*$/.test(line.trim()); | ||
const isApiRequestDeclLine = line.indexOf(" ApiRequest ") > -1; | ||
const isApiResponseDeclLine = line.indexOf(" ApiResponse ") > -1; | ||
const isOpeningBracketLine = line.indexOf("{") > -1; | ||
const isClosingBracketLine = line.indexOf("}") > -1; | ||
if (isImport) { | ||
imports.push(line); | ||
return; | ||
} | ||
if (isTypeDeclLine) { | ||
genericTypes.push(line); | ||
return; | ||
} | ||
if (isInterfaceDeclLine && isApiRequestDeclLine) { | ||
apiRequestDeclBlockStartLine = i; | ||
} | ||
if (isInterfaceDeclLine && isApiResponseDeclLine) { | ||
apiResponseDeclBlockStartLine = i; | ||
} | ||
if (isInterfaceDeclLine && apiRequestDeclBlockStartLine !== i && apiResponseDeclBlockStartLine !== i) { | ||
genericInterfaceDeclBlockStartLine = i; | ||
} | ||
if ((apiRequestDeclBlockStartLine || apiResponseDeclBlockStartLine || genericInterfaceDeclBlockStartLine) && isOpeningBracketLine) { | ||
interfaceDeclBlockIndent++; | ||
} | ||
if ((apiRequestDeclBlockStartLine || apiResponseDeclBlockStartLine || genericInterfaceDeclBlockStartLine) && isClosingBracketLine) { | ||
interfaceDeclBlockIndent--; | ||
if (interfaceDeclBlockIndent === 0) { | ||
if (apiRequestDeclBlockStartLine) { | ||
requestInterfaceCode = [apiRequestDeclBlockStartLine, i]; | ||
apiRequestDeclBlockStartLine = void 0; | ||
} | ||
if (apiResponseDeclBlockStartLine) { | ||
responseInterfaceCode = [apiResponseDeclBlockStartLine, i]; | ||
apiResponseDeclBlockStartLine = void 0; | ||
} | ||
if (genericInterfaceDeclBlockStartLine) { | ||
genericInterfaces.push(cleanupInterfce(codeLines.slice(genericInterfaceDeclBlockStartLine, i + 1))); | ||
genericInterfaceDeclBlockStartLine = void 0; | ||
} | ||
} | ||
} | ||
}); | ||
analyzeHttpMethodsImplemented(impl).forEach((method) => { | ||
apiRouteParseResults.push({ | ||
apiRoute, | ||
imports, | ||
method, | ||
requestInterface: cleanupInterfce(codeLines.slice(requestInterfaceCode[0], requestInterfaceCode[1] + 1)), | ||
responseInterface: cleanupInterfce(codeLines.slice(responseInterfaceCode[0], responseInterfaceCode[1] + 1)), | ||
genericInterfaces, | ||
genericTypes | ||
}); | ||
}); | ||
}); | ||
return apiRouteParseResults; | ||
}; | ||
var groupByApiRoute = (routes) => { | ||
return routes.reduce((acc, file) => { | ||
const { path } = file; | ||
if (!acc[path]) { | ||
acc[path] = []; | ||
} | ||
acc[path].push(file); | ||
return acc; | ||
}, {}); | ||
}; | ||
var generateClientApis = (apiGeneratorOptions) => { | ||
apiGeneratorOptions = validateConfig(apiGeneratorOptions); | ||
const apiFolder = (0, import_path.resolve)(process.cwd(), apiGeneratorOptions.apiDir); | ||
const apiRoutes = import_fast_glob.default.sync(`${apiFolder}/**/*.ts`); | ||
let apiRouteParseResults; | ||
switch (apiGeneratorOptions.parser) { | ||
case "baseline": | ||
apiRouteParseResults = parseApiRoutesBaseline(apiRoutes, apiGeneratorOptions.tsConfigPath); | ||
break; | ||
case "naive": | ||
default: | ||
apiRouteParseResults = parseApiRoutesNaive(apiRoutes); | ||
break; | ||
} | ||
apiRouteParseResults = apiRouteParseResults.filter((apiRoute) => apiRoute.responseInterface !== ""); | ||
apiRouteParseResults.map((result) => { | ||
result.relativePath = `api${result.apiRoute.replace(apiFolder, "")}`.trim(); | ||
result.path = `${(0, import_path.parse)(result.relativePath).dir}/${(0, import_path.parse)(result.relativePath).name}`; | ||
result.camelCaseName = result.path.replace(/^api/i, "").split("/").map((part) => upperCaseFirst(part)).join("").split(/[-_\ \.]/g).reduce((prev, current) => prev + upperCaseFirst(current), ""); | ||
return result; | ||
}); | ||
const perRoute = groupByApiRoute(apiRouteParseResults); | ||
Object.keys(perRoute).forEach((apiRoute) => { | ||
const parseResults = perRoute[apiRoute]; | ||
const clientCode = produceClientApiCode(parseResults, apiGeneratorOptions); | ||
const clientFilePath = (0, import_path.resolve)( | ||
process.cwd(), | ||
apiGeneratorOptions.outDir, | ||
parseResults[0].relativePath.replace(/^api\//, "") | ||
); | ||
const parsed = (0, import_path.parse)(clientFilePath); | ||
const clientFileDir = parsed.dir; | ||
const finalFilePath = `${clientFileDir}${import_path.sep}${parsed.name}-client.ts`; | ||
(0, import_fs.mkdirSync)(clientFileDir, { recursive: true }); | ||
(0, import_fs.writeFileSync)(finalFilePath.toLowerCase(), clientCode, { encoding: "utf-8" }); | ||
}); | ||
}; | ||
var produceClientApiRequestImplCode = (parseResult, apiGeneratorOptions, requestParamDecl, bodyInst, hasMultipleEndpointsInOneFile) => { | ||
const methodNameAppendix = hasMultipleEndpointsInOneFile ? upperCaseFirst(parseResult.method.toLowerCase()) : ""; | ||
return ` | ||
/** return (await fetch('${apiGeneratorOptions.baseUrl}/${parseResult.path}', { method: '${parseResult.method}', ... })).json() */ | ||
export const ${lowerCaseFirst( | ||
parseResult.camelCaseName | ||
)}${methodNameAppendix} = async(${requestParamDecl}options: RequestOptions = {}): Promise<ApiResponse> => { | ||
let requestUrl = '${apiGeneratorOptions.site}${apiGeneratorOptions.baseUrl}/${parseResult.path}' | ||
/** return (await fetch('${t.baseUrl}/${e.path}', { method: '${e.method}', ... })).json() */ | ||
export const ${Y(e.camelCaseName)}${n} = async(${r}options: RequestOptions = {}): Promise<ApiResponse> => { | ||
let requestUrl = '${t.site}${t.baseUrl}/${e.path}' | ||
if (options && options.query) { | ||
@@ -279,13 +15,13 @@ requestUrl += '?' + Object.keys(options.query) | ||
delete options.query | ||
options.method = '${parseResult.method}' | ||
${bodyInst} | ||
options.method = '${e.method}' | ||
${o} | ||
return (await fetch(requestUrl, options)).json() | ||
}`; | ||
}; | ||
var produceClientApiHeaderCode = (parseResult, requestInterfaceDecl) => { | ||
return `${parseResult.imports.join("\n")} | ||
}`},se=(e,t)=>`${e.imports.join(` | ||
`)} | ||
${parseResult.genericTypes.join("\n")} | ||
${e.genericTypes.join(` | ||
`)} | ||
${parseResult.genericInterfaces.join("\n")} | ||
${e.genericInterfaces.join(` | ||
`)} | ||
@@ -300,67 +36,9 @@ export interface QueryMap { | ||
${requestInterfaceDecl} | ||
${t} | ||
${parseResult.responseInterface}`; | ||
}; | ||
var produceClientApiCode = (parseResults, apiGeneratorOptions) => { | ||
const requestInterfaceDecl = parseResults[0].requestInterface ? `${parseResults[0].requestInterface}` : ""; | ||
const requestParamDecl = parseResults[0].requestInterface ? `payload: ApiRequest, ` : ""; | ||
let bodyCode = ""; | ||
parseResults.forEach((parseResult) => { | ||
const bodyInst = parseResult.method !== "HEAD" && parseResult.method !== "GET" && parseResult.requestInterface ? "options.body = JSON.stringify(payload)" : ""; | ||
bodyCode += produceClientApiRequestImplCode( | ||
parseResult, | ||
apiGeneratorOptions, | ||
requestParamDecl, | ||
bodyInst, | ||
parseResults.length > 1 | ||
); | ||
}); | ||
return `${produceClientApiHeaderCode(parseResults[0], requestInterfaceDecl)} | ||
${bodyCode}`; | ||
}; | ||
${e.responseInterface}`,ie=(e,t)=>{let r=e[0].requestInterface?`${e[0].requestInterface}`:"",o=e[0].requestInterface?"payload: ApiRequest, ":"",s="";return e.forEach(n=>{let c=n.method!=="HEAD"&&n.method!=="GET"&&n.requestInterface?"options.body = JSON.stringify(payload)":"";s+=ne(n,t,o,c,e.length>1)}),`${se(e[0],r)} | ||
${s}`};var E=require("path"),v=require("url"),q=require("fs/promises"),pe={},ae=async e=>JSON.parse(await(0,q.readFile)(e,{encoding:"utf-8"})),D=async()=>await ae((0,E.resolve)((0,E.parse)((0,v.fileURLToPath)(pe.url)).dir,"../package.json"));var k=(o=>(o.HELP="help",o.VERSION="version",o.GENERATE="generate",o))(k||{}),U=e=>{let t={apiDir:typeof e.apiDir=="string"?e.apiDir:x.apiDir,baseUrl:typeof e.baseUrl=="string"?e.baseUrl:x.baseUrl,outDir:typeof e.outDir=="string"?e.outDir:x.outDir};if(e.version)return{cmd:"version",options:t};if(e.help)return{cmd:"help",options:t};switch(e._[2]){case"help":return{cmd:"help",options:t};case"generate":return{cmd:"generate",options:t};default:return{cmd:"version",options:t}}},ce=()=>{console.error(` | ||
${i.bold("astro-client-generator")} - generates TypeScript clients for Astro endpoints | ||
// src/version.ts | ||
var import_path2 = require("path"); | ||
var import_url = require("url"); | ||
var import_promises = require("fs/promises"); | ||
var import_meta = {}; | ||
var getPackageJson = async (packageJsonPath) => { | ||
return JSON.parse(await (0, import_promises.readFile)(packageJsonPath, { encoding: "utf-8" })); | ||
}; | ||
var getOwnVersion = async () => await getPackageJson((0, import_path2.resolve)((0, import_path2.parse)((0, import_url.fileURLToPath)(import_meta.url)).dir, "../package.json")); | ||
// src/cli.ts | ||
var Commands = /* @__PURE__ */ ((Commands2) => { | ||
Commands2["HELP"] = "help"; | ||
Commands2["VERSION"] = "version"; | ||
Commands2["GENERATE"] = "generate"; | ||
return Commands2; | ||
})(Commands || {}); | ||
var resolveArgs = (flags) => { | ||
const options = { | ||
apiDir: typeof flags.apiDir === "string" ? flags.apiDir : apiGeneratorOptionsDefaults.apiDir, | ||
baseUrl: typeof flags.baseUrl === "string" ? flags.baseUrl : apiGeneratorOptionsDefaults.baseUrl, | ||
outDir: typeof flags.outDir === "string" ? flags.outDir : apiGeneratorOptionsDefaults.outDir | ||
}; | ||
if (flags.version) { | ||
return { cmd: "version", options }; | ||
} else if (flags.help) { | ||
return { cmd: "help", options }; | ||
} | ||
const cmd = flags._[2]; | ||
switch (cmd) { | ||
case "help": | ||
return { cmd: "help", options }; | ||
case "generate": | ||
return { cmd: "generate", options }; | ||
default: | ||
return { cmd: "version", options }; | ||
} | ||
}; | ||
var printHelp = () => { | ||
console.error(` | ||
${colors.bold("astro-client-generator")} - generates TypeScript clients for Astro endpoints | ||
${colors.bold("Commands:")} | ||
${i.bold("Commands:")} | ||
generate Generates the TypeScript clients for the endpoints. | ||
@@ -370,3 +48,3 @@ version Show the program version. | ||
${colors.bold("Flags:")} | ||
${i.bold("Flags:")} | ||
--apiDir <string> Folder to the API directory on disk (source code), default: './src/pages/api' | ||
@@ -378,59 +56,5 @@ --baseUrl <string> API base URL for calling the API (only relevant if you host in a subdir, it's very unlikely), default: '' | ||
${colors.bold("Example(s):")} | ||
${i.bold("Example(s):")} | ||
npx @jsheaven/astro-client-generator generate | ||
`); | ||
}; | ||
var printVersion = async () => { | ||
console.log((await getOwnVersion()).version); | ||
}; | ||
var cli = async (args) => { | ||
const flags = (0, import_yargs_parser.default)(args); | ||
const state = resolveArgs(flags); | ||
const options = { ...state.options }; | ||
console.log( | ||
colors.dim(">"), | ||
`${colors.bold(colors.yellow("astro-client-generator"))} @ ${colors.dim( | ||
(await getOwnVersion()).version | ||
)}: ${colors.magenta(colors.bold(state.cmd))}`, | ||
colors.gray("...") | ||
); | ||
switch (state.cmd) { | ||
case "help": { | ||
printHelp(); | ||
process.exit(0); | ||
} | ||
case "version": { | ||
await printVersion(); | ||
process.exit(0); | ||
} | ||
case "generate": { | ||
try { | ||
await generateClientApis(options); | ||
} catch (e) { | ||
throwAndExit(e); | ||
} | ||
process.exit(0); | ||
} | ||
default: { | ||
throw new Error(`Error running ${state.cmd}`); | ||
} | ||
} | ||
}; | ||
var printError = (err) => console.error(colors.red(err.toString() || err)); | ||
var throwAndExit = (err) => { | ||
printError(err); | ||
process.exit(1); | ||
}; | ||
try { | ||
cli(process.argv); | ||
} catch (error) { | ||
console.error(error); | ||
process.exit(1); | ||
} | ||
// Annotate the CommonJS export names for ESM import in node: | ||
0 && (module.exports = { | ||
Commands, | ||
cli, | ||
resolveArgs | ||
}); | ||
`)},le=async()=>{console.log((await D()).version)},N=async e=>{let t=(0,H.default)(e),r=U(t),o={...r.options};switch(console.log(i.dim(">"),`${i.bold(i.yellow("astro-client-generator"))} @ ${i.dim((await D()).version)}: ${i.magenta(i.bold(r.cmd))}`,i.gray("...")),r.cmd){case"help":ce(),process.exit(0);case"version":await le(),process.exit(0);case"generate":{try{await S(o)}catch(s){fe(s)}process.exit(0)}default:throw new Error(`Error running ${r.cmd}`)}},ue=e=>console.error(i.red(e.toString()||e)),fe=e=>{ue(e),process.exit(1)};try{N(process.argv)}catch(e){console.error(e),process.exit(1)}0&&(module.exports={Commands,cli,resolveArgs}); | ||
//# sourceMappingURL=cli.cjs.js.map |
#!/usr/bin/env node | ||
import*as s from"kleur/colors";import Z from"yargs-parser";import v from"fast-glob";import{resolve as I,parse as x,sep as q}from"path";import{readFileSync as H,mkdirSync as k,writeFileSync as U}from"fs";import{Project as N}from"ts-morph";var A={apiDir:"./src/pages/api",baseUrl:"",outDir:"./src/pages/api-client",tsConfigPath:"./tsconfig.json",site:"http://localhost:3000"},O=["GET","POST","DELETE","PATCH","HEAD","PUT","OPTIONS","ALL"],C=["POST","DELETE","GET","PATCH","HEAD","PUT","OPTIONS"],E=e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}`,F=e=>`${e.charAt(0).toLowerCase()}${e.slice(1)}`,P=e=>e.join(` | ||
`).trim(),j=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?C:t},M=e=>({...A,...e});var G=(e,t)=>{let n=new N({tsConfigFilePath:t}),o=[];return e.forEach(i=>{let r=n.getSourceFile(i),c=r.getImportDeclarations().map(a=>a.getFullText()),g=[],f="",l="";r.getInterfaces().map(a=>{switch(a.getName()){case"ApiRequest":f=a.getText();break;case"ApiResponse":l=a.getText();break;default:g.push(a.getText())}});let u=r.getTypeAliases().map(a=>a.getText()),d=[];r.getExportSymbols().forEach(a=>{let h=O.indexOf(a.getName().toUpperCase());if(h>-1){let p=O[h];p==="DELETE"?d.push("DELETE"):p==="ALL"?d=C:d.push(p)}}),d.forEach(a=>{o.push({apiRoute:i,imports:c,method:a,requestInterface:f,responseInterface:l,genericInterfaces:g,genericTypes:u})})}),o},B=e=>{let t=[];return e.forEach(n=>{let o=H(n,{encoding:"utf-8"}),i=o.split(` | ||
`),r=[],c=[],g=[],f,l,u,d=0,a=[-1,-1],h=[-1,-1];i.forEach((p,m)=>{let R=/^import (.*) from (.*)/.test(p.trim()),y=p.indexOf("interface ")>-1,b=/^\s*(?:export\s+)?type\s+\w+\s*=\s*[\w<>,\s|]+\s*;?\s*$/.test(p.trim()),$=p.indexOf(" ApiRequest ")>-1,L=p.indexOf(" ApiResponse ")>-1,w=p.indexOf("{")>-1,S=p.indexOf("}")>-1;if(R){r.push(p);return}if(b){g.push(p);return}y&&$&&(l=m),y&&L&&(u=m),y&&l!==m&&u!==m&&(f=m),(l||u||f)&&w&&d++,(l||u||f)&&S&&(d--,d===0&&(l&&(a=[l,m],l=void 0),u&&(h=[u,m],u=void 0),f&&(c.push(P(i.slice(f,m+1))),f=void 0)))}),j(o).forEach(p=>{t.push({apiRoute:n,imports:r,method:p,requestInterface:P(i.slice(a[0],a[1]+1)),responseInterface:P(i.slice(h[0],h[1]+1)),genericInterfaces:c,genericTypes:g})})}),t},V=e=>e.reduce((t,n)=>{let{path:o}=n;return t[o]||(t[o]=[]),t[o].push(n),t},{}),D=e=>{e=M(e);let t=I(process.cwd(),e.apiDir),n=v.sync(`${t}/**/*.ts`),o;switch(e.parser){case"baseline":o=G(n,e.tsConfigPath);break;case"naive":default:o=B(n);break}o=o.filter(r=>r.responseInterface!==""),o.map(r=>(r.relativePath=`api${r.apiRoute.replace(t,"")}`.trim(),r.path=`${x(r.relativePath).dir}/${x(r.relativePath).name}`,r.camelCaseName=r.path.replace(/^api/i,"").split("/").map(c=>E(c)).join("").split(/[-_\ \.]/g).reduce((c,g)=>c+E(g),""),r));let i=V(o);Object.keys(i).forEach(r=>{let c=i[r],g=_(c,e),f=I(process.cwd(),e.outDir,c[0].relativePath.replace(/^api\//,"")),l=x(f),u=l.dir,d=`${u}${q}${l.name}-client.ts`;k(u,{recursive:!0}),U(d.toLowerCase(),g,{encoding:"utf-8"})})},J=(e,t,n,o,i)=>{let r=i?E(e.method.toLowerCase()):"";return` | ||
// src/cli.ts | ||
import * as colors from "kleur/colors"; | ||
import yargs from "yargs-parser"; | ||
// src/index.ts | ||
import fastGlob from "fast-glob"; | ||
import { resolve, parse, sep } from "path"; | ||
import { readFileSync, mkdirSync, writeFileSync } from "fs"; | ||
import { Project } from "ts-morph"; | ||
var apiGeneratorOptionsDefaults = { | ||
apiDir: "./src/pages/api", | ||
baseUrl: "", | ||
outDir: "./src/pages/api-client", | ||
tsConfigPath: "./tsconfig.json", | ||
site: "http://localhost:3000" | ||
}; | ||
var AstroHttpEndpointMethodNames = ["GET", "POST", "DEL", "PATCH", "HEAD", "PUT", "OPTIONS", "ALL"]; | ||
var HttpMethods = ["POST", "DELETE", "GET", "PATCH", "HEAD", "PUT", "OPTIONS"]; | ||
var upperCaseFirst = (text) => `${text.charAt(0).toUpperCase()}${text.slice(1)}`; | ||
var lowerCaseFirst = (text) => `${text.charAt(0).toLowerCase()}${text.slice(1)}`; | ||
var cleanupInterfce = (codeLines) => codeLines.join("\n").trim(); | ||
var analyzeHttpMethodsImplemented = (code) => { | ||
const methods = []; | ||
if (code.indexOf("function get") > -1 || code.indexOf("export const get") > -1) { | ||
methods.push("GET"); | ||
} | ||
if (code.indexOf("function post") > -1 || code.indexOf("export const post") > -1) { | ||
methods.push("POST"); | ||
} | ||
if (code.indexOf("function del") > -1 || code.indexOf("export const del") > -1) { | ||
methods.push("DELETE"); | ||
} | ||
if (code.indexOf("function patch") > -1 || code.indexOf("export const patch") > -1) { | ||
methods.push("PATCH"); | ||
} | ||
if (code.indexOf("function head") > -1 || code.indexOf("export const head") > -1) { | ||
methods.push("HEAD"); | ||
} | ||
if (code.indexOf("function put") > -1 || code.indexOf("export const put") > -1) { | ||
methods.push("PUT"); | ||
} | ||
if (code.indexOf("function options") > -1 || code.indexOf("export const options") > -1) { | ||
methods.push("OPTIONS"); | ||
} | ||
if (code.indexOf("function all") > -1 || code.indexOf("export const all") > -1) { | ||
return HttpMethods; | ||
} | ||
return methods; | ||
}; | ||
var validateConfig = (apiGeneratorOptions) => ({ | ||
...apiGeneratorOptionsDefaults, | ||
...apiGeneratorOptions | ||
}); | ||
var parseApiRoutesBaseline = (apiRoutes, tsConfigPath) => { | ||
const project = new Project({ | ||
tsConfigFilePath: tsConfigPath | ||
}); | ||
const apiRouteParseResults = []; | ||
apiRoutes.forEach((apiRoute) => { | ||
const sourceFile = project.getSourceFile(apiRoute); | ||
const imports = sourceFile.getImportDeclarations().map((d) => d.getFullText()); | ||
const genericInterfaces = []; | ||
let requestInterface = ""; | ||
let responseInterface = ""; | ||
sourceFile.getInterfaces().map((d) => { | ||
switch (d.getName()) { | ||
case "ApiRequest": | ||
requestInterface = d.getText(); | ||
break; | ||
case "ApiResponse": | ||
responseInterface = d.getText(); | ||
break; | ||
default: | ||
genericInterfaces.push(d.getText()); | ||
} | ||
}); | ||
const genericTypes = sourceFile.getTypeAliases().map((d) => d.getText()); | ||
let methods = []; | ||
sourceFile.getExportSymbols().forEach((d) => { | ||
const methodIndex = AstroHttpEndpointMethodNames.indexOf(d.getName().toUpperCase()); | ||
if (methodIndex > -1) { | ||
const astroMethod = AstroHttpEndpointMethodNames[methodIndex]; | ||
if (astroMethod === "DEL") { | ||
methods.push("DELETE"); | ||
} else if (astroMethod === "ALL") { | ||
methods = HttpMethods; | ||
} else { | ||
methods.push(astroMethod); | ||
} | ||
} | ||
}); | ||
methods.forEach((method) => { | ||
apiRouteParseResults.push({ | ||
apiRoute, | ||
imports, | ||
method, | ||
requestInterface, | ||
responseInterface, | ||
genericInterfaces, | ||
genericTypes | ||
}); | ||
}); | ||
}); | ||
return apiRouteParseResults; | ||
}; | ||
var parseApiRoutesNaive = (apiRoutes) => { | ||
const apiRouteParseResults = []; | ||
apiRoutes.forEach((apiRoute) => { | ||
const impl = readFileSync(apiRoute, { encoding: "utf-8" }); | ||
const codeLines = impl.split("\n"); | ||
const imports = []; | ||
const genericInterfaces = []; | ||
const genericTypes = []; | ||
let genericInterfaceDeclBlockStartLine; | ||
let apiRequestDeclBlockStartLine; | ||
let apiResponseDeclBlockStartLine; | ||
let interfaceDeclBlockIndent = 0; | ||
let requestInterfaceCode = [-1, -1]; | ||
let responseInterfaceCode = [-1, -1]; | ||
codeLines.forEach((line, i) => { | ||
const isImport = /^import (.*) from (.*)/.test(line.trim()); | ||
const isInterfaceDeclLine = line.indexOf("interface ") > -1; | ||
const isTypeDeclLine = /^\s*(?:export\s+)?type\s+\w+\s*=\s*[\w<>,\s|]+\s*;?\s*$/.test(line.trim()); | ||
const isApiRequestDeclLine = line.indexOf(" ApiRequest ") > -1; | ||
const isApiResponseDeclLine = line.indexOf(" ApiResponse ") > -1; | ||
const isOpeningBracketLine = line.indexOf("{") > -1; | ||
const isClosingBracketLine = line.indexOf("}") > -1; | ||
if (isImport) { | ||
imports.push(line); | ||
return; | ||
} | ||
if (isTypeDeclLine) { | ||
genericTypes.push(line); | ||
return; | ||
} | ||
if (isInterfaceDeclLine && isApiRequestDeclLine) { | ||
apiRequestDeclBlockStartLine = i; | ||
} | ||
if (isInterfaceDeclLine && isApiResponseDeclLine) { | ||
apiResponseDeclBlockStartLine = i; | ||
} | ||
if (isInterfaceDeclLine && apiRequestDeclBlockStartLine !== i && apiResponseDeclBlockStartLine !== i) { | ||
genericInterfaceDeclBlockStartLine = i; | ||
} | ||
if ((apiRequestDeclBlockStartLine || apiResponseDeclBlockStartLine || genericInterfaceDeclBlockStartLine) && isOpeningBracketLine) { | ||
interfaceDeclBlockIndent++; | ||
} | ||
if ((apiRequestDeclBlockStartLine || apiResponseDeclBlockStartLine || genericInterfaceDeclBlockStartLine) && isClosingBracketLine) { | ||
interfaceDeclBlockIndent--; | ||
if (interfaceDeclBlockIndent === 0) { | ||
if (apiRequestDeclBlockStartLine) { | ||
requestInterfaceCode = [apiRequestDeclBlockStartLine, i]; | ||
apiRequestDeclBlockStartLine = void 0; | ||
} | ||
if (apiResponseDeclBlockStartLine) { | ||
responseInterfaceCode = [apiResponseDeclBlockStartLine, i]; | ||
apiResponseDeclBlockStartLine = void 0; | ||
} | ||
if (genericInterfaceDeclBlockStartLine) { | ||
genericInterfaces.push(cleanupInterfce(codeLines.slice(genericInterfaceDeclBlockStartLine, i + 1))); | ||
genericInterfaceDeclBlockStartLine = void 0; | ||
} | ||
} | ||
} | ||
}); | ||
analyzeHttpMethodsImplemented(impl).forEach((method) => { | ||
apiRouteParseResults.push({ | ||
apiRoute, | ||
imports, | ||
method, | ||
requestInterface: cleanupInterfce(codeLines.slice(requestInterfaceCode[0], requestInterfaceCode[1] + 1)), | ||
responseInterface: cleanupInterfce(codeLines.slice(responseInterfaceCode[0], responseInterfaceCode[1] + 1)), | ||
genericInterfaces, | ||
genericTypes | ||
}); | ||
}); | ||
}); | ||
return apiRouteParseResults; | ||
}; | ||
var groupByApiRoute = (routes) => { | ||
return routes.reduce((acc, file) => { | ||
const { path } = file; | ||
if (!acc[path]) { | ||
acc[path] = []; | ||
} | ||
acc[path].push(file); | ||
return acc; | ||
}, {}); | ||
}; | ||
var generateClientApis = (apiGeneratorOptions) => { | ||
apiGeneratorOptions = validateConfig(apiGeneratorOptions); | ||
const apiFolder = resolve(process.cwd(), apiGeneratorOptions.apiDir); | ||
const apiRoutes = fastGlob.sync(`${apiFolder}/**/*.ts`); | ||
let apiRouteParseResults; | ||
switch (apiGeneratorOptions.parser) { | ||
case "baseline": | ||
apiRouteParseResults = parseApiRoutesBaseline(apiRoutes, apiGeneratorOptions.tsConfigPath); | ||
break; | ||
case "naive": | ||
default: | ||
apiRouteParseResults = parseApiRoutesNaive(apiRoutes); | ||
break; | ||
} | ||
apiRouteParseResults = apiRouteParseResults.filter((apiRoute) => apiRoute.responseInterface !== ""); | ||
apiRouteParseResults.map((result) => { | ||
result.relativePath = `api${result.apiRoute.replace(apiFolder, "")}`.trim(); | ||
result.path = `${parse(result.relativePath).dir}/${parse(result.relativePath).name}`; | ||
result.camelCaseName = result.path.replace(/^api/i, "").split("/").map((part) => upperCaseFirst(part)).join("").split(/[-_\ \.]/g).reduce((prev, current) => prev + upperCaseFirst(current), ""); | ||
return result; | ||
}); | ||
const perRoute = groupByApiRoute(apiRouteParseResults); | ||
Object.keys(perRoute).forEach((apiRoute) => { | ||
const parseResults = perRoute[apiRoute]; | ||
const clientCode = produceClientApiCode(parseResults, apiGeneratorOptions); | ||
const clientFilePath = resolve( | ||
process.cwd(), | ||
apiGeneratorOptions.outDir, | ||
parseResults[0].relativePath.replace(/^api\//, "") | ||
); | ||
const parsed = parse(clientFilePath); | ||
const clientFileDir = parsed.dir; | ||
const finalFilePath = `${clientFileDir}${sep}${parsed.name}-client.ts`; | ||
mkdirSync(clientFileDir, { recursive: true }); | ||
writeFileSync(finalFilePath.toLowerCase(), clientCode, { encoding: "utf-8" }); | ||
}); | ||
}; | ||
var produceClientApiRequestImplCode = (parseResult, apiGeneratorOptions, requestParamDecl, bodyInst, hasMultipleEndpointsInOneFile) => { | ||
const methodNameAppendix = hasMultipleEndpointsInOneFile ? upperCaseFirst(parseResult.method.toLowerCase()) : ""; | ||
return ` | ||
/** return (await fetch('${apiGeneratorOptions.baseUrl}/${parseResult.path}', { method: '${parseResult.method}', ... })).json() */ | ||
export const ${lowerCaseFirst( | ||
parseResult.camelCaseName | ||
)}${methodNameAppendix} = async(${requestParamDecl}options: RequestOptions = {}): Promise<ApiResponse> => { | ||
let requestUrl = '${apiGeneratorOptions.site}${apiGeneratorOptions.baseUrl}/${parseResult.path}' | ||
/** return (await fetch('${t.baseUrl}/${e.path}', { method: '${e.method}', ... })).json() */ | ||
export const ${F(e.camelCaseName)}${r} = async(${n}options: RequestOptions = {}): Promise<ApiResponse> => { | ||
let requestUrl = '${t.site}${t.baseUrl}/${e.path}' | ||
if (options && options.query) { | ||
@@ -244,13 +15,13 @@ requestUrl += '?' + Object.keys(options.query) | ||
delete options.query | ||
options.method = '${parseResult.method}' | ||
${bodyInst} | ||
options.method = '${e.method}' | ||
${o} | ||
return (await fetch(requestUrl, options)).json() | ||
}`; | ||
}; | ||
var produceClientApiHeaderCode = (parseResult, requestInterfaceDecl) => { | ||
return `${parseResult.imports.join("\n")} | ||
}`},Q=(e,t)=>`${e.imports.join(` | ||
`)} | ||
${parseResult.genericTypes.join("\n")} | ||
${e.genericTypes.join(` | ||
`)} | ||
${parseResult.genericInterfaces.join("\n")} | ||
${e.genericInterfaces.join(` | ||
`)} | ||
@@ -265,66 +36,9 @@ export interface QueryMap { | ||
${requestInterfaceDecl} | ||
${t} | ||
${parseResult.responseInterface}`; | ||
}; | ||
var produceClientApiCode = (parseResults, apiGeneratorOptions) => { | ||
const requestInterfaceDecl = parseResults[0].requestInterface ? `${parseResults[0].requestInterface}` : ""; | ||
const requestParamDecl = parseResults[0].requestInterface ? `payload: ApiRequest, ` : ""; | ||
let bodyCode = ""; | ||
parseResults.forEach((parseResult) => { | ||
const bodyInst = parseResult.method !== "HEAD" && parseResult.method !== "GET" && parseResult.requestInterface ? "options.body = JSON.stringify(payload)" : ""; | ||
bodyCode += produceClientApiRequestImplCode( | ||
parseResult, | ||
apiGeneratorOptions, | ||
requestParamDecl, | ||
bodyInst, | ||
parseResults.length > 1 | ||
); | ||
}); | ||
return `${produceClientApiHeaderCode(parseResults[0], requestInterfaceDecl)} | ||
${bodyCode}`; | ||
}; | ||
${e.responseInterface}`,_=(e,t)=>{let n=e[0].requestInterface?`${e[0].requestInterface}`:"",o=e[0].requestInterface?"payload: ApiRequest, ":"",i="";return e.forEach(r=>{let c=r.method!=="HEAD"&&r.method!=="GET"&&r.requestInterface?"options.body = JSON.stringify(payload)":"";i+=J(r,t,o,c,e.length>1)}),`${Q(e[0],n)} | ||
${i}`};import{resolve as z,parse as K}from"path";import{fileURLToPath as W}from"url";import{readFile as X}from"fs/promises";var Y=async e=>JSON.parse(await X(e,{encoding:"utf-8"})),T=async()=>await Y(z(K(W(import.meta.url)).dir,"../package.json"));var ee=(o=>(o.HELP="help",o.VERSION="version",o.GENERATE="generate",o))(ee||{}),te=e=>{let t={apiDir:typeof e.apiDir=="string"?e.apiDir:A.apiDir,baseUrl:typeof e.baseUrl=="string"?e.baseUrl:A.baseUrl,outDir:typeof e.outDir=="string"?e.outDir:A.outDir};if(e.version)return{cmd:"version",options:t};if(e.help)return{cmd:"help",options:t};switch(e._[2]){case"help":return{cmd:"help",options:t};case"generate":return{cmd:"generate",options:t};default:return{cmd:"version",options:t}}},re=()=>{console.error(` | ||
${s.bold("astro-client-generator")} - generates TypeScript clients for Astro endpoints | ||
// src/version.ts | ||
import { resolve as resolve2, parse as parse2 } from "path"; | ||
import { fileURLToPath } from "url"; | ||
import { readFile } from "fs/promises"; | ||
var getPackageJson = async (packageJsonPath) => { | ||
return JSON.parse(await readFile(packageJsonPath, { encoding: "utf-8" })); | ||
}; | ||
var getOwnVersion = async () => await getPackageJson(resolve2(parse2(fileURLToPath(import.meta.url)).dir, "../package.json")); | ||
// src/cli.ts | ||
var Commands = /* @__PURE__ */ ((Commands2) => { | ||
Commands2["HELP"] = "help"; | ||
Commands2["VERSION"] = "version"; | ||
Commands2["GENERATE"] = "generate"; | ||
return Commands2; | ||
})(Commands || {}); | ||
var resolveArgs = (flags) => { | ||
const options = { | ||
apiDir: typeof flags.apiDir === "string" ? flags.apiDir : apiGeneratorOptionsDefaults.apiDir, | ||
baseUrl: typeof flags.baseUrl === "string" ? flags.baseUrl : apiGeneratorOptionsDefaults.baseUrl, | ||
outDir: typeof flags.outDir === "string" ? flags.outDir : apiGeneratorOptionsDefaults.outDir | ||
}; | ||
if (flags.version) { | ||
return { cmd: "version", options }; | ||
} else if (flags.help) { | ||
return { cmd: "help", options }; | ||
} | ||
const cmd = flags._[2]; | ||
switch (cmd) { | ||
case "help": | ||
return { cmd: "help", options }; | ||
case "generate": | ||
return { cmd: "generate", options }; | ||
default: | ||
return { cmd: "version", options }; | ||
} | ||
}; | ||
var printHelp = () => { | ||
console.error(` | ||
${colors.bold("astro-client-generator")} - generates TypeScript clients for Astro endpoints | ||
${colors.bold("Commands:")} | ||
${s.bold("Commands:")} | ||
generate Generates the TypeScript clients for the endpoints. | ||
@@ -334,3 +48,3 @@ version Show the program version. | ||
${colors.bold("Flags:")} | ||
${s.bold("Flags:")} | ||
--apiDir <string> Folder to the API directory on disk (source code), default: './src/pages/api' | ||
@@ -342,58 +56,5 @@ --baseUrl <string> API base URL for calling the API (only relevant if you host in a subdir, it's very unlikely), default: '' | ||
${colors.bold("Example(s):")} | ||
${s.bold("Example(s):")} | ||
npx @jsheaven/astro-client-generator generate | ||
`); | ||
}; | ||
var printVersion = async () => { | ||
console.log((await getOwnVersion()).version); | ||
}; | ||
var cli = async (args) => { | ||
const flags = yargs(args); | ||
const state = resolveArgs(flags); | ||
const options = { ...state.options }; | ||
console.log( | ||
colors.dim(">"), | ||
`${colors.bold(colors.yellow("astro-client-generator"))} @ ${colors.dim( | ||
(await getOwnVersion()).version | ||
)}: ${colors.magenta(colors.bold(state.cmd))}`, | ||
colors.gray("...") | ||
); | ||
switch (state.cmd) { | ||
case "help": { | ||
printHelp(); | ||
process.exit(0); | ||
} | ||
case "version": { | ||
await printVersion(); | ||
process.exit(0); | ||
} | ||
case "generate": { | ||
try { | ||
await generateClientApis(options); | ||
} catch (e) { | ||
throwAndExit(e); | ||
} | ||
process.exit(0); | ||
} | ||
default: { | ||
throw new Error(`Error running ${state.cmd}`); | ||
} | ||
} | ||
}; | ||
var printError = (err) => console.error(colors.red(err.toString() || err)); | ||
var throwAndExit = (err) => { | ||
printError(err); | ||
process.exit(1); | ||
}; | ||
try { | ||
cli(process.argv); | ||
} catch (error) { | ||
console.error(error); | ||
process.exit(1); | ||
} | ||
export { | ||
Commands, | ||
cli, | ||
resolveArgs | ||
}; | ||
`)},oe=async()=>{console.log((await T()).version)},ne=async e=>{let t=Z(e),n=te(t),o={...n.options};switch(console.log(s.dim(">"),`${s.bold(s.yellow("astro-client-generator"))} @ ${s.dim((await T()).version)}: ${s.magenta(s.bold(n.cmd))}`,s.gray("...")),n.cmd){case"help":re(),process.exit(0);case"version":await oe(),process.exit(0);case"generate":{try{await D(o)}catch(i){ie(i)}process.exit(0)}default:throw new Error(`Error running ${n.cmd}`)}},se=e=>console.error(s.red(e.toString()||e)),ie=e=>{se(e),process.exit(1)};try{ne(process.argv)}catch(e){console.error(e),process.exit(1)}export{ee as Commands,ne as cli,te as resolveArgs}; | ||
//# sourceMappingURL=cli.esm.js.map |
#!/usr/bin/env node | ||
"use strict"; | ||
(() => { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { | ||
get: (a, b) => (typeof require !== "undefined" ? require : a)[b] | ||
}) : x)(function(x) { | ||
if (typeof require !== "undefined") | ||
return require.apply(this, arguments); | ||
throw new Error('Dynamic require of "' + x + '" is not supported'); | ||
}); | ||
var __copyProps = (to, from, except, desc) => { | ||
if (from && typeof from === "object" || typeof from === "function") { | ||
for (let key of __getOwnPropNames(from)) | ||
if (!__hasOwnProp.call(to, key) && key !== except) | ||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
} | ||
return to; | ||
}; | ||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
// If the importer is in node compatibility mode or this is not an ESM | ||
// file that has been converted to a CommonJS file using a Babel- | ||
// compatible transform (i.e. "__esModule" has not been set), then set | ||
// "default" to the CommonJS "module.exports" for node compatibility. | ||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||
mod | ||
)); | ||
"use strict";(()=>{var G=Object.create;var R=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var J=Object.getPrototypeOf,Q=Object.prototype.hasOwnProperty;var m=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,o)=>(typeof require<"u"?require:t)[o]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw new Error('Dynamic require of "'+e+'" is not supported')});var _=(e,t,o,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of V(t))!Q.call(e,s)&&s!==o&&R(e,s,{get:()=>t[s],enumerable:!(r=B(t,s))||r.enumerable});return e};var I=(e,t,o)=>(o=e!=null?G(J(e)):{},_(t||!e||!e.__esModule?R(o,"default",{value:e,enumerable:!0}):o,e));var i=I(m("kleur/colors"),1),H=I(m("yargs-parser"),1);var $=I(m("fast-glob"),1),h=m("path"),x=m("fs"),L=m("ts-morph"),P={apiDir:"./src/pages/api",baseUrl:"",outDir:"./src/pages/api-client",tsConfigPath:"./tsconfig.json",site:"http://localhost:3000"},b=["GET","POST","DELETE","PATCH","HEAD","PUT","OPTIONS","ALL"],w=["POST","DELETE","GET","PATCH","HEAD","PUT","OPTIONS"],C=e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}`,z=e=>`${e.charAt(0).toLowerCase()}${e.slice(1)}`,O=e=>e.join(` | ||
`).trim(),K=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?w:t},W=e=>({...P,...e});var X=(e,t)=>{let o=new L.Project({tsConfigFilePath:t}),r=[];return e.forEach(s=>{let n=o.getSourceFile(s),c=n.getImportDeclarations().map(a=>a.getFullText()),g=[],f="",l="";n.getInterfaces().map(a=>{switch(a.getName()){case"ApiRequest":f=a.getText();break;case"ApiResponse":l=a.getText();break;default:g.push(a.getText())}});let u=n.getTypeAliases().map(a=>a.getText()),d=[];n.getExportSymbols().forEach(a=>{let y=b.indexOf(a.getName().toUpperCase());if(y>-1){let p=b[y];p==="DELETE"?d.push("DELETE"):p==="ALL"?d=w:d.push(p)}}),d.forEach(a=>{r.push({apiRoute:s,imports:c,method:a,requestInterface:f,responseInterface:l,genericInterfaces:g,genericTypes:u})})}),r},Y=e=>{let t=[];return e.forEach(o=>{let r=(0,x.readFileSync)(o,{encoding:"utf-8"}),s=r.split(` | ||
`),n=[],c=[],g=[],f,l,u,d=0,a=[-1,-1],y=[-1,-1];s.forEach((p,A)=>{let k=/^import (.*) from (.*)/.test(p.trim()),T=p.indexOf("interface ")>-1,U=/^\s*(?:export\s+)?type\s+\w+\s*=\s*[\w<>,\s|]+\s*;?\s*$/.test(p.trim()),N=p.indexOf(" ApiRequest ")>-1,F=p.indexOf(" ApiResponse ")>-1,j=p.indexOf("{")>-1,M=p.indexOf("}")>-1;if(k){n.push(p);return}if(U){g.push(p);return}T&&N&&(l=A),T&&F&&(u=A),T&&l!==A&&u!==A&&(f=A),(l||u||f)&&j&&d++,(l||u||f)&&M&&(d--,d===0&&(l&&(a=[l,A],l=void 0),u&&(y=[u,A],u=void 0),f&&(c.push(O(s.slice(f,A+1))),f=void 0)))}),K(r).forEach(p=>{t.push({apiRoute:o,imports:n,method:p,requestInterface:O(s.slice(a[0],a[1]+1)),responseInterface:O(s.slice(y[0],y[1]+1)),genericInterfaces:c,genericTypes:g})})}),t},Z=e=>e.reduce((t,o)=>{let{path:r}=o;return t[r]||(t[r]=[]),t[r].push(o),t},{}),S=e=>{e=W(e);let t=(0,h.resolve)(process.cwd(),e.apiDir),o=$.default.sync(`${t}/**/*.ts`),r;switch(e.parser){case"baseline":r=X(o,e.tsConfigPath);break;case"naive":default:r=Y(o);break}r=r.filter(n=>n.responseInterface!==""),r.map(n=>(n.relativePath=`api${n.apiRoute.replace(t,"")}`.trim(),n.path=`${(0,h.parse)(n.relativePath).dir}/${(0,h.parse)(n.relativePath).name}`,n.camelCaseName=n.path.replace(/^api/i,"").split("/").map(c=>C(c)).join("").split(/[-_\ \.]/g).reduce((c,g)=>c+C(g),""),n));let s=Z(r);Object.keys(s).forEach(n=>{let c=s[n],g=re(c,e),f=(0,h.resolve)(process.cwd(),e.outDir,c[0].relativePath.replace(/^api\//,"")),l=(0,h.parse)(f),u=l.dir,d=`${u}${h.sep}${l.name}-client.ts`;(0,x.mkdirSync)(u,{recursive:!0}),(0,x.writeFileSync)(d.toLowerCase(),g,{encoding:"utf-8"})})},ee=(e,t,o,r,s)=>{let n=s?C(e.method.toLowerCase()):"";return` | ||
// src/cli.ts | ||
var colors = __toESM(__require("kleur/colors"), 1); | ||
var import_yargs_parser = __toESM(__require("yargs-parser"), 1); | ||
// src/index.ts | ||
var import_fast_glob = __toESM(__require("fast-glob"), 1); | ||
var import_path = __require("path"); | ||
var import_fs = __require("fs"); | ||
var import_ts_morph = __require("ts-morph"); | ||
var apiGeneratorOptionsDefaults = { | ||
apiDir: "./src/pages/api", | ||
baseUrl: "", | ||
outDir: "./src/pages/api-client", | ||
tsConfigPath: "./tsconfig.json", | ||
site: "http://localhost:3000" | ||
}; | ||
var AstroHttpEndpointMethodNames = ["GET", "POST", "DEL", "PATCH", "HEAD", "PUT", "OPTIONS", "ALL"]; | ||
var HttpMethods = ["POST", "DELETE", "GET", "PATCH", "HEAD", "PUT", "OPTIONS"]; | ||
var upperCaseFirst = (text) => `${text.charAt(0).toUpperCase()}${text.slice(1)}`; | ||
var lowerCaseFirst = (text) => `${text.charAt(0).toLowerCase()}${text.slice(1)}`; | ||
var cleanupInterfce = (codeLines) => codeLines.join("\n").trim(); | ||
var analyzeHttpMethodsImplemented = (code) => { | ||
const methods = []; | ||
if (code.indexOf("function get") > -1 || code.indexOf("export const get") > -1) { | ||
methods.push("GET"); | ||
} | ||
if (code.indexOf("function post") > -1 || code.indexOf("export const post") > -1) { | ||
methods.push("POST"); | ||
} | ||
if (code.indexOf("function del") > -1 || code.indexOf("export const del") > -1) { | ||
methods.push("DELETE"); | ||
} | ||
if (code.indexOf("function patch") > -1 || code.indexOf("export const patch") > -1) { | ||
methods.push("PATCH"); | ||
} | ||
if (code.indexOf("function head") > -1 || code.indexOf("export const head") > -1) { | ||
methods.push("HEAD"); | ||
} | ||
if (code.indexOf("function put") > -1 || code.indexOf("export const put") > -1) { | ||
methods.push("PUT"); | ||
} | ||
if (code.indexOf("function options") > -1 || code.indexOf("export const options") > -1) { | ||
methods.push("OPTIONS"); | ||
} | ||
if (code.indexOf("function all") > -1 || code.indexOf("export const all") > -1) { | ||
return HttpMethods; | ||
} | ||
return methods; | ||
}; | ||
var validateConfig = (apiGeneratorOptions) => ({ | ||
...apiGeneratorOptionsDefaults, | ||
...apiGeneratorOptions | ||
}); | ||
var parseApiRoutesBaseline = (apiRoutes, tsConfigPath) => { | ||
const project = new import_ts_morph.Project({ | ||
tsConfigFilePath: tsConfigPath | ||
}); | ||
const apiRouteParseResults = []; | ||
apiRoutes.forEach((apiRoute) => { | ||
const sourceFile = project.getSourceFile(apiRoute); | ||
const imports = sourceFile.getImportDeclarations().map((d) => d.getFullText()); | ||
const genericInterfaces = []; | ||
let requestInterface = ""; | ||
let responseInterface = ""; | ||
sourceFile.getInterfaces().map((d) => { | ||
switch (d.getName()) { | ||
case "ApiRequest": | ||
requestInterface = d.getText(); | ||
break; | ||
case "ApiResponse": | ||
responseInterface = d.getText(); | ||
break; | ||
default: | ||
genericInterfaces.push(d.getText()); | ||
} | ||
}); | ||
const genericTypes = sourceFile.getTypeAliases().map((d) => d.getText()); | ||
let methods = []; | ||
sourceFile.getExportSymbols().forEach((d) => { | ||
const methodIndex = AstroHttpEndpointMethodNames.indexOf(d.getName().toUpperCase()); | ||
if (methodIndex > -1) { | ||
const astroMethod = AstroHttpEndpointMethodNames[methodIndex]; | ||
if (astroMethod === "DEL") { | ||
methods.push("DELETE"); | ||
} else if (astroMethod === "ALL") { | ||
methods = HttpMethods; | ||
} else { | ||
methods.push(astroMethod); | ||
} | ||
} | ||
}); | ||
methods.forEach((method) => { | ||
apiRouteParseResults.push({ | ||
apiRoute, | ||
imports, | ||
method, | ||
requestInterface, | ||
responseInterface, | ||
genericInterfaces, | ||
genericTypes | ||
}); | ||
}); | ||
}); | ||
return apiRouteParseResults; | ||
}; | ||
var parseApiRoutesNaive = (apiRoutes) => { | ||
const apiRouteParseResults = []; | ||
apiRoutes.forEach((apiRoute) => { | ||
const impl = (0, import_fs.readFileSync)(apiRoute, { encoding: "utf-8" }); | ||
const codeLines = impl.split("\n"); | ||
const imports = []; | ||
const genericInterfaces = []; | ||
const genericTypes = []; | ||
let genericInterfaceDeclBlockStartLine; | ||
let apiRequestDeclBlockStartLine; | ||
let apiResponseDeclBlockStartLine; | ||
let interfaceDeclBlockIndent = 0; | ||
let requestInterfaceCode = [-1, -1]; | ||
let responseInterfaceCode = [-1, -1]; | ||
codeLines.forEach((line, i) => { | ||
const isImport = /^import (.*) from (.*)/.test(line.trim()); | ||
const isInterfaceDeclLine = line.indexOf("interface ") > -1; | ||
const isTypeDeclLine = /^\s*(?:export\s+)?type\s+\w+\s*=\s*[\w<>,\s|]+\s*;?\s*$/.test(line.trim()); | ||
const isApiRequestDeclLine = line.indexOf(" ApiRequest ") > -1; | ||
const isApiResponseDeclLine = line.indexOf(" ApiResponse ") > -1; | ||
const isOpeningBracketLine = line.indexOf("{") > -1; | ||
const isClosingBracketLine = line.indexOf("}") > -1; | ||
if (isImport) { | ||
imports.push(line); | ||
return; | ||
} | ||
if (isTypeDeclLine) { | ||
genericTypes.push(line); | ||
return; | ||
} | ||
if (isInterfaceDeclLine && isApiRequestDeclLine) { | ||
apiRequestDeclBlockStartLine = i; | ||
} | ||
if (isInterfaceDeclLine && isApiResponseDeclLine) { | ||
apiResponseDeclBlockStartLine = i; | ||
} | ||
if (isInterfaceDeclLine && apiRequestDeclBlockStartLine !== i && apiResponseDeclBlockStartLine !== i) { | ||
genericInterfaceDeclBlockStartLine = i; | ||
} | ||
if ((apiRequestDeclBlockStartLine || apiResponseDeclBlockStartLine || genericInterfaceDeclBlockStartLine) && isOpeningBracketLine) { | ||
interfaceDeclBlockIndent++; | ||
} | ||
if ((apiRequestDeclBlockStartLine || apiResponseDeclBlockStartLine || genericInterfaceDeclBlockStartLine) && isClosingBracketLine) { | ||
interfaceDeclBlockIndent--; | ||
if (interfaceDeclBlockIndent === 0) { | ||
if (apiRequestDeclBlockStartLine) { | ||
requestInterfaceCode = [apiRequestDeclBlockStartLine, i]; | ||
apiRequestDeclBlockStartLine = void 0; | ||
} | ||
if (apiResponseDeclBlockStartLine) { | ||
responseInterfaceCode = [apiResponseDeclBlockStartLine, i]; | ||
apiResponseDeclBlockStartLine = void 0; | ||
} | ||
if (genericInterfaceDeclBlockStartLine) { | ||
genericInterfaces.push(cleanupInterfce(codeLines.slice(genericInterfaceDeclBlockStartLine, i + 1))); | ||
genericInterfaceDeclBlockStartLine = void 0; | ||
} | ||
} | ||
} | ||
}); | ||
analyzeHttpMethodsImplemented(impl).forEach((method) => { | ||
apiRouteParseResults.push({ | ||
apiRoute, | ||
imports, | ||
method, | ||
requestInterface: cleanupInterfce(codeLines.slice(requestInterfaceCode[0], requestInterfaceCode[1] + 1)), | ||
responseInterface: cleanupInterfce(codeLines.slice(responseInterfaceCode[0], responseInterfaceCode[1] + 1)), | ||
genericInterfaces, | ||
genericTypes | ||
}); | ||
}); | ||
}); | ||
return apiRouteParseResults; | ||
}; | ||
var groupByApiRoute = (routes) => { | ||
return routes.reduce((acc, file) => { | ||
const { path } = file; | ||
if (!acc[path]) { | ||
acc[path] = []; | ||
} | ||
acc[path].push(file); | ||
return acc; | ||
}, {}); | ||
}; | ||
var generateClientApis = (apiGeneratorOptions) => { | ||
apiGeneratorOptions = validateConfig(apiGeneratorOptions); | ||
const apiFolder = (0, import_path.resolve)(process.cwd(), apiGeneratorOptions.apiDir); | ||
const apiRoutes = import_fast_glob.default.sync(`${apiFolder}/**/*.ts`); | ||
let apiRouteParseResults; | ||
switch (apiGeneratorOptions.parser) { | ||
case "baseline": | ||
apiRouteParseResults = parseApiRoutesBaseline(apiRoutes, apiGeneratorOptions.tsConfigPath); | ||
break; | ||
case "naive": | ||
default: | ||
apiRouteParseResults = parseApiRoutesNaive(apiRoutes); | ||
break; | ||
} | ||
apiRouteParseResults = apiRouteParseResults.filter((apiRoute) => apiRoute.responseInterface !== ""); | ||
apiRouteParseResults.map((result) => { | ||
result.relativePath = `api${result.apiRoute.replace(apiFolder, "")}`.trim(); | ||
result.path = `${(0, import_path.parse)(result.relativePath).dir}/${(0, import_path.parse)(result.relativePath).name}`; | ||
result.camelCaseName = result.path.replace(/^api/i, "").split("/").map((part) => upperCaseFirst(part)).join("").split(/[-_\ \.]/g).reduce((prev, current) => prev + upperCaseFirst(current), ""); | ||
return result; | ||
}); | ||
const perRoute = groupByApiRoute(apiRouteParseResults); | ||
Object.keys(perRoute).forEach((apiRoute) => { | ||
const parseResults = perRoute[apiRoute]; | ||
const clientCode = produceClientApiCode(parseResults, apiGeneratorOptions); | ||
const clientFilePath = (0, import_path.resolve)( | ||
process.cwd(), | ||
apiGeneratorOptions.outDir, | ||
parseResults[0].relativePath.replace(/^api\//, "") | ||
); | ||
const parsed = (0, import_path.parse)(clientFilePath); | ||
const clientFileDir = parsed.dir; | ||
const finalFilePath = `${clientFileDir}${import_path.sep}${parsed.name}-client.ts`; | ||
(0, import_fs.mkdirSync)(clientFileDir, { recursive: true }); | ||
(0, import_fs.writeFileSync)(finalFilePath.toLowerCase(), clientCode, { encoding: "utf-8" }); | ||
}); | ||
}; | ||
var produceClientApiRequestImplCode = (parseResult, apiGeneratorOptions, requestParamDecl, bodyInst, hasMultipleEndpointsInOneFile) => { | ||
const methodNameAppendix = hasMultipleEndpointsInOneFile ? upperCaseFirst(parseResult.method.toLowerCase()) : ""; | ||
return ` | ||
/** return (await fetch('${apiGeneratorOptions.baseUrl}/${parseResult.path}', { method: '${parseResult.method}', ... })).json() */ | ||
export const ${lowerCaseFirst( | ||
parseResult.camelCaseName | ||
)}${methodNameAppendix} = async(${requestParamDecl}options: RequestOptions = {}): Promise<ApiResponse> => { | ||
let requestUrl = '${apiGeneratorOptions.site}${apiGeneratorOptions.baseUrl}/${parseResult.path}' | ||
/** return (await fetch('${t.baseUrl}/${e.path}', { method: '${e.method}', ... })).json() */ | ||
export const ${z(e.camelCaseName)}${n} = async(${o}options: RequestOptions = {}): Promise<ApiResponse> => { | ||
let requestUrl = '${t.site}${t.baseUrl}/${e.path}' | ||
if (options && options.query) { | ||
@@ -275,13 +15,13 @@ requestUrl += '?' + Object.keys(options.query) | ||
delete options.query | ||
options.method = '${parseResult.method}' | ||
${bodyInst} | ||
options.method = '${e.method}' | ||
${r} | ||
return (await fetch(requestUrl, options)).json() | ||
}`; | ||
}; | ||
var produceClientApiHeaderCode = (parseResult, requestInterfaceDecl) => { | ||
return `${parseResult.imports.join("\n")} | ||
}`},te=(e,t)=>`${e.imports.join(` | ||
`)} | ||
${parseResult.genericTypes.join("\n")} | ||
${e.genericTypes.join(` | ||
`)} | ||
${parseResult.genericInterfaces.join("\n")} | ||
${e.genericInterfaces.join(` | ||
`)} | ||
@@ -296,67 +36,9 @@ export interface QueryMap { | ||
${requestInterfaceDecl} | ||
${t} | ||
${parseResult.responseInterface}`; | ||
}; | ||
var produceClientApiCode = (parseResults, apiGeneratorOptions) => { | ||
const requestInterfaceDecl = parseResults[0].requestInterface ? `${parseResults[0].requestInterface}` : ""; | ||
const requestParamDecl = parseResults[0].requestInterface ? `payload: ApiRequest, ` : ""; | ||
let bodyCode = ""; | ||
parseResults.forEach((parseResult) => { | ||
const bodyInst = parseResult.method !== "HEAD" && parseResult.method !== "GET" && parseResult.requestInterface ? "options.body = JSON.stringify(payload)" : ""; | ||
bodyCode += produceClientApiRequestImplCode( | ||
parseResult, | ||
apiGeneratorOptions, | ||
requestParamDecl, | ||
bodyInst, | ||
parseResults.length > 1 | ||
); | ||
}); | ||
return `${produceClientApiHeaderCode(parseResults[0], requestInterfaceDecl)} | ||
${bodyCode}`; | ||
}; | ||
${e.responseInterface}`,re=(e,t)=>{let o=e[0].requestInterface?`${e[0].requestInterface}`:"",r=e[0].requestInterface?"payload: ApiRequest, ":"",s="";return e.forEach(n=>{let c=n.method!=="HEAD"&&n.method!=="GET"&&n.requestInterface?"options.body = JSON.stringify(payload)":"";s+=ee(n,t,r,c,e.length>1)}),`${te(e[0],o)} | ||
${s}`};var E=m("path"),v=m("url"),q=m("fs/promises"),ne={},oe=async e=>JSON.parse(await(0,q.readFile)(e,{encoding:"utf-8"})),D=async()=>await oe((0,E.resolve)((0,E.parse)((0,v.fileURLToPath)(ne.url)).dir,"../package.json"));var se=(r=>(r.HELP="help",r.VERSION="version",r.GENERATE="generate",r))(se||{}),ie=e=>{let t={apiDir:typeof e.apiDir=="string"?e.apiDir:P.apiDir,baseUrl:typeof e.baseUrl=="string"?e.baseUrl:P.baseUrl,outDir:typeof e.outDir=="string"?e.outDir:P.outDir};if(e.version)return{cmd:"version",options:t};if(e.help)return{cmd:"help",options:t};switch(e._[2]){case"help":return{cmd:"help",options:t};case"generate":return{cmd:"generate",options:t};default:return{cmd:"version",options:t}}},ae=()=>{console.error(` | ||
${i.bold("astro-client-generator")} - generates TypeScript clients for Astro endpoints | ||
// src/version.ts | ||
var import_path2 = __require("path"); | ||
var import_url = __require("url"); | ||
var import_promises = __require("fs/promises"); | ||
var import_meta = {}; | ||
var getPackageJson = async (packageJsonPath) => { | ||
return JSON.parse(await (0, import_promises.readFile)(packageJsonPath, { encoding: "utf-8" })); | ||
}; | ||
var getOwnVersion = async () => await getPackageJson((0, import_path2.resolve)((0, import_path2.parse)((0, import_url.fileURLToPath)(import_meta.url)).dir, "../package.json")); | ||
// src/cli.ts | ||
var Commands = /* @__PURE__ */ ((Commands2) => { | ||
Commands2["HELP"] = "help"; | ||
Commands2["VERSION"] = "version"; | ||
Commands2["GENERATE"] = "generate"; | ||
return Commands2; | ||
})(Commands || {}); | ||
var resolveArgs = (flags) => { | ||
const options = { | ||
apiDir: typeof flags.apiDir === "string" ? flags.apiDir : apiGeneratorOptionsDefaults.apiDir, | ||
baseUrl: typeof flags.baseUrl === "string" ? flags.baseUrl : apiGeneratorOptionsDefaults.baseUrl, | ||
outDir: typeof flags.outDir === "string" ? flags.outDir : apiGeneratorOptionsDefaults.outDir | ||
}; | ||
if (flags.version) { | ||
return { cmd: "version", options }; | ||
} else if (flags.help) { | ||
return { cmd: "help", options }; | ||
} | ||
const cmd = flags._[2]; | ||
switch (cmd) { | ||
case "help": | ||
return { cmd: "help", options }; | ||
case "generate": | ||
return { cmd: "generate", options }; | ||
default: | ||
return { cmd: "version", options }; | ||
} | ||
}; | ||
var printHelp = () => { | ||
console.error(` | ||
${colors.bold("astro-client-generator")} - generates TypeScript clients for Astro endpoints | ||
${colors.bold("Commands:")} | ||
${i.bold("Commands:")} | ||
generate Generates the TypeScript clients for the endpoints. | ||
@@ -366,3 +48,3 @@ version Show the program version. | ||
${colors.bold("Flags:")} | ||
${i.bold("Flags:")} | ||
--apiDir <string> Folder to the API directory on disk (source code), default: './src/pages/api' | ||
@@ -374,54 +56,5 @@ --baseUrl <string> API base URL for calling the API (only relevant if you host in a subdir, it's very unlikely), default: '' | ||
${colors.bold("Example(s):")} | ||
${i.bold("Example(s):")} | ||
npx @jsheaven/astro-client-generator generate | ||
`); | ||
}; | ||
var printVersion = async () => { | ||
console.log((await getOwnVersion()).version); | ||
}; | ||
var cli = async (args) => { | ||
const flags = (0, import_yargs_parser.default)(args); | ||
const state = resolveArgs(flags); | ||
const options = { ...state.options }; | ||
console.log( | ||
colors.dim(">"), | ||
`${colors.bold(colors.yellow("astro-client-generator"))} @ ${colors.dim( | ||
(await getOwnVersion()).version | ||
)}: ${colors.magenta(colors.bold(state.cmd))}`, | ||
colors.gray("...") | ||
); | ||
switch (state.cmd) { | ||
case "help": { | ||
printHelp(); | ||
process.exit(0); | ||
} | ||
case "version": { | ||
await printVersion(); | ||
process.exit(0); | ||
} | ||
case "generate": { | ||
try { | ||
await generateClientApis(options); | ||
} catch (e) { | ||
throwAndExit(e); | ||
} | ||
process.exit(0); | ||
} | ||
default: { | ||
throw new Error(`Error running ${state.cmd}`); | ||
} | ||
} | ||
}; | ||
var printError = (err) => console.error(colors.red(err.toString() || err)); | ||
var throwAndExit = (err) => { | ||
printError(err); | ||
process.exit(1); | ||
}; | ||
try { | ||
cli(process.argv); | ||
} catch (error) { | ||
console.error(error); | ||
process.exit(1); | ||
} | ||
})(); | ||
`)},pe=async()=>{console.log((await D()).version)},ce=async e=>{let t=(0,H.default)(e),o=ie(t),r={...o.options};switch(console.log(i.dim(">"),`${i.bold(i.yellow("astro-client-generator"))} @ ${i.dim((await D()).version)}: ${i.magenta(i.bold(o.cmd))}`,i.gray("...")),o.cmd){case"help":ae(),process.exit(0);case"version":await pe(),process.exit(0);case"generate":{try{await S(r)}catch(s){ue(s)}process.exit(0)}default:throw new Error(`Error running ${o.cmd}`)}},le=e=>console.error(i.red(e.toString()||e)),ue=e=>{le(e),process.exit(1)};try{ce(process.argv)}catch(e){console.error(e),process.exit(1)}})(); | ||
//# sourceMappingURL=cli.iife.js.map |
@@ -1,291 +0,8 @@ | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __export = (target, all) => { | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __copyProps = (to, from, except, desc) => { | ||
if (from && typeof from === "object" || typeof from === "function") { | ||
for (let key of __getOwnPropNames(from)) | ||
if (!__hasOwnProp.call(to, key) && key !== except) | ||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
} | ||
return to; | ||
}; | ||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
// If the importer is in node compatibility mode or this is not an ESM | ||
// file that has been converted to a CommonJS file using a Babel- | ||
// compatible transform (i.e. "__esModule" has not been set), then set | ||
// "default" to the CommonJS "module.exports" for node compatibility. | ||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||
mod | ||
)); | ||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
var Q=Object.create;var P=Object.defineProperty;var z=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var _=Object.getPrototypeOf,K=Object.prototype.hasOwnProperty;var V=(e,t)=>{for(var n in t)P(e,n,{get:t[n],enumerable:!0})},R=(e,t,n,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of J(t))!K.call(e,i)&&i!==n&&P(e,i,{get:()=>t[i],enumerable:!(s=z(t,i))||s.enumerable});return e};var W=(e,t,n)=>(n=e!=null?Q(_(e)):{},R(t||!e||!e.__esModule?P(n,"default",{value:e,enumerable:!0}):n,e)),X=e=>R(P({},"__esModule",{value:!0}),e);var Z={};V(Z,{AstroHttpEndpointMethodNames:()=>E,HttpMethods:()=>I,analyzeHttpMethodsImplemented:()=>q,apiClientGenerator:()=>Y,apiGeneratorOptionsDefaults:()=>O,cleanupInterfce:()=>x,generateClientApis:()=>k,groupByApiRoute:()=>S,lowerCaseFirst:()=>L,parseApiRoutesBaseline:()=>b,parseApiRoutesNaive:()=>H,produceClientApiCode:()=>w,produceClientApiHeaderCode:()=>U,produceClientApiRequestImplCode:()=>N,upperCaseFirst:()=>y,validateConfig:()=>C});module.exports=X(Z);var D=W(require("fast-glob"),1),d=require("path"),m=require("fs"),$=require("ts-morph"),O={apiDir:"./src/pages/api",baseUrl:"",outDir:"./src/pages/api-client",tsConfigPath:"./tsconfig.json",site:"http://localhost:3000"},E=["GET","POST","DELETE","PATCH","HEAD","PUT","OPTIONS","ALL"],I=["POST","DELETE","GET","PATCH","HEAD","PUT","OPTIONS"],y=e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}`,L=e=>`${e.charAt(0).toLowerCase()}${e.slice(1)}`,x=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?I:t},C=e=>({...O,...e}),Y=(e=O)=>(e=C(e),{name:"astro-client-generator",hooks:{"astro:build:done":async()=>{k(e)}}}),b=(e,t)=>{let n=new $.Project({tsConfigFilePath:t}),s=[];return e.forEach(i=>{let r=n.getSourceFile(i),p=r.getImportDeclarations().map(o=>o.getFullText()),A=[],u="",c="";r.getInterfaces().map(o=>{switch(o.getName()){case"ApiRequest":u=o.getText();break;case"ApiResponse":c=o.getText();break;default:A.push(o.getText())}});let f=r.getTypeAliases().map(o=>o.getText()),l=[];r.getExportSymbols().forEach(o=>{let g=E.indexOf(o.getName().toUpperCase());if(g>-1){let a=E[g];a==="DELETE"?l.push("DELETE"):a==="ALL"?l=I:l.push(a)}}),l.forEach(o=>{s.push({apiRoute:i,imports:p,method:o,requestInterface:u,responseInterface:c,genericInterfaces:A,genericTypes:f})})}),s},H=e=>{let t=[];return e.forEach(n=>{let s=(0,m.readFileSync)(n,{encoding:"utf-8"}),i=s.split(` | ||
`),r=[],p=[],A=[],u,c,f,l=0,o=[-1,-1],g=[-1,-1];i.forEach((a,h)=>{let j=/^import (.*) from (.*)/.test(a.trim()),T=a.indexOf("interface ")>-1,M=/^\s*(?:export\s+)?type\s+\w+\s*=\s*[\w<>,\s|]+\s*;?\s*$/.test(a.trim()),F=a.indexOf(" ApiRequest ")>-1,v=a.indexOf(" ApiResponse ")>-1,G=a.indexOf("{")>-1,B=a.indexOf("}")>-1;if(j){r.push(a);return}if(M){A.push(a);return}T&&F&&(c=h),T&&v&&(f=h),T&&c!==h&&f!==h&&(u=h),(c||f||u)&&G&&l++,(c||f||u)&&B&&(l--,l===0&&(c&&(o=[c,h],c=void 0),f&&(g=[f,h],f=void 0),u&&(p.push(x(i.slice(u,h+1))),u=void 0)))}),q(s).forEach(a=>{t.push({apiRoute:n,imports:r,method:a,requestInterface:x(i.slice(o[0],o[1]+1)),responseInterface:x(i.slice(g[0],g[1]+1)),genericInterfaces:p,genericTypes:A})})}),t},S=e=>e.reduce((t,n)=>{let{path:s}=n;return t[s]||(t[s]=[]),t[s].push(n),t},{}),k=e=>{e=C(e);let t=(0,d.resolve)(process.cwd(),e.apiDir),n=D.default.sync(`${t}/**/*.ts`),s;switch(e.parser){case"baseline":s=b(n,e.tsConfigPath);break;case"naive":default:s=H(n);break}s=s.filter(r=>r.responseInterface!==""),s.map(r=>(r.relativePath=`api${r.apiRoute.replace(t,"")}`.trim(),r.path=`${(0,d.parse)(r.relativePath).dir}/${(0,d.parse)(r.relativePath).name}`,r.camelCaseName=r.path.replace(/^api/i,"").split("/").map(p=>y(p)).join("").split(/[-_\ \.]/g).reduce((p,A)=>p+y(A),""),r));let i=S(s);Object.keys(i).forEach(r=>{let p=i[r],A=w(p,e),u=(0,d.resolve)(process.cwd(),e.outDir,p[0].relativePath.replace(/^api\//,"")),c=(0,d.parse)(u),f=c.dir,l=`${f}${d.sep}${c.name}-client.ts`;(0,m.mkdirSync)(f,{recursive:!0}),(0,m.writeFileSync)(l.toLowerCase(),A,{encoding:"utf-8"})})},N=(e,t,n,s,i)=>{let r=i?y(e.method.toLowerCase()):"";return` | ||
// src/index.ts | ||
var src_exports = {}; | ||
__export(src_exports, { | ||
AstroHttpEndpointMethodNames: () => AstroHttpEndpointMethodNames, | ||
HttpMethods: () => HttpMethods, | ||
analyzeHttpMethodsImplemented: () => analyzeHttpMethodsImplemented, | ||
apiClientGenerator: () => apiClientGenerator, | ||
apiGeneratorOptionsDefaults: () => apiGeneratorOptionsDefaults, | ||
cleanupInterfce: () => cleanupInterfce, | ||
generateClientApis: () => generateClientApis, | ||
groupByApiRoute: () => groupByApiRoute, | ||
lowerCaseFirst: () => lowerCaseFirst, | ||
parseApiRoutesBaseline: () => parseApiRoutesBaseline, | ||
parseApiRoutesNaive: () => parseApiRoutesNaive, | ||
produceClientApiCode: () => produceClientApiCode, | ||
produceClientApiHeaderCode: () => produceClientApiHeaderCode, | ||
produceClientApiRequestImplCode: () => produceClientApiRequestImplCode, | ||
upperCaseFirst: () => upperCaseFirst, | ||
validateConfig: () => validateConfig | ||
}); | ||
module.exports = __toCommonJS(src_exports); | ||
var import_fast_glob = __toESM(require("fast-glob"), 1); | ||
var import_path = require("path"); | ||
var import_fs = require("fs"); | ||
var import_ts_morph = require("ts-morph"); | ||
var apiGeneratorOptionsDefaults = { | ||
apiDir: "./src/pages/api", | ||
baseUrl: "", | ||
outDir: "./src/pages/api-client", | ||
tsConfigPath: "./tsconfig.json", | ||
site: "http://localhost:3000" | ||
}; | ||
var AstroHttpEndpointMethodNames = ["GET", "POST", "DEL", "PATCH", "HEAD", "PUT", "OPTIONS", "ALL"]; | ||
var HttpMethods = ["POST", "DELETE", "GET", "PATCH", "HEAD", "PUT", "OPTIONS"]; | ||
var upperCaseFirst = (text) => `${text.charAt(0).toUpperCase()}${text.slice(1)}`; | ||
var lowerCaseFirst = (text) => `${text.charAt(0).toLowerCase()}${text.slice(1)}`; | ||
var cleanupInterfce = (codeLines) => codeLines.join("\n").trim(); | ||
var analyzeHttpMethodsImplemented = (code) => { | ||
const methods = []; | ||
if (code.indexOf("function get") > -1 || code.indexOf("export const get") > -1) { | ||
methods.push("GET"); | ||
} | ||
if (code.indexOf("function post") > -1 || code.indexOf("export const post") > -1) { | ||
methods.push("POST"); | ||
} | ||
if (code.indexOf("function del") > -1 || code.indexOf("export const del") > -1) { | ||
methods.push("DELETE"); | ||
} | ||
if (code.indexOf("function patch") > -1 || code.indexOf("export const patch") > -1) { | ||
methods.push("PATCH"); | ||
} | ||
if (code.indexOf("function head") > -1 || code.indexOf("export const head") > -1) { | ||
methods.push("HEAD"); | ||
} | ||
if (code.indexOf("function put") > -1 || code.indexOf("export const put") > -1) { | ||
methods.push("PUT"); | ||
} | ||
if (code.indexOf("function options") > -1 || code.indexOf("export const options") > -1) { | ||
methods.push("OPTIONS"); | ||
} | ||
if (code.indexOf("function all") > -1 || code.indexOf("export const all") > -1) { | ||
return HttpMethods; | ||
} | ||
return methods; | ||
}; | ||
var validateConfig = (apiGeneratorOptions) => ({ | ||
...apiGeneratorOptionsDefaults, | ||
...apiGeneratorOptions | ||
}); | ||
var apiClientGenerator = (apiGeneratorOptions = apiGeneratorOptionsDefaults) => { | ||
apiGeneratorOptions = validateConfig(apiGeneratorOptions); | ||
return { | ||
name: "astro-client-generator", | ||
hooks: { | ||
"astro:build:done": async () => { | ||
generateClientApis(apiGeneratorOptions); | ||
} | ||
} | ||
}; | ||
}; | ||
var parseApiRoutesBaseline = (apiRoutes, tsConfigPath) => { | ||
const project = new import_ts_morph.Project({ | ||
tsConfigFilePath: tsConfigPath | ||
}); | ||
const apiRouteParseResults = []; | ||
apiRoutes.forEach((apiRoute) => { | ||
const sourceFile = project.getSourceFile(apiRoute); | ||
const imports = sourceFile.getImportDeclarations().map((d) => d.getFullText()); | ||
const genericInterfaces = []; | ||
let requestInterface = ""; | ||
let responseInterface = ""; | ||
sourceFile.getInterfaces().map((d) => { | ||
switch (d.getName()) { | ||
case "ApiRequest": | ||
requestInterface = d.getText(); | ||
break; | ||
case "ApiResponse": | ||
responseInterface = d.getText(); | ||
break; | ||
default: | ||
genericInterfaces.push(d.getText()); | ||
} | ||
}); | ||
const genericTypes = sourceFile.getTypeAliases().map((d) => d.getText()); | ||
let methods = []; | ||
sourceFile.getExportSymbols().forEach((d) => { | ||
const methodIndex = AstroHttpEndpointMethodNames.indexOf(d.getName().toUpperCase()); | ||
if (methodIndex > -1) { | ||
const astroMethod = AstroHttpEndpointMethodNames[methodIndex]; | ||
if (astroMethod === "DEL") { | ||
methods.push("DELETE"); | ||
} else if (astroMethod === "ALL") { | ||
methods = HttpMethods; | ||
} else { | ||
methods.push(astroMethod); | ||
} | ||
} | ||
}); | ||
methods.forEach((method) => { | ||
apiRouteParseResults.push({ | ||
apiRoute, | ||
imports, | ||
method, | ||
requestInterface, | ||
responseInterface, | ||
genericInterfaces, | ||
genericTypes | ||
}); | ||
}); | ||
}); | ||
return apiRouteParseResults; | ||
}; | ||
var parseApiRoutesNaive = (apiRoutes) => { | ||
const apiRouteParseResults = []; | ||
apiRoutes.forEach((apiRoute) => { | ||
const impl = (0, import_fs.readFileSync)(apiRoute, { encoding: "utf-8" }); | ||
const codeLines = impl.split("\n"); | ||
const imports = []; | ||
const genericInterfaces = []; | ||
const genericTypes = []; | ||
let genericInterfaceDeclBlockStartLine; | ||
let apiRequestDeclBlockStartLine; | ||
let apiResponseDeclBlockStartLine; | ||
let interfaceDeclBlockIndent = 0; | ||
let requestInterfaceCode = [-1, -1]; | ||
let responseInterfaceCode = [-1, -1]; | ||
codeLines.forEach((line, i) => { | ||
const isImport = /^import (.*) from (.*)/.test(line.trim()); | ||
const isInterfaceDeclLine = line.indexOf("interface ") > -1; | ||
const isTypeDeclLine = /^\s*(?:export\s+)?type\s+\w+\s*=\s*[\w<>,\s|]+\s*;?\s*$/.test(line.trim()); | ||
const isApiRequestDeclLine = line.indexOf(" ApiRequest ") > -1; | ||
const isApiResponseDeclLine = line.indexOf(" ApiResponse ") > -1; | ||
const isOpeningBracketLine = line.indexOf("{") > -1; | ||
const isClosingBracketLine = line.indexOf("}") > -1; | ||
if (isImport) { | ||
imports.push(line); | ||
return; | ||
} | ||
if (isTypeDeclLine) { | ||
genericTypes.push(line); | ||
return; | ||
} | ||
if (isInterfaceDeclLine && isApiRequestDeclLine) { | ||
apiRequestDeclBlockStartLine = i; | ||
} | ||
if (isInterfaceDeclLine && isApiResponseDeclLine) { | ||
apiResponseDeclBlockStartLine = i; | ||
} | ||
if (isInterfaceDeclLine && apiRequestDeclBlockStartLine !== i && apiResponseDeclBlockStartLine !== i) { | ||
genericInterfaceDeclBlockStartLine = i; | ||
} | ||
if ((apiRequestDeclBlockStartLine || apiResponseDeclBlockStartLine || genericInterfaceDeclBlockStartLine) && isOpeningBracketLine) { | ||
interfaceDeclBlockIndent++; | ||
} | ||
if ((apiRequestDeclBlockStartLine || apiResponseDeclBlockStartLine || genericInterfaceDeclBlockStartLine) && isClosingBracketLine) { | ||
interfaceDeclBlockIndent--; | ||
if (interfaceDeclBlockIndent === 0) { | ||
if (apiRequestDeclBlockStartLine) { | ||
requestInterfaceCode = [apiRequestDeclBlockStartLine, i]; | ||
apiRequestDeclBlockStartLine = void 0; | ||
} | ||
if (apiResponseDeclBlockStartLine) { | ||
responseInterfaceCode = [apiResponseDeclBlockStartLine, i]; | ||
apiResponseDeclBlockStartLine = void 0; | ||
} | ||
if (genericInterfaceDeclBlockStartLine) { | ||
genericInterfaces.push(cleanupInterfce(codeLines.slice(genericInterfaceDeclBlockStartLine, i + 1))); | ||
genericInterfaceDeclBlockStartLine = void 0; | ||
} | ||
} | ||
} | ||
}); | ||
analyzeHttpMethodsImplemented(impl).forEach((method) => { | ||
apiRouteParseResults.push({ | ||
apiRoute, | ||
imports, | ||
method, | ||
requestInterface: cleanupInterfce(codeLines.slice(requestInterfaceCode[0], requestInterfaceCode[1] + 1)), | ||
responseInterface: cleanupInterfce(codeLines.slice(responseInterfaceCode[0], responseInterfaceCode[1] + 1)), | ||
genericInterfaces, | ||
genericTypes | ||
}); | ||
}); | ||
}); | ||
return apiRouteParseResults; | ||
}; | ||
var groupByApiRoute = (routes) => { | ||
return routes.reduce((acc, file) => { | ||
const { path } = file; | ||
if (!acc[path]) { | ||
acc[path] = []; | ||
} | ||
acc[path].push(file); | ||
return acc; | ||
}, {}); | ||
}; | ||
var generateClientApis = (apiGeneratorOptions) => { | ||
apiGeneratorOptions = validateConfig(apiGeneratorOptions); | ||
const apiFolder = (0, import_path.resolve)(process.cwd(), apiGeneratorOptions.apiDir); | ||
const apiRoutes = import_fast_glob.default.sync(`${apiFolder}/**/*.ts`); | ||
let apiRouteParseResults; | ||
switch (apiGeneratorOptions.parser) { | ||
case "baseline": | ||
apiRouteParseResults = parseApiRoutesBaseline(apiRoutes, apiGeneratorOptions.tsConfigPath); | ||
break; | ||
case "naive": | ||
default: | ||
apiRouteParseResults = parseApiRoutesNaive(apiRoutes); | ||
break; | ||
} | ||
apiRouteParseResults = apiRouteParseResults.filter((apiRoute) => apiRoute.responseInterface !== ""); | ||
apiRouteParseResults.map((result) => { | ||
result.relativePath = `api${result.apiRoute.replace(apiFolder, "")}`.trim(); | ||
result.path = `${(0, import_path.parse)(result.relativePath).dir}/${(0, import_path.parse)(result.relativePath).name}`; | ||
result.camelCaseName = result.path.replace(/^api/i, "").split("/").map((part) => upperCaseFirst(part)).join("").split(/[-_\ \.]/g).reduce((prev, current) => prev + upperCaseFirst(current), ""); | ||
return result; | ||
}); | ||
const perRoute = groupByApiRoute(apiRouteParseResults); | ||
Object.keys(perRoute).forEach((apiRoute) => { | ||
const parseResults = perRoute[apiRoute]; | ||
const clientCode = produceClientApiCode(parseResults, apiGeneratorOptions); | ||
const clientFilePath = (0, import_path.resolve)( | ||
process.cwd(), | ||
apiGeneratorOptions.outDir, | ||
parseResults[0].relativePath.replace(/^api\//, "") | ||
); | ||
const parsed = (0, import_path.parse)(clientFilePath); | ||
const clientFileDir = parsed.dir; | ||
const finalFilePath = `${clientFileDir}${import_path.sep}${parsed.name}-client.ts`; | ||
(0, import_fs.mkdirSync)(clientFileDir, { recursive: true }); | ||
(0, import_fs.writeFileSync)(finalFilePath.toLowerCase(), clientCode, { encoding: "utf-8" }); | ||
}); | ||
}; | ||
var produceClientApiRequestImplCode = (parseResult, apiGeneratorOptions, requestParamDecl, bodyInst, hasMultipleEndpointsInOneFile) => { | ||
const methodNameAppendix = hasMultipleEndpointsInOneFile ? upperCaseFirst(parseResult.method.toLowerCase()) : ""; | ||
return ` | ||
/** return (await fetch('${apiGeneratorOptions.baseUrl}/${parseResult.path}', { method: '${parseResult.method}', ... })).json() */ | ||
export const ${lowerCaseFirst( | ||
parseResult.camelCaseName | ||
)}${methodNameAppendix} = async(${requestParamDecl}options: RequestOptions = {}): Promise<ApiResponse> => { | ||
let requestUrl = '${apiGeneratorOptions.site}${apiGeneratorOptions.baseUrl}/${parseResult.path}' | ||
/** return (await fetch('${t.baseUrl}/${e.path}', { method: '${e.method}', ... })).json() */ | ||
export const ${L(e.camelCaseName)}${r} = async(${n}options: RequestOptions = {}): Promise<ApiResponse> => { | ||
let requestUrl = '${t.site}${t.baseUrl}/${e.path}' | ||
if (options && options.query) { | ||
@@ -297,13 +14,13 @@ requestUrl += '?' + Object.keys(options.query) | ||
delete options.query | ||
options.method = '${parseResult.method}' | ||
${bodyInst} | ||
options.method = '${e.method}' | ||
${s} | ||
return (await fetch(requestUrl, options)).json() | ||
}`; | ||
}; | ||
var produceClientApiHeaderCode = (parseResult, requestInterfaceDecl) => { | ||
return `${parseResult.imports.join("\n")} | ||
}`},U=(e,t)=>`${e.imports.join(` | ||
`)} | ||
${parseResult.genericTypes.join("\n")} | ||
${e.genericTypes.join(` | ||
`)} | ||
${parseResult.genericInterfaces.join("\n")} | ||
${e.genericInterfaces.join(` | ||
`)} | ||
@@ -318,42 +35,6 @@ export interface QueryMap { | ||
${requestInterfaceDecl} | ||
${t} | ||
${parseResult.responseInterface}`; | ||
}; | ||
var produceClientApiCode = (parseResults, apiGeneratorOptions) => { | ||
const requestInterfaceDecl = parseResults[0].requestInterface ? `${parseResults[0].requestInterface}` : ""; | ||
const requestParamDecl = parseResults[0].requestInterface ? `payload: ApiRequest, ` : ""; | ||
let bodyCode = ""; | ||
parseResults.forEach((parseResult) => { | ||
const bodyInst = parseResult.method !== "HEAD" && parseResult.method !== "GET" && parseResult.requestInterface ? "options.body = JSON.stringify(payload)" : ""; | ||
bodyCode += produceClientApiRequestImplCode( | ||
parseResult, | ||
apiGeneratorOptions, | ||
requestParamDecl, | ||
bodyInst, | ||
parseResults.length > 1 | ||
); | ||
}); | ||
return `${produceClientApiHeaderCode(parseResults[0], requestInterfaceDecl)} | ||
${bodyCode}`; | ||
}; | ||
// Annotate the CommonJS export names for ESM import in node: | ||
0 && (module.exports = { | ||
AstroHttpEndpointMethodNames, | ||
HttpMethods, | ||
analyzeHttpMethodsImplemented, | ||
apiClientGenerator, | ||
apiGeneratorOptionsDefaults, | ||
cleanupInterfce, | ||
generateClientApis, | ||
groupByApiRoute, | ||
lowerCaseFirst, | ||
parseApiRoutesBaseline, | ||
parseApiRoutesNaive, | ||
produceClientApiCode, | ||
produceClientApiHeaderCode, | ||
produceClientApiRequestImplCode, | ||
upperCaseFirst, | ||
validateConfig | ||
}); | ||
${e.responseInterface}`,w=(e,t)=>{let n=e[0].requestInterface?`${e[0].requestInterface}`:"",s=e[0].requestInterface?"payload: ApiRequest, ":"",i="";return e.forEach(r=>{let p=r.method!=="HEAD"&&r.method!=="GET"&&r.requestInterface?"options.body = JSON.stringify(payload)":"";i+=N(r,t,s,p,e.length>1)}),`${U(e[0],n)} | ||
${i}`};0&&(module.exports={AstroHttpEndpointMethodNames,HttpMethods,analyzeHttpMethodsImplemented,apiClientGenerator,apiGeneratorOptionsDefaults,cleanupInterfce,generateClientApis,groupByApiRoute,lowerCaseFirst,parseApiRoutesBaseline,parseApiRoutesNaive,produceClientApiCode,produceClientApiHeaderCode,produceClientApiRequestImplCode,upperCaseFirst,validateConfig}); | ||
//# sourceMappingURL=index.cjs.js.map |
@@ -1,243 +0,8 @@ | ||
// src/index.ts | ||
import fastGlob from "fast-glob"; | ||
import { resolve, parse, sep } from "path"; | ||
import { readFileSync, mkdirSync, writeFileSync } from "fs"; | ||
import { Project } from "ts-morph"; | ||
var apiGeneratorOptionsDefaults = { | ||
apiDir: "./src/pages/api", | ||
baseUrl: "", | ||
outDir: "./src/pages/api-client", | ||
tsConfigPath: "./tsconfig.json", | ||
site: "http://localhost:3000" | ||
}; | ||
var AstroHttpEndpointMethodNames = ["GET", "POST", "DEL", "PATCH", "HEAD", "PUT", "OPTIONS", "ALL"]; | ||
var HttpMethods = ["POST", "DELETE", "GET", "PATCH", "HEAD", "PUT", "OPTIONS"]; | ||
var upperCaseFirst = (text) => `${text.charAt(0).toUpperCase()}${text.slice(1)}`; | ||
var lowerCaseFirst = (text) => `${text.charAt(0).toLowerCase()}${text.slice(1)}`; | ||
var cleanupInterfce = (codeLines) => codeLines.join("\n").trim(); | ||
var analyzeHttpMethodsImplemented = (code) => { | ||
const methods = []; | ||
if (code.indexOf("function get") > -1 || code.indexOf("export const get") > -1) { | ||
methods.push("GET"); | ||
} | ||
if (code.indexOf("function post") > -1 || code.indexOf("export const post") > -1) { | ||
methods.push("POST"); | ||
} | ||
if (code.indexOf("function del") > -1 || code.indexOf("export const del") > -1) { | ||
methods.push("DELETE"); | ||
} | ||
if (code.indexOf("function patch") > -1 || code.indexOf("export const patch") > -1) { | ||
methods.push("PATCH"); | ||
} | ||
if (code.indexOf("function head") > -1 || code.indexOf("export const head") > -1) { | ||
methods.push("HEAD"); | ||
} | ||
if (code.indexOf("function put") > -1 || code.indexOf("export const put") > -1) { | ||
methods.push("PUT"); | ||
} | ||
if (code.indexOf("function options") > -1 || code.indexOf("export const options") > -1) { | ||
methods.push("OPTIONS"); | ||
} | ||
if (code.indexOf("function all") > -1 || code.indexOf("export const all") > -1) { | ||
return HttpMethods; | ||
} | ||
return methods; | ||
}; | ||
var validateConfig = (apiGeneratorOptions) => ({ | ||
...apiGeneratorOptionsDefaults, | ||
...apiGeneratorOptions | ||
}); | ||
var apiClientGenerator = (apiGeneratorOptions = apiGeneratorOptionsDefaults) => { | ||
apiGeneratorOptions = validateConfig(apiGeneratorOptions); | ||
return { | ||
name: "astro-client-generator", | ||
hooks: { | ||
"astro:build:done": async () => { | ||
generateClientApis(apiGeneratorOptions); | ||
} | ||
} | ||
}; | ||
}; | ||
var parseApiRoutesBaseline = (apiRoutes, tsConfigPath) => { | ||
const project = new Project({ | ||
tsConfigFilePath: tsConfigPath | ||
}); | ||
const apiRouteParseResults = []; | ||
apiRoutes.forEach((apiRoute) => { | ||
const sourceFile = project.getSourceFile(apiRoute); | ||
const imports = sourceFile.getImportDeclarations().map((d) => d.getFullText()); | ||
const genericInterfaces = []; | ||
let requestInterface = ""; | ||
let responseInterface = ""; | ||
sourceFile.getInterfaces().map((d) => { | ||
switch (d.getName()) { | ||
case "ApiRequest": | ||
requestInterface = d.getText(); | ||
break; | ||
case "ApiResponse": | ||
responseInterface = d.getText(); | ||
break; | ||
default: | ||
genericInterfaces.push(d.getText()); | ||
} | ||
}); | ||
const genericTypes = sourceFile.getTypeAliases().map((d) => d.getText()); | ||
let methods = []; | ||
sourceFile.getExportSymbols().forEach((d) => { | ||
const methodIndex = AstroHttpEndpointMethodNames.indexOf(d.getName().toUpperCase()); | ||
if (methodIndex > -1) { | ||
const astroMethod = AstroHttpEndpointMethodNames[methodIndex]; | ||
if (astroMethod === "DEL") { | ||
methods.push("DELETE"); | ||
} else if (astroMethod === "ALL") { | ||
methods = HttpMethods; | ||
} else { | ||
methods.push(astroMethod); | ||
} | ||
} | ||
}); | ||
methods.forEach((method) => { | ||
apiRouteParseResults.push({ | ||
apiRoute, | ||
imports, | ||
method, | ||
requestInterface, | ||
responseInterface, | ||
genericInterfaces, | ||
genericTypes | ||
}); | ||
}); | ||
}); | ||
return apiRouteParseResults; | ||
}; | ||
var parseApiRoutesNaive = (apiRoutes) => { | ||
const apiRouteParseResults = []; | ||
apiRoutes.forEach((apiRoute) => { | ||
const impl = readFileSync(apiRoute, { encoding: "utf-8" }); | ||
const codeLines = impl.split("\n"); | ||
const imports = []; | ||
const genericInterfaces = []; | ||
const genericTypes = []; | ||
let genericInterfaceDeclBlockStartLine; | ||
let apiRequestDeclBlockStartLine; | ||
let apiResponseDeclBlockStartLine; | ||
let interfaceDeclBlockIndent = 0; | ||
let requestInterfaceCode = [-1, -1]; | ||
let responseInterfaceCode = [-1, -1]; | ||
codeLines.forEach((line, i) => { | ||
const isImport = /^import (.*) from (.*)/.test(line.trim()); | ||
const isInterfaceDeclLine = line.indexOf("interface ") > -1; | ||
const isTypeDeclLine = /^\s*(?:export\s+)?type\s+\w+\s*=\s*[\w<>,\s|]+\s*;?\s*$/.test(line.trim()); | ||
const isApiRequestDeclLine = line.indexOf(" ApiRequest ") > -1; | ||
const isApiResponseDeclLine = line.indexOf(" ApiResponse ") > -1; | ||
const isOpeningBracketLine = line.indexOf("{") > -1; | ||
const isClosingBracketLine = line.indexOf("}") > -1; | ||
if (isImport) { | ||
imports.push(line); | ||
return; | ||
} | ||
if (isTypeDeclLine) { | ||
genericTypes.push(line); | ||
return; | ||
} | ||
if (isInterfaceDeclLine && isApiRequestDeclLine) { | ||
apiRequestDeclBlockStartLine = i; | ||
} | ||
if (isInterfaceDeclLine && isApiResponseDeclLine) { | ||
apiResponseDeclBlockStartLine = i; | ||
} | ||
if (isInterfaceDeclLine && apiRequestDeclBlockStartLine !== i && apiResponseDeclBlockStartLine !== i) { | ||
genericInterfaceDeclBlockStartLine = i; | ||
} | ||
if ((apiRequestDeclBlockStartLine || apiResponseDeclBlockStartLine || genericInterfaceDeclBlockStartLine) && isOpeningBracketLine) { | ||
interfaceDeclBlockIndent++; | ||
} | ||
if ((apiRequestDeclBlockStartLine || apiResponseDeclBlockStartLine || genericInterfaceDeclBlockStartLine) && isClosingBracketLine) { | ||
interfaceDeclBlockIndent--; | ||
if (interfaceDeclBlockIndent === 0) { | ||
if (apiRequestDeclBlockStartLine) { | ||
requestInterfaceCode = [apiRequestDeclBlockStartLine, i]; | ||
apiRequestDeclBlockStartLine = void 0; | ||
} | ||
if (apiResponseDeclBlockStartLine) { | ||
responseInterfaceCode = [apiResponseDeclBlockStartLine, i]; | ||
apiResponseDeclBlockStartLine = void 0; | ||
} | ||
if (genericInterfaceDeclBlockStartLine) { | ||
genericInterfaces.push(cleanupInterfce(codeLines.slice(genericInterfaceDeclBlockStartLine, i + 1))); | ||
genericInterfaceDeclBlockStartLine = void 0; | ||
} | ||
} | ||
} | ||
}); | ||
analyzeHttpMethodsImplemented(impl).forEach((method) => { | ||
apiRouteParseResults.push({ | ||
apiRoute, | ||
imports, | ||
method, | ||
requestInterface: cleanupInterfce(codeLines.slice(requestInterfaceCode[0], requestInterfaceCode[1] + 1)), | ||
responseInterface: cleanupInterfce(codeLines.slice(responseInterfaceCode[0], responseInterfaceCode[1] + 1)), | ||
genericInterfaces, | ||
genericTypes | ||
}); | ||
}); | ||
}); | ||
return apiRouteParseResults; | ||
}; | ||
var groupByApiRoute = (routes) => { | ||
return routes.reduce((acc, file) => { | ||
const { path } = file; | ||
if (!acc[path]) { | ||
acc[path] = []; | ||
} | ||
acc[path].push(file); | ||
return acc; | ||
}, {}); | ||
}; | ||
var generateClientApis = (apiGeneratorOptions) => { | ||
apiGeneratorOptions = validateConfig(apiGeneratorOptions); | ||
const apiFolder = resolve(process.cwd(), apiGeneratorOptions.apiDir); | ||
const apiRoutes = fastGlob.sync(`${apiFolder}/**/*.ts`); | ||
let apiRouteParseResults; | ||
switch (apiGeneratorOptions.parser) { | ||
case "baseline": | ||
apiRouteParseResults = parseApiRoutesBaseline(apiRoutes, apiGeneratorOptions.tsConfigPath); | ||
break; | ||
case "naive": | ||
default: | ||
apiRouteParseResults = parseApiRoutesNaive(apiRoutes); | ||
break; | ||
} | ||
apiRouteParseResults = apiRouteParseResults.filter((apiRoute) => apiRoute.responseInterface !== ""); | ||
apiRouteParseResults.map((result) => { | ||
result.relativePath = `api${result.apiRoute.replace(apiFolder, "")}`.trim(); | ||
result.path = `${parse(result.relativePath).dir}/${parse(result.relativePath).name}`; | ||
result.camelCaseName = result.path.replace(/^api/i, "").split("/").map((part) => upperCaseFirst(part)).join("").split(/[-_\ \.]/g).reduce((prev, current) => prev + upperCaseFirst(current), ""); | ||
return result; | ||
}); | ||
const perRoute = groupByApiRoute(apiRouteParseResults); | ||
Object.keys(perRoute).forEach((apiRoute) => { | ||
const parseResults = perRoute[apiRoute]; | ||
const clientCode = produceClientApiCode(parseResults, apiGeneratorOptions); | ||
const clientFilePath = resolve( | ||
process.cwd(), | ||
apiGeneratorOptions.outDir, | ||
parseResults[0].relativePath.replace(/^api\//, "") | ||
); | ||
const parsed = parse(clientFilePath); | ||
const clientFileDir = parsed.dir; | ||
const finalFilePath = `${clientFileDir}${sep}${parsed.name}-client.ts`; | ||
mkdirSync(clientFileDir, { recursive: true }); | ||
writeFileSync(finalFilePath.toLowerCase(), clientCode, { encoding: "utf-8" }); | ||
}); | ||
}; | ||
var produceClientApiRequestImplCode = (parseResult, apiGeneratorOptions, requestParamDecl, bodyInst, hasMultipleEndpointsInOneFile) => { | ||
const methodNameAppendix = hasMultipleEndpointsInOneFile ? upperCaseFirst(parseResult.method.toLowerCase()) : ""; | ||
return ` | ||
import b from"fast-glob";import{resolve as y,parse as m,sep as H}from"path";import{readFileSync as S,mkdirSync as k,writeFileSync as N}from"fs";import{Project as U}from"ts-morph";var E={apiDir:"./src/pages/api",baseUrl:"",outDir:"./src/pages/api-client",tsConfigPath:"./tsconfig.json",site:"http://localhost:3000"},T=["GET","POST","DELETE","PATCH","HEAD","PUT","OPTIONS","ALL"],O=["POST","DELETE","GET","PATCH","HEAD","PUT","OPTIONS"],x=e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}`,w=e=>`${e.charAt(0).toLowerCase()}${e.slice(1)}`,P=e=>e.join(` | ||
`).trim(),j=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?O:t},I=e=>({...E,...e}),W=(e=E)=>(e=I(e),{name:"astro-client-generator",hooks:{"astro:build:done":async()=>{G(e)}}}),M=(e,t)=>{let a=new U({tsConfigFilePath:t}),n=[];return e.forEach(i=>{let r=a.getSourceFile(i),p=r.getImportDeclarations().map(s=>s.getFullText()),A=[],u="",c="";r.getInterfaces().map(s=>{switch(s.getName()){case"ApiRequest":u=s.getText();break;case"ApiResponse":c=s.getText();break;default:A.push(s.getText())}});let f=r.getTypeAliases().map(s=>s.getText()),l=[];r.getExportSymbols().forEach(s=>{let h=T.indexOf(s.getName().toUpperCase());if(h>-1){let o=T[h];o==="DELETE"?l.push("DELETE"):o==="ALL"?l=O:l.push(o)}}),l.forEach(s=>{n.push({apiRoute:i,imports:p,method:s,requestInterface:u,responseInterface:c,genericInterfaces:A,genericTypes:f})})}),n},F=e=>{let t=[];return e.forEach(a=>{let n=S(a,{encoding:"utf-8"}),i=n.split(` | ||
`),r=[],p=[],A=[],u,c,f,l=0,s=[-1,-1],h=[-1,-1];i.forEach((o,d)=>{let C=/^import (.*) from (.*)/.test(o.trim()),g=o.indexOf("interface ")>-1,R=/^\s*(?:export\s+)?type\s+\w+\s*=\s*[\w<>,\s|]+\s*;?\s*$/.test(o.trim()),D=o.indexOf(" ApiRequest ")>-1,$=o.indexOf(" ApiResponse ")>-1,L=o.indexOf("{")>-1,q=o.indexOf("}")>-1;if(C){r.push(o);return}if(R){A.push(o);return}g&&D&&(c=d),g&&$&&(f=d),g&&c!==d&&f!==d&&(u=d),(c||f||u)&&L&&l++,(c||f||u)&&q&&(l--,l===0&&(c&&(s=[c,d],c=void 0),f&&(h=[f,d],f=void 0),u&&(p.push(P(i.slice(u,d+1))),u=void 0)))}),j(n).forEach(o=>{t.push({apiRoute:a,imports:r,method:o,requestInterface:P(i.slice(s[0],s[1]+1)),responseInterface:P(i.slice(h[0],h[1]+1)),genericInterfaces:p,genericTypes:A})})}),t},v=e=>e.reduce((t,a)=>{let{path:n}=a;return t[n]||(t[n]=[]),t[n].push(a),t},{}),G=e=>{e=I(e);let t=y(process.cwd(),e.apiDir),a=b.sync(`${t}/**/*.ts`),n;switch(e.parser){case"baseline":n=M(a,e.tsConfigPath);break;case"naive":default:n=F(a);break}n=n.filter(r=>r.responseInterface!==""),n.map(r=>(r.relativePath=`api${r.apiRoute.replace(t,"")}`.trim(),r.path=`${m(r.relativePath).dir}/${m(r.relativePath).name}`,r.camelCaseName=r.path.replace(/^api/i,"").split("/").map(p=>x(p)).join("").split(/[-_\ \.]/g).reduce((p,A)=>p+x(A),""),r));let i=v(n);Object.keys(i).forEach(r=>{let p=i[r],A=z(p,e),u=y(process.cwd(),e.outDir,p[0].relativePath.replace(/^api\//,"")),c=m(u),f=c.dir,l=`${f}${H}${c.name}-client.ts`;k(f,{recursive:!0}),N(l.toLowerCase(),A,{encoding:"utf-8"})})},B=(e,t,a,n,i)=>{let r=i?x(e.method.toLowerCase()):"";return` | ||
/** return (await fetch('${apiGeneratorOptions.baseUrl}/${parseResult.path}', { method: '${parseResult.method}', ... })).json() */ | ||
export const ${lowerCaseFirst( | ||
parseResult.camelCaseName | ||
)}${methodNameAppendix} = async(${requestParamDecl}options: RequestOptions = {}): Promise<ApiResponse> => { | ||
let requestUrl = '${apiGeneratorOptions.site}${apiGeneratorOptions.baseUrl}/${parseResult.path}' | ||
/** return (await fetch('${t.baseUrl}/${e.path}', { method: '${e.method}', ... })).json() */ | ||
export const ${w(e.camelCaseName)}${r} = async(${a}options: RequestOptions = {}): Promise<ApiResponse> => { | ||
let requestUrl = '${t.site}${t.baseUrl}/${e.path}' | ||
if (options && options.query) { | ||
@@ -249,13 +14,13 @@ requestUrl += '?' + Object.keys(options.query) | ||
delete options.query | ||
options.method = '${parseResult.method}' | ||
${bodyInst} | ||
options.method = '${e.method}' | ||
${n} | ||
return (await fetch(requestUrl, options)).json() | ||
}`; | ||
}; | ||
var produceClientApiHeaderCode = (parseResult, requestInterfaceDecl) => { | ||
return `${parseResult.imports.join("\n")} | ||
}`},Q=(e,t)=>`${e.imports.join(` | ||
`)} | ||
${parseResult.genericTypes.join("\n")} | ||
${e.genericTypes.join(` | ||
`)} | ||
${parseResult.genericInterfaces.join("\n")} | ||
${e.genericInterfaces.join(` | ||
`)} | ||
@@ -270,41 +35,6 @@ export interface QueryMap { | ||
${requestInterfaceDecl} | ||
${t} | ||
${parseResult.responseInterface}`; | ||
}; | ||
var produceClientApiCode = (parseResults, apiGeneratorOptions) => { | ||
const requestInterfaceDecl = parseResults[0].requestInterface ? `${parseResults[0].requestInterface}` : ""; | ||
const requestParamDecl = parseResults[0].requestInterface ? `payload: ApiRequest, ` : ""; | ||
let bodyCode = ""; | ||
parseResults.forEach((parseResult) => { | ||
const bodyInst = parseResult.method !== "HEAD" && parseResult.method !== "GET" && parseResult.requestInterface ? "options.body = JSON.stringify(payload)" : ""; | ||
bodyCode += produceClientApiRequestImplCode( | ||
parseResult, | ||
apiGeneratorOptions, | ||
requestParamDecl, | ||
bodyInst, | ||
parseResults.length > 1 | ||
); | ||
}); | ||
return `${produceClientApiHeaderCode(parseResults[0], requestInterfaceDecl)} | ||
${bodyCode}`; | ||
}; | ||
export { | ||
AstroHttpEndpointMethodNames, | ||
HttpMethods, | ||
analyzeHttpMethodsImplemented, | ||
apiClientGenerator, | ||
apiGeneratorOptionsDefaults, | ||
cleanupInterfce, | ||
generateClientApis, | ||
groupByApiRoute, | ||
lowerCaseFirst, | ||
parseApiRoutesBaseline, | ||
parseApiRoutesNaive, | ||
produceClientApiCode, | ||
produceClientApiHeaderCode, | ||
produceClientApiRequestImplCode, | ||
upperCaseFirst, | ||
validateConfig | ||
}; | ||
${e.responseInterface}`,z=(e,t)=>{let a=e[0].requestInterface?`${e[0].requestInterface}`:"",n=e[0].requestInterface?"payload: ApiRequest, ":"",i="";return e.forEach(r=>{let p=r.method!=="HEAD"&&r.method!=="GET"&&r.requestInterface?"options.body = JSON.stringify(payload)":"";i+=B(r,t,n,p,e.length>1)}),`${Q(e[0],a)} | ||
${i}`};export{T as AstroHttpEndpointMethodNames,O as HttpMethods,j as analyzeHttpMethodsImplemented,W as apiClientGenerator,E as apiGeneratorOptionsDefaults,P as cleanupInterfce,G as generateClientApis,v as groupByApiRoute,w as lowerCaseFirst,M as parseApiRoutesBaseline,F as parseApiRoutesNaive,z as produceClientApiCode,Q as produceClientApiHeaderCode,B as produceClientApiRequestImplCode,x as upperCaseFirst,I as validateConfig}; | ||
//# sourceMappingURL=index.esm.js.map |
@@ -1,274 +0,8 @@ | ||
(() => { | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { | ||
get: (a, b) => (typeof require !== "undefined" ? require : a)[b] | ||
}) : x)(function(x) { | ||
if (typeof require !== "undefined") | ||
return require.apply(this, arguments); | ||
throw new Error('Dynamic require of "' + x + '" is not supported'); | ||
}); | ||
var __copyProps = (to, from, except, desc) => { | ||
if (from && typeof from === "object" || typeof from === "function") { | ||
for (let key of __getOwnPropNames(from)) | ||
if (!__hasOwnProp.call(to, key) && key !== except) | ||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
} | ||
return to; | ||
}; | ||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
// If the importer is in node compatibility mode or this is not an ESM | ||
// file that has been converted to a CommonJS file using a Babel- | ||
// compatible transform (i.e. "__esModule" has not been set), then set | ||
// "default" to the CommonJS "module.exports" for node compatibility. | ||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||
mod | ||
)); | ||
(()=>{var N=Object.create;var E=Object.defineProperty;var U=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var j=Object.getPrototypeOf,M=Object.prototype.hasOwnProperty;var P=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,s)=>(typeof require<"u"?require:t)[s]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw new Error('Dynamic require of "'+e+'" is not supported')});var F=(e,t,s,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of w(t))!M.call(e,i)&&i!==s&&E(e,i,{get:()=>t[i],enumerable:!(n=U(t,i))||n.enumerable});return e};var v=(e,t,s)=>(s=e!=null?N(j(e)):{},F(t||!e||!e.__esModule?E(s,"default",{value:e,enumerable:!0}):s,e));var I=v(P("fast-glob"),1),d=P("path"),m=P("fs"),C=P("ts-morph"),R={apiDir:"./src/pages/api",baseUrl:"",outDir:"./src/pages/api-client",tsConfigPath:"./tsconfig.json",site:"http://localhost:3000"},O=["GET","POST","DELETE","PATCH","HEAD","PUT","OPTIONS","ALL"],D=["POST","DELETE","GET","PATCH","HEAD","PUT","OPTIONS"],T=e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}`,G=e=>`${e.charAt(0).toLowerCase()}${e.slice(1)}`,y=e=>e.join(` | ||
`).trim(),B=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?D:t},$=e=>({...R,...e}),Y=(e=R)=>(e=$(e),{name:"astro-client-generator",hooks:{"astro:build:done":async()=>{_(e)}}}),Q=(e,t)=>{let s=new C.Project({tsConfigFilePath:t}),n=[];return e.forEach(i=>{let r=s.getSourceFile(i),p=r.getImportDeclarations().map(o=>o.getFullText()),A=[],u="",c="";r.getInterfaces().map(o=>{switch(o.getName()){case"ApiRequest":u=o.getText();break;case"ApiResponse":c=o.getText();break;default:A.push(o.getText())}});let f=r.getTypeAliases().map(o=>o.getText()),l=[];r.getExportSymbols().forEach(o=>{let g=O.indexOf(o.getName().toUpperCase());if(g>-1){let a=O[g];a==="DELETE"?l.push("DELETE"):a==="ALL"?l=D:l.push(a)}}),l.forEach(o=>{n.push({apiRoute:i,imports:p,method:o,requestInterface:u,responseInterface:c,genericInterfaces:A,genericTypes:f})})}),n},z=e=>{let t=[];return e.forEach(s=>{let n=(0,m.readFileSync)(s,{encoding:"utf-8"}),i=n.split(` | ||
`),r=[],p=[],A=[],u,c,f,l=0,o=[-1,-1],g=[-1,-1];i.forEach((a,h)=>{let L=/^import (.*) from (.*)/.test(a.trim()),x=a.indexOf("interface ")>-1,q=/^\s*(?:export\s+)?type\s+\w+\s*=\s*[\w<>,\s|]+\s*;?\s*$/.test(a.trim()),b=a.indexOf(" ApiRequest ")>-1,H=a.indexOf(" ApiResponse ")>-1,S=a.indexOf("{")>-1,k=a.indexOf("}")>-1;if(L){r.push(a);return}if(q){A.push(a);return}x&&b&&(c=h),x&&H&&(f=h),x&&c!==h&&f!==h&&(u=h),(c||f||u)&&S&&l++,(c||f||u)&&k&&(l--,l===0&&(c&&(o=[c,h],c=void 0),f&&(g=[f,h],f=void 0),u&&(p.push(y(i.slice(u,h+1))),u=void 0)))}),B(n).forEach(a=>{t.push({apiRoute:s,imports:r,method:a,requestInterface:y(i.slice(o[0],o[1]+1)),responseInterface:y(i.slice(g[0],g[1]+1)),genericInterfaces:p,genericTypes:A})})}),t},J=e=>e.reduce((t,s)=>{let{path:n}=s;return t[n]||(t[n]=[]),t[n].push(s),t},{}),_=e=>{e=$(e);let t=(0,d.resolve)(process.cwd(),e.apiDir),s=I.default.sync(`${t}/**/*.ts`),n;switch(e.parser){case"baseline":n=Q(s,e.tsConfigPath);break;case"naive":default:n=z(s);break}n=n.filter(r=>r.responseInterface!==""),n.map(r=>(r.relativePath=`api${r.apiRoute.replace(t,"")}`.trim(),r.path=`${(0,d.parse)(r.relativePath).dir}/${(0,d.parse)(r.relativePath).name}`,r.camelCaseName=r.path.replace(/^api/i,"").split("/").map(p=>T(p)).join("").split(/[-_\ \.]/g).reduce((p,A)=>p+T(A),""),r));let i=J(n);Object.keys(i).forEach(r=>{let p=i[r],A=W(p,e),u=(0,d.resolve)(process.cwd(),e.outDir,p[0].relativePath.replace(/^api\//,"")),c=(0,d.parse)(u),f=c.dir,l=`${f}${d.sep}${c.name}-client.ts`;(0,m.mkdirSync)(f,{recursive:!0}),(0,m.writeFileSync)(l.toLowerCase(),A,{encoding:"utf-8"})})},K=(e,t,s,n,i)=>{let r=i?T(e.method.toLowerCase()):"";return` | ||
// src/index.ts | ||
var import_fast_glob = __toESM(__require("fast-glob"), 1); | ||
var import_path = __require("path"); | ||
var import_fs = __require("fs"); | ||
var import_ts_morph = __require("ts-morph"); | ||
var apiGeneratorOptionsDefaults = { | ||
apiDir: "./src/pages/api", | ||
baseUrl: "", | ||
outDir: "./src/pages/api-client", | ||
tsConfigPath: "./tsconfig.json", | ||
site: "http://localhost:3000" | ||
}; | ||
var AstroHttpEndpointMethodNames = ["GET", "POST", "DEL", "PATCH", "HEAD", "PUT", "OPTIONS", "ALL"]; | ||
var HttpMethods = ["POST", "DELETE", "GET", "PATCH", "HEAD", "PUT", "OPTIONS"]; | ||
var upperCaseFirst = (text) => `${text.charAt(0).toUpperCase()}${text.slice(1)}`; | ||
var lowerCaseFirst = (text) => `${text.charAt(0).toLowerCase()}${text.slice(1)}`; | ||
var cleanupInterfce = (codeLines) => codeLines.join("\n").trim(); | ||
var analyzeHttpMethodsImplemented = (code) => { | ||
const methods = []; | ||
if (code.indexOf("function get") > -1 || code.indexOf("export const get") > -1) { | ||
methods.push("GET"); | ||
} | ||
if (code.indexOf("function post") > -1 || code.indexOf("export const post") > -1) { | ||
methods.push("POST"); | ||
} | ||
if (code.indexOf("function del") > -1 || code.indexOf("export const del") > -1) { | ||
methods.push("DELETE"); | ||
} | ||
if (code.indexOf("function patch") > -1 || code.indexOf("export const patch") > -1) { | ||
methods.push("PATCH"); | ||
} | ||
if (code.indexOf("function head") > -1 || code.indexOf("export const head") > -1) { | ||
methods.push("HEAD"); | ||
} | ||
if (code.indexOf("function put") > -1 || code.indexOf("export const put") > -1) { | ||
methods.push("PUT"); | ||
} | ||
if (code.indexOf("function options") > -1 || code.indexOf("export const options") > -1) { | ||
methods.push("OPTIONS"); | ||
} | ||
if (code.indexOf("function all") > -1 || code.indexOf("export const all") > -1) { | ||
return HttpMethods; | ||
} | ||
return methods; | ||
}; | ||
var validateConfig = (apiGeneratorOptions) => ({ | ||
...apiGeneratorOptionsDefaults, | ||
...apiGeneratorOptions | ||
}); | ||
var apiClientGenerator = (apiGeneratorOptions = apiGeneratorOptionsDefaults) => { | ||
apiGeneratorOptions = validateConfig(apiGeneratorOptions); | ||
return { | ||
name: "astro-client-generator", | ||
hooks: { | ||
"astro:build:done": async () => { | ||
generateClientApis(apiGeneratorOptions); | ||
} | ||
} | ||
}; | ||
}; | ||
var parseApiRoutesBaseline = (apiRoutes, tsConfigPath) => { | ||
const project = new import_ts_morph.Project({ | ||
tsConfigFilePath: tsConfigPath | ||
}); | ||
const apiRouteParseResults = []; | ||
apiRoutes.forEach((apiRoute) => { | ||
const sourceFile = project.getSourceFile(apiRoute); | ||
const imports = sourceFile.getImportDeclarations().map((d) => d.getFullText()); | ||
const genericInterfaces = []; | ||
let requestInterface = ""; | ||
let responseInterface = ""; | ||
sourceFile.getInterfaces().map((d) => { | ||
switch (d.getName()) { | ||
case "ApiRequest": | ||
requestInterface = d.getText(); | ||
break; | ||
case "ApiResponse": | ||
responseInterface = d.getText(); | ||
break; | ||
default: | ||
genericInterfaces.push(d.getText()); | ||
} | ||
}); | ||
const genericTypes = sourceFile.getTypeAliases().map((d) => d.getText()); | ||
let methods = []; | ||
sourceFile.getExportSymbols().forEach((d) => { | ||
const methodIndex = AstroHttpEndpointMethodNames.indexOf(d.getName().toUpperCase()); | ||
if (methodIndex > -1) { | ||
const astroMethod = AstroHttpEndpointMethodNames[methodIndex]; | ||
if (astroMethod === "DEL") { | ||
methods.push("DELETE"); | ||
} else if (astroMethod === "ALL") { | ||
methods = HttpMethods; | ||
} else { | ||
methods.push(astroMethod); | ||
} | ||
} | ||
}); | ||
methods.forEach((method) => { | ||
apiRouteParseResults.push({ | ||
apiRoute, | ||
imports, | ||
method, | ||
requestInterface, | ||
responseInterface, | ||
genericInterfaces, | ||
genericTypes | ||
}); | ||
}); | ||
}); | ||
return apiRouteParseResults; | ||
}; | ||
var parseApiRoutesNaive = (apiRoutes) => { | ||
const apiRouteParseResults = []; | ||
apiRoutes.forEach((apiRoute) => { | ||
const impl = (0, import_fs.readFileSync)(apiRoute, { encoding: "utf-8" }); | ||
const codeLines = impl.split("\n"); | ||
const imports = []; | ||
const genericInterfaces = []; | ||
const genericTypes = []; | ||
let genericInterfaceDeclBlockStartLine; | ||
let apiRequestDeclBlockStartLine; | ||
let apiResponseDeclBlockStartLine; | ||
let interfaceDeclBlockIndent = 0; | ||
let requestInterfaceCode = [-1, -1]; | ||
let responseInterfaceCode = [-1, -1]; | ||
codeLines.forEach((line, i) => { | ||
const isImport = /^import (.*) from (.*)/.test(line.trim()); | ||
const isInterfaceDeclLine = line.indexOf("interface ") > -1; | ||
const isTypeDeclLine = /^\s*(?:export\s+)?type\s+\w+\s*=\s*[\w<>,\s|]+\s*;?\s*$/.test(line.trim()); | ||
const isApiRequestDeclLine = line.indexOf(" ApiRequest ") > -1; | ||
const isApiResponseDeclLine = line.indexOf(" ApiResponse ") > -1; | ||
const isOpeningBracketLine = line.indexOf("{") > -1; | ||
const isClosingBracketLine = line.indexOf("}") > -1; | ||
if (isImport) { | ||
imports.push(line); | ||
return; | ||
} | ||
if (isTypeDeclLine) { | ||
genericTypes.push(line); | ||
return; | ||
} | ||
if (isInterfaceDeclLine && isApiRequestDeclLine) { | ||
apiRequestDeclBlockStartLine = i; | ||
} | ||
if (isInterfaceDeclLine && isApiResponseDeclLine) { | ||
apiResponseDeclBlockStartLine = i; | ||
} | ||
if (isInterfaceDeclLine && apiRequestDeclBlockStartLine !== i && apiResponseDeclBlockStartLine !== i) { | ||
genericInterfaceDeclBlockStartLine = i; | ||
} | ||
if ((apiRequestDeclBlockStartLine || apiResponseDeclBlockStartLine || genericInterfaceDeclBlockStartLine) && isOpeningBracketLine) { | ||
interfaceDeclBlockIndent++; | ||
} | ||
if ((apiRequestDeclBlockStartLine || apiResponseDeclBlockStartLine || genericInterfaceDeclBlockStartLine) && isClosingBracketLine) { | ||
interfaceDeclBlockIndent--; | ||
if (interfaceDeclBlockIndent === 0) { | ||
if (apiRequestDeclBlockStartLine) { | ||
requestInterfaceCode = [apiRequestDeclBlockStartLine, i]; | ||
apiRequestDeclBlockStartLine = void 0; | ||
} | ||
if (apiResponseDeclBlockStartLine) { | ||
responseInterfaceCode = [apiResponseDeclBlockStartLine, i]; | ||
apiResponseDeclBlockStartLine = void 0; | ||
} | ||
if (genericInterfaceDeclBlockStartLine) { | ||
genericInterfaces.push(cleanupInterfce(codeLines.slice(genericInterfaceDeclBlockStartLine, i + 1))); | ||
genericInterfaceDeclBlockStartLine = void 0; | ||
} | ||
} | ||
} | ||
}); | ||
analyzeHttpMethodsImplemented(impl).forEach((method) => { | ||
apiRouteParseResults.push({ | ||
apiRoute, | ||
imports, | ||
method, | ||
requestInterface: cleanupInterfce(codeLines.slice(requestInterfaceCode[0], requestInterfaceCode[1] + 1)), | ||
responseInterface: cleanupInterfce(codeLines.slice(responseInterfaceCode[0], responseInterfaceCode[1] + 1)), | ||
genericInterfaces, | ||
genericTypes | ||
}); | ||
}); | ||
}); | ||
return apiRouteParseResults; | ||
}; | ||
var groupByApiRoute = (routes) => { | ||
return routes.reduce((acc, file) => { | ||
const { path } = file; | ||
if (!acc[path]) { | ||
acc[path] = []; | ||
} | ||
acc[path].push(file); | ||
return acc; | ||
}, {}); | ||
}; | ||
var generateClientApis = (apiGeneratorOptions) => { | ||
apiGeneratorOptions = validateConfig(apiGeneratorOptions); | ||
const apiFolder = (0, import_path.resolve)(process.cwd(), apiGeneratorOptions.apiDir); | ||
const apiRoutes = import_fast_glob.default.sync(`${apiFolder}/**/*.ts`); | ||
let apiRouteParseResults; | ||
switch (apiGeneratorOptions.parser) { | ||
case "baseline": | ||
apiRouteParseResults = parseApiRoutesBaseline(apiRoutes, apiGeneratorOptions.tsConfigPath); | ||
break; | ||
case "naive": | ||
default: | ||
apiRouteParseResults = parseApiRoutesNaive(apiRoutes); | ||
break; | ||
} | ||
apiRouteParseResults = apiRouteParseResults.filter((apiRoute) => apiRoute.responseInterface !== ""); | ||
apiRouteParseResults.map((result) => { | ||
result.relativePath = `api${result.apiRoute.replace(apiFolder, "")}`.trim(); | ||
result.path = `${(0, import_path.parse)(result.relativePath).dir}/${(0, import_path.parse)(result.relativePath).name}`; | ||
result.camelCaseName = result.path.replace(/^api/i, "").split("/").map((part) => upperCaseFirst(part)).join("").split(/[-_\ \.]/g).reduce((prev, current) => prev + upperCaseFirst(current), ""); | ||
return result; | ||
}); | ||
const perRoute = groupByApiRoute(apiRouteParseResults); | ||
Object.keys(perRoute).forEach((apiRoute) => { | ||
const parseResults = perRoute[apiRoute]; | ||
const clientCode = produceClientApiCode(parseResults, apiGeneratorOptions); | ||
const clientFilePath = (0, import_path.resolve)( | ||
process.cwd(), | ||
apiGeneratorOptions.outDir, | ||
parseResults[0].relativePath.replace(/^api\//, "") | ||
); | ||
const parsed = (0, import_path.parse)(clientFilePath); | ||
const clientFileDir = parsed.dir; | ||
const finalFilePath = `${clientFileDir}${import_path.sep}${parsed.name}-client.ts`; | ||
(0, import_fs.mkdirSync)(clientFileDir, { recursive: true }); | ||
(0, import_fs.writeFileSync)(finalFilePath.toLowerCase(), clientCode, { encoding: "utf-8" }); | ||
}); | ||
}; | ||
var produceClientApiRequestImplCode = (parseResult, apiGeneratorOptions, requestParamDecl, bodyInst, hasMultipleEndpointsInOneFile) => { | ||
const methodNameAppendix = hasMultipleEndpointsInOneFile ? upperCaseFirst(parseResult.method.toLowerCase()) : ""; | ||
return ` | ||
/** return (await fetch('${apiGeneratorOptions.baseUrl}/${parseResult.path}', { method: '${parseResult.method}', ... })).json() */ | ||
export const ${lowerCaseFirst( | ||
parseResult.camelCaseName | ||
)}${methodNameAppendix} = async(${requestParamDecl}options: RequestOptions = {}): Promise<ApiResponse> => { | ||
let requestUrl = '${apiGeneratorOptions.site}${apiGeneratorOptions.baseUrl}/${parseResult.path}' | ||
/** return (await fetch('${t.baseUrl}/${e.path}', { method: '${e.method}', ... })).json() */ | ||
export const ${G(e.camelCaseName)}${r} = async(${s}options: RequestOptions = {}): Promise<ApiResponse> => { | ||
let requestUrl = '${t.site}${t.baseUrl}/${e.path}' | ||
if (options && options.query) { | ||
@@ -280,13 +14,13 @@ requestUrl += '?' + Object.keys(options.query) | ||
delete options.query | ||
options.method = '${parseResult.method}' | ||
${bodyInst} | ||
options.method = '${e.method}' | ||
${n} | ||
return (await fetch(requestUrl, options)).json() | ||
}`; | ||
}; | ||
var produceClientApiHeaderCode = (parseResult, requestInterfaceDecl) => { | ||
return `${parseResult.imports.join("\n")} | ||
}`},V=(e,t)=>`${e.imports.join(` | ||
`)} | ||
${parseResult.genericTypes.join("\n")} | ||
${e.genericTypes.join(` | ||
`)} | ||
${parseResult.genericInterfaces.join("\n")} | ||
${e.genericInterfaces.join(` | ||
`)} | ||
@@ -301,24 +35,6 @@ export interface QueryMap { | ||
${requestInterfaceDecl} | ||
${t} | ||
${parseResult.responseInterface}`; | ||
}; | ||
var produceClientApiCode = (parseResults, apiGeneratorOptions) => { | ||
const requestInterfaceDecl = parseResults[0].requestInterface ? `${parseResults[0].requestInterface}` : ""; | ||
const requestParamDecl = parseResults[0].requestInterface ? `payload: ApiRequest, ` : ""; | ||
let bodyCode = ""; | ||
parseResults.forEach((parseResult) => { | ||
const bodyInst = parseResult.method !== "HEAD" && parseResult.method !== "GET" && parseResult.requestInterface ? "options.body = JSON.stringify(payload)" : ""; | ||
bodyCode += produceClientApiRequestImplCode( | ||
parseResult, | ||
apiGeneratorOptions, | ||
requestParamDecl, | ||
bodyInst, | ||
parseResults.length > 1 | ||
); | ||
}); | ||
return `${produceClientApiHeaderCode(parseResults[0], requestInterfaceDecl)} | ||
${bodyCode}`; | ||
}; | ||
})(); | ||
${e.responseInterface}`,W=(e,t)=>{let s=e[0].requestInterface?`${e[0].requestInterface}`:"",n=e[0].requestInterface?"payload: ApiRequest, ":"",i="";return e.forEach(r=>{let p=r.method!=="HEAD"&&r.method!=="GET"&&r.requestInterface?"options.body = JSON.stringify(payload)":"";i+=K(r,t,n,p,e.length>1)}),`${V(e[0],s)} | ||
${i}`};})(); | ||
//# sourceMappingURL=index.iife.js.map |
{ | ||
"name": "@jsheaven/astro-client-generator", | ||
"version": "1.0.10", | ||
"version": "1.1.0", | ||
"type": "module", | ||
@@ -27,5 +27,5 @@ "publishConfig": { | ||
"scripts": { | ||
"preexample:dev": "yarn --cwd ./example/todo-list", | ||
"example:dev": "yarn --cwd ./example/todo-list build", | ||
"postexample:dev": "yarn --cwd ./example/todo-list dev", | ||
"presandbox:example:dev": "IS_SANDBOX=true yarn --cwd ./example/todo-list", | ||
"sandbox:example:dev": "IS_SANDBOX=true yarn --cwd ./example/todo-list build", | ||
"postsandbox:example:dev": "IS_SANDBOX=true yarn --cwd ./example/todo-list dev", | ||
"test": "NODE_OPTIONS='--experimental-vm-modules --enable-source-maps --no-warnings' jest ./test/*.test.ts", | ||
@@ -39,2 +39,5 @@ "clean": "rm -rf ./dist && rm -rf ./coverage", | ||
}, | ||
"stackblitz": { | ||
"startCommand": "yarn sandbox:example:dev" | ||
}, | ||
"author": "Aron Homberg <info@aron-homberg.de>", | ||
@@ -57,3 +60,3 @@ "sideEffects": false, | ||
"@types/jest": "^29.4.0", | ||
"astro": "*", | ||
"astro": "^4.1.2", | ||
"jest": "^29.4.2", | ||
@@ -65,5 +68,5 @@ "ts-jest": "^29.0.5", | ||
"kleur": "^4.1.5", | ||
"ts-morph": "^17.0.1", | ||
"ts-morph": "^21.0.1", | ||
"yargs-parser": "^21.1.1" | ||
} | ||
} |
@@ -11,2 +11,4 @@ <h1 align="center">@jsheaven/astro-client-generator</h1> | ||
See: [LIVE DEMO](https://stackblitz.com/github/jsheaven/astro-client-generator?file=example%2Ftodo-list%2Fastro.config.mjs,example%2Ftodo-list%2Fsrc%2Fpages%2Findex.astro) | ||
<h2 align="center">Features</h2> | ||
@@ -19,3 +21,3 @@ | ||
- ✅ Comes with two parsers: `naive` (highly optimized) and `baseline` (deoptimization, safer) | ||
- ✅ Just `2.64 kb` nano sized library | ||
- ✅ Just `2.26 kb` nano sized library | ||
- ✅ `0 byte` runtime overhead/dependencies as it just generates vanilla TS/JS code | ||
@@ -22,0 +24,0 @@ - ✅ Tree-shakable and side-effect free |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
264
227577
23
645
5
1
+ Added@ts-morph/common@0.22.0(transitive)
+ Addedcode-block-writer@12.0.0(transitive)
+ Addedminimatch@9.0.4(transitive)
+ Addedmkdirp@3.0.1(transitive)
+ Addedts-morph@21.0.1(transitive)
- Removed@ts-morph/common@0.18.1(transitive)
- Removedcode-block-writer@11.0.3(transitive)
- Removedminimatch@5.1.6(transitive)
- Removedmkdirp@1.0.4(transitive)
- Removedts-morph@17.0.1(transitive)
Updatedts-morph@^21.0.1