Page MenuHomeSealhub

generate-routes.ts
No OneTemporary

generate-routes.ts

import { promises as fs } from "fs";
import * as prettier from "prettier";
import { relative, resolve, basename } from "path";
import _locreq from "locreq";
import { walkDir } from "./utils/walk.js";
import { importPath } from "./utils/import-path.js";
import { assertType, predicates } from "@sealcode/ts-predicates";
import { unescape_url_params } from "./utils/escape-url-params.js";
import { getPrettierConfig } from "./utils/prettier.js";
import { Templates } from "./templates/templates.js";
const target_locreq = _locreq(process.cwd());
async function extractActionName(full_file_path: string): Promise<string> {
const file_content = await fs.readFile(full_file_path, "utf-8");
const result = /export const actionName = "(\w+)"/.exec(file_content);
if (result === null) {
throw new Error("Missing 'export const actionName' statement?");
}
return assertType(
result[1],
predicates.string,
"Missing export default class?"
);
}
export function generateURL(actionName: string, url: string) {
const parts = url.split("/");
const params = parts
.filter((part) => part.startsWith(":"))
.map((part) => part.slice(1));
if (params.length === 0) {
return `export const ${actionName}URL = "${encodeURI(url)}";`;
}
return `export const ${actionName}URL = (${params
.map((s) => s + ": string")
.join(", ")})=>\`${parts
.map((part) =>
part.startsWith(":")
? "${" + part.slice(1) + "}"
: encodeURIComponent(part)
)
.join("/")}\`;
${actionName}URL.params = [${params
.map((param) => `"${param}"`)
.join(",")}];
${actionName}URL.rawURL = "${encodeURI(url)}";`;
}
export async function generateRoutes(): Promise<void> {
const files = await Promise.all(
(
await walkDir(target_locreq.resolve("src/back/routes"))
)
.filter((f) =>
Object.keys(Templates).some(
(key) =>
f.endsWith(`.${key}.ts`) || f.endsWith(`.${key}.tsx`)
)
)
.map(async (fullpath) => ({
fullpath,
actionName: await extractActionName(fullpath),
// trailing slash is important here, as it enables to use the entire path while building relative URLs. For example, while visiting /users/123, the path ./add-photo leads to /users/add-photo. While visiting /users/123/ (note the trailing slash), the path ./add-photo leads to /users/123/add-photo
url: unescape_url_params(
"/" +
relative(
target_locreq.resolve("src/back/routes"),
resolve(
fullpath,
basename(fullpath).startsWith("index.")
? "../"
: ""
)
).replace(/\..+/, "") +
"/"
).replace(/\/\//g, "/"),
}))
);
const routes_content = `// DO NOT EDIT! This file is generated automaticaly with npm run generate-routes
import type Router from "@koa/router";
import { mount } from "@sealcode/sealgen";
import * as URLs from "./urls.js"
${files
.map(
({ actionName, fullpath }) =>
`import { default as ${actionName} } from "${importPath(
target_locreq.resolve("src/back/routes/routes.ts"),
fullpath
)}";`
)
.join("\n")}
export default function mountAutoRoutes(router: Router) {
${files
.sort(({ url: url1 }, { url: url2 }) =>
url1.includes(":") // make sure the parametrized ones come later, so the more concrete ones show up first. This is not a full solution, but works for single-parameter urls
? 1
: url2.includes(":")
? -1
: url1 < url2
? -1
: 1
)
.map(
({ actionName }) =>
` mount(router, URLs.${actionName}URL, ${actionName});`
)
.join("\n")}
}`;
const urls_content = `${files
.sort(({ url: url1 }, { url: url2 }) => (url1 < url2 ? -1 : 1))
.map(({ actionName, url }) => generateURL(actionName, url))
.join("\n")}
`;
const prettier_config = await getPrettierConfig();
await fs.writeFile(
target_locreq.resolve("src/back/routes/routes.ts"),
prettier.format(routes_content, prettier_config)
);
await fs.writeFile(
target_locreq.resolve("src/back/routes/urls.ts"),
prettier.format(urls_content, prettier_config)
);
// eslint-disable-next-line no-console
console.log(
"Successfuly generated new src/back/routes/routes.ts and src/back/routes/urls.ts"
);
}

File Metadata

Mime Type
text/x-java
Expires
Tue, Dec 24, 14:02 (10 h, 37 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
556469
Default Alt Text
generate-routes.ts (4 KB)

Event Timeline