Page MenuHomeSealhub

generate-routes.ts
No OneTemporary

generate-routes.ts

import { promises as fs } from "fs";
import * as prettier from "prettier";
import { relative } from "path";
import _locreq from "locreq";
import { walkDir } from "./utils/walk";
import { importPath } from "./utils/import-path";
import { assertType, predicates } from "@sealcode/ts-predicates";
import { unescape_url_params } from "./utils/escape-url-params";
import { getPrettierConfig } from "./utils/prettier";
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 async function generateRoutes(): Promise<void> {
const files = await Promise.all(
(
await walkDir(target_locreq.resolve("src/back/routes"))
)
.filter(
(f) =>
f.endsWith(".page.ts") ||
f.endsWith(".form.ts") ||
f.endsWith(".multiform.ts") ||
f.endsWith(".list.ts") ||
f.endsWith(".redirect.ts") ||
f.endsWith(".post.ts")
)
.map(async (fullpath) => ({
fullpath,
actionName: await extractActionName(fullpath),
url: unescape_url_params(
"/" +
relative(
target_locreq.resolve("src/back/routes"),
fullpath
).replace(/\..+/, "") +
"/" // 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
),
}))
);
const routes_content = `// DO NOT EDIT! This file is generated automaticaly with npm run generate-routes
import Koa, { Context } from "koa";
import Router from "@koa/router";
import { Middlewares } from "sealious";
import { Mountable } from "@sealcode/sealgen";
import * as URLs from "./urls"
${files
.map(
({ actionName, fullpath }) =>
`import { default as ${actionName} } from "${importPath(
target_locreq.resolve("src/back/routes/routes.ts"),
fullpath
)}";`
)
.join("\n")}
async function handleHtmlPromise(ctx: Context, next: Koa.Next) {
await next();
if (ctx.body instanceof Promise) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
ctx.body = await ctx.body;
}
ctx.set("content-type", "text/html;charset=utf-8");
}
function mount(router: Router, URL: string, mountable: Mountable) {
router.use(
URL,
Middlewares.extractContext(),
Middlewares.parseBody(),
handleHtmlPromise
);
mountable.init();
mountable.mount(router, URL);
// to automatically add trailing slashes:
router.get(URL.slice(0, -1), (ctx) => {
ctx.redirect(ctx.originalUrl + "/");
});
router.use(URL, (ctx) => ctx.set("content-type", "text/html;charset=utf-8"));
}
export default function mountAutoRoutes(router: Router) {
${files
.map(
({ actionName }) =>
` mount(router, URLs.${actionName}URL, ${actionName});`
)
.join("\n")}
}`;
const urls_content = `${files
.map(
({ actionName, url }) => `export const ${actionName}URL = "${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
Sat, Jun 7, 23:22 (5 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
662498
Default Alt Text
generate-routes.ts (3 KB)

Event Timeline