Skip to content

Commit

Permalink
feat: support renamed index syntax (fix #21)
Browse files Browse the repository at this point in the history
  • Loading branch information
kravetsone committed Sep 4, 2024
1 parent 70aa2a3 commit 0ddeb24
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 107 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
```
Expand All @@ -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

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "elysia-autoload",
"version": "1.2.1",
"version": "1.3.0",
"author": "kravetsone",
"type": "module",
"types": "./dist/index.d.ts",
Expand Down
77 changes: 40 additions & 37 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -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;
}
145 changes: 77 additions & 68 deletions tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Routes>("http://127.0.0.1:5173");
// const fetcher = edenFetch<Routes>("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", () => {
Expand Down

0 comments on commit 0ddeb24

Please sign in to comment.