@compas/code-gen
Version:
Generate various boring parts of your server
122 lines (103 loc) • 3.46 kB
JavaScript
import { js } from "./tag/tag.js";
/**
* Generate common files depending on the enabled generators.
* Not all generators use this way of generating a common file as some have a computed
* generated output that is shared over all groups.
* These generators will write to the common directory them selves.
*
* @param {import("../generated/common/types").CodeGenContext} context
*/
export function generateCommonFiles(context) {
if (context.options.enabledGenerators?.includes("apiClient")) {
context.outputFiles.push({
contents: generateCommonApiClientFile(context),
relativePath: `./common/apiClient${context.extension}`,
});
}
if (context.options.enabledGenerators?.includes("reactQuery")) {
context.outputFiles.push({
contents: generateCommonReactQueryFile(),
relativePath: `./common/reactQuery${context.extension}x`,
});
}
}
/**
* @param {import("../generated/common/types").CodeGenContext} context
* @returns {string}
*/
function generateCommonApiClientFile(context) {
let contents = "";
if (context.options.isNodeServer) {
contents += js`
import { AppError, streamToBuffer } from "@compas/stdlib";
export function handleError(e, group, name) {
// Validator error
if (AppError.instanceOf(e)) {
e.key = \`response.$\{group}.$\{name}.$\{e.key}\`
throw e;
}
if (typeof e?.response?.data?.pipe === "function") {
// Handle response streams
return streamToBuffer(e.response.data).then(buffer => {
try {
e.response.data = JSON.parse(buffer.toString("utf8"));
} catch {
// Unknown error
throw new AppError(\`response.$\{group}.$\{name}\`,
e.response?.status ?? 500, {
data: e?.response?.data, headers: e?.response?.headers
}, e,
);
}
return handleError(e, group, name);
});
}
// Server AppError
const { key, info } = e.response?.data ?? {};
if (typeof key === "string" && !!info && typeof info === "object") {
throw new AppError(key, e.response.status, info, e);
}
// Unknown error
throw new AppError(\`response.$\{group}.$\{name}\`, e.response?.status ?? 500,
{ data: e?.response?.data, headers: e?.response?.headers }, e,
);
}
`;
}
if (context.options.useTypescript) {
contents += `import { AxiosInstance } from "axios";`;
}
return contents;
}
/**
* @returns {string}
*/
function generateCommonReactQueryFile() {
return `
import { AxiosError, AxiosInstance } from "axios";
import React, { createContext, PropsWithChildren, useContext } from "react";
const ApiContext = createContext<AxiosInstance | undefined>(undefined);
export function ApiProvider({
instance, children,
}: PropsWithChildren<{
instance: AxiosInstance;
}>) {
return <ApiContext.Provider value={instance}>{children}</ApiContext.Provider>;
}
export const useApi = () => {
const context = useContext(ApiContext);
if (!context) {
throw Error("Be sure to wrap your application with <ApiProvider>.");
}
return context;
};
export type AppErrorResponse = AxiosError<{
key?: string;
status?: number;
requestId?: number;
info?: {
[key: string]: any;
}
}>
`;
}