Socket
Socket
Sign inDemoInstall

@jsheaven/astro-client-generator

Package Overview
Dependencies
28
Maintainers
1
Versions
16
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.0.10 to 1.1.0

420

dist/cli.cjs.js
#!/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

15

package.json
{
"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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc