From 0ddeb241c18883b950eb3672e81efe7af5fa2a0b Mon Sep 17 00:00:00 2001 From: Kravets <57632712+kravetsone@users.noreply.github.com> Date: Wed, 4 Sep 2024 17:49:46 +0300 Subject: [PATCH] feat: support renamed index syntax (fix https://github.com/kravetsone/elysia-autoload/issues/21) --- README.md | 7 ++- package.json | 2 +- src/utils.ts | 77 ++++++++++++----------- tests/index.test.ts | 145 +++++++++++++++++++++++--------------------- 4 files changed, 124 insertions(+), 107 deletions(-) diff --git a/README.md b/README.md index 41f78be..f391ad1 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Plugin for [Elysia](https://elysiajs.com/) which autoload all routes in directory and code-generate types for [Eden](https://elysiajs.com/eden/overview.html) with [`Bun.build`](#bun-build-usage) support! -**Currently, Eden types generation is broken!!** +**Currently, Eden types generation is broken!!** Feels free to send PR ## Installation @@ -65,6 +65,9 @@ Guide how `elysia-autoload` match routes └──index.ts ├── frontend └──index.tsx // usage of tsx extension + ├── events + └──(post).ts // post and get will not be in the link + └──(get).ts └── users.ts └── package.json ``` @@ -76,6 +79,8 @@ Guide how `elysia-autoload` match routes - /routes/likes/[...].ts → /likes/\* - /routes/domains/@[...]/index.ts → /domains/@\* - /routes/frontend/index.tsx → /frontend +- /routes/events/(post).ts → /events +- /routes/events/(get).ts → /events ## Options diff --git a/package.json b/package.json index 8bbae09..692c392 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "elysia-autoload", - "version": "1.2.1", + "version": "1.3.0", "author": "kravetsone", "type": "module", "types": "./dist/index.d.ts", diff --git a/src/utils.ts b/src/utils.ts index 0c317f7..ecc46d5 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,67 +1,70 @@ import path from "node:path"; export function getPath(dir: string) { - if (path.isAbsolute(dir)) return dir; - if (path.isAbsolute(process.argv[1])) - return path.join(process.argv[1], "..", dir); + if (path.isAbsolute(dir)) return dir; + if (path.isAbsolute(process.argv[1])) + return path.join(process.argv[1], "..", dir); - return path.join(process.cwd(), process.argv[1], "..", dir); + return path.join(process.cwd(), process.argv[1], "..", dir); } // Inspired by https://github.com/wobsoriano/elysia-autoroutes/blob/main/src/utils/transformPathToUrl.ts#L4C31-L4C31 export function transformToUrl(path: string) { - const replacements = [ - // Clean the url extensions - { regex: /\.(ts|tsx|js|jsx|mjs|cjs)$/u, replacement: "" }, - // Fix windows slashes - { regex: /\\/gu, replacement: "/" }, + const replacements = [ + // Clean the url extensions + { regex: /\.(ts|tsx|js|jsx|mjs|cjs)$/u, replacement: "" }, + // Fix windows slashes + { regex: /\\/gu, replacement: "/" }, - // Handle wild card based routes - users/[...id]/profile.ts -> users/*/profile - { regex: /\[\.\.\..*\]/gu, replacement: "*" }, + // Handle wild card based routes - users/[...id]/profile.ts -> users/*/profile + { regex: /\[\.\.\..*\]/gu, replacement: "*" }, - // Handle generic square bracket based routes - users/[id]/index.ts -> users/:id - { - regex: /\[(.*?)\]/gu, - replacement: (_: string, match: string) => `:${match}`, - }, + // Handle generic square bracket based routes - users/[id]/index.ts -> users/:id + { + regex: /\[(.*?)\]/gu, + replacement: (_: string, match: string) => `:${match}`, + }, + { + regex: /\/?\((.*)\)/, + replacement: "", + }, + // Handle the case when multiple parameters are present in one file + // users / [id] - [name].ts to users /: id -:name and users / [id] - [name] / [age].ts to users /: id -: name /: age + { regex: /\]-\[/gu, replacement: "-:" }, + { regex: /\]\//gu, replacement: "/" }, + { regex: /\[/gu, replacement: "" }, + { regex: /\]/gu, replacement: "" }, + // remove index from end of path + { regex: /\/?index$/, replacement: "" }, + ]; - // Handle the case when multiple parameters are present in one file - // users / [id] - [name].ts to users /: id -:name and users / [id] - [name] / [age].ts to users /: id -: name /: age - { regex: /\]-\[/gu, replacement: "-:" }, - { regex: /\]\//gu, replacement: "/" }, - { regex: /\[/gu, replacement: "" }, - { regex: /\]/gu, replacement: "" }, - // remove index from end of path - { regex: /\/?index$/, replacement: "" }, - ]; + let url = path; - let url = path; + for (const { regex, replacement } of replacements) { + url = url.replace(regex, replacement as string); + } - for (const { regex, replacement } of replacements) { - url = url.replace(regex, replacement as string); - } - - return url; + return url; } function getParamsCount(path: string) { - return path.match(/\[(.*?)\]/gu)?.length || 0; + return path.match(/\[(.*?)\]/gu)?.length || 0; } // Is it necessary?.. // Sorts by the smallest parameters export function sortByNestedParams(routes: string[]): string[] { - return routes.sort((a, b) => getParamsCount(a) - getParamsCount(b)); + return routes.sort((a, b) => getParamsCount(a) - getParamsCount(b)); } export function fixSlashes(prefix?: string) { - if (!prefix?.endsWith("/")) return prefix; + if (!prefix?.endsWith("/")) return prefix; - return prefix.slice(0, -1); + return prefix.slice(0, -1); } export function addRelativeIfNotDot(path: string) { - if (path.at(0) !== ".") return `./${path}`; + if (path.at(0) !== ".") return `./${path}`; - return path; + return path; } diff --git a/tests/index.test.ts b/tests/index.test.ts index acae454..c3bc0fe 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -4,83 +4,92 @@ import { Elysia } from "elysia"; import { autoload } from "../src/index"; import { sortByNestedParams, transformToUrl } from "../src/utils"; -const app_with_prefix = new Elysia({ - prefix: "/api", // BROKEN FOR NOW -}).use( - autoload({ - pattern: "**/*.{ts,js}", - dir: "./routes", - types: { - output: "./types/routes.ts", - }, - }), -); +// const app_with_prefix = new Elysia({ +// prefix: "/api", // BROKEN FOR NOW +// }).use( +// autoload({ +// pattern: "**/*.{ts,js}", +// dir: "./routes", +// types: { +// output: "./types/routes.ts", +// }, +// }), +// ); -const app_with_plugin_prefix = new Elysia().use( - autoload({ - prefix: "/api", - pattern: "**/*.{ts,js}", - dir: "./routes", - types: { - output: "./types/routes.ts", - }, - }), -); +// const app_with_plugin_prefix = new Elysia().use( +// autoload({ +// prefix: "/api", +// pattern: "**/*.{ts,js}", +// dir: "./routes", +// types: { +// output: "./types/routes.ts", +// }, +// }), +// ); -const fetcher = edenFetch("http://127.0.0.1:5173"); +// const fetcher = edenFetch("http://127.0.0.1:5173"); -export type ElysiaApp = typeof app_with_prefix; +// export type ElysiaApp = typeof app_with_prefix; describe("Path to URL", () => { - test("/index.ts → ", () => { - expect(transformToUrl("/index.ts")).toBe(""); - }); - test("/posts/index.ts → /posts", () => { - expect(transformToUrl("/posts/index.ts")).toBe("/posts"); - }); - test("/posts/[id].ts → /posts/:id", () => { - expect(transformToUrl("/posts/[id].ts")).toBe("/posts/:id"); - }); - test("/users.ts → /users", () => { - expect(transformToUrl("/users.ts")).toBe("/users"); - }); - test("/likes/[...].ts → /likes/*", () => { - expect(transformToUrl("/likes/[...].ts")).toBe("/likes/*"); - }); - test("/domains/@[...]/index.ts → /domains/@*", () => { - expect(transformToUrl("/domains/@[...]/index.ts")).toBe("/domains/@*"); - }); - test("/frontend/index.tsx → /frontend", () => { - expect(transformToUrl("/frontend/index.tsx")).toBe("/frontend"); - }); + test("/index.ts → ", () => { + expect(transformToUrl("/index.ts")).toBe(""); + }); + test("/posts/index.ts → /posts", () => { + expect(transformToUrl("/posts/index.ts")).toBe("/posts"); + }); + test("/posts/[id].ts → /posts/:id", () => { + expect(transformToUrl("/posts/[id].ts")).toBe("/posts/:id"); + }); + test("/users.ts → /users", () => { + expect(transformToUrl("/users.ts")).toBe("/users"); + }); + test("/likes/[...].ts → /likes/*", () => { + expect(transformToUrl("/likes/[...].ts")).toBe("/likes/*"); + }); + test("/domains/@[...]/index.ts → /domains/@*", () => { + expect(transformToUrl("/domains/@[...]/index.ts")).toBe("/domains/@*"); + }); + test("/frontend/index.tsx → /frontend", () => { + expect(transformToUrl("/frontend/index.tsx")).toBe("/frontend"); + }); + test("/events/(post).ts → /events", () => { + expect(transformToUrl("/events/(post).ts")).toBe("/events"); + }); + test("/(post)/events.ts → /events", () => { + expect(transformToUrl("/(post)/events.ts")).toBe("/events"); + }); + test("(post).ts → ", () => { + expect(transformToUrl("(post).ts")).toBe(""); + }); }); describe("sortByNestedParams", () => { - test("Place routes with params to the end of array", () => { - expect( - sortByNestedParams([ - "/index.ts", - "/likes/test.ts", - "/domains/[test]/some.ts", - "/domains/[test]/[some].ts", - "/likes/[...].ts", - "/posts/some.ts", - "/posts/[id].ts", - ]), - ).toEqual([ - "/index.ts", - "/likes/test.ts", - "/posts/some.ts", - "/domains/[test]/some.ts", - "/likes/[...].ts", - "/posts/[id].ts", - "/domains/[test]/[some].ts", - ]); - }); + test("Place routes with params to the end of array", () => { + expect( + sortByNestedParams([ + "/index.ts", + "/likes/test.ts", + "/domains/[test]/some.ts", + "/domains/[test]/[some].ts", + "/likes/[...].ts", + "/posts/some.ts", + "/posts/[id].ts", + ]) + ).toEqual([ + "/index.ts", + "/likes/test.ts", + "/posts/some.ts", + "/domains/[test]/some.ts", + "/likes/[...].ts", + "/posts/[id].ts", + "/domains/[test]/[some].ts", + ]); + }); - test("Verify Intellisense", () => { - // const request = fetcher("", {}); - }); + test("Verify Intellisense", () => { + // const request = fetcher("", {}); + }); }); // describe("Autoload Plugin", () => {