htmv 0.0.18 → 0.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +27 -0
- package/.github/workflows/publish.yml +42 -0
- package/README.md +31 -0
- package/biome.json +34 -0
- package/bun.lock +1 -0
- package/dist/app.d.ts +35 -0
- package/dist/app.js +7 -0
- package/dist/index.d.ts +2 -13
- package/dist/index.js +6 -56
- package/dist/routing.d.ts +2 -0
- package/dist/routing.js +37 -0
- package/dist/views.d.ts +2 -0
- package/dist/views.js +24 -0
- package/package.json +28 -19
- package/src/app.ts +10 -0
- package/src/index.ts +7 -78
- package/src/routing.ts +43 -0
- package/src/types.d.ts +16 -0
- package/src/views.ts +32 -0
- package/tsconfig.json +27 -28
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Setup Bun
|
|
16
|
+
uses: oven-sh/setup-bun@v1
|
|
17
|
+
with:
|
|
18
|
+
bun-version: latest
|
|
19
|
+
|
|
20
|
+
- name: Install dependencies
|
|
21
|
+
run: bun install
|
|
22
|
+
|
|
23
|
+
- name: Lint
|
|
24
|
+
run: npm run lint
|
|
25
|
+
|
|
26
|
+
- name: Run TypeScript build
|
|
27
|
+
run: npm run build
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
id-token: write # Required for OIDC
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
publish:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- uses: actions/setup-node@v4
|
|
20
|
+
with:
|
|
21
|
+
node-version: '20'
|
|
22
|
+
registry-url: 'https://registry.npmjs.org'
|
|
23
|
+
|
|
24
|
+
- name: Update npm
|
|
25
|
+
run: npm install -g npm@latest
|
|
26
|
+
|
|
27
|
+
- name: Setup Bun
|
|
28
|
+
uses: oven-sh/setup-bun@v1
|
|
29
|
+
with:
|
|
30
|
+
bun-version: latest
|
|
31
|
+
|
|
32
|
+
- name: Install dependencies
|
|
33
|
+
run: bun install
|
|
34
|
+
|
|
35
|
+
- name: Lint
|
|
36
|
+
run: npm run lint
|
|
37
|
+
|
|
38
|
+
- name: Build (tsc)
|
|
39
|
+
run: npm run build
|
|
40
|
+
|
|
41
|
+
- name: Publish to npm
|
|
42
|
+
run: npm publish --provenance
|
package/README.md
CHANGED
|
@@ -77,5 +77,36 @@ Supported methods currently are:
|
|
|
77
77
|
- DELETE
|
|
78
78
|
- ALL (add `default` keyword)
|
|
79
79
|
|
|
80
|
+
# Renaming folders
|
|
81
|
+
If you wish to rename either the `views`, `routes` or `public` folders you can do so in `index.ts` as follows:
|
|
82
|
+
```ts
|
|
83
|
+
setup({
|
|
84
|
+
routes: path.join(dirPath, "my_custom_routes_folder"),
|
|
85
|
+
views: path.join(dirPath, "stuff", "views"),
|
|
86
|
+
public: path.join(dirPath, "static"),
|
|
87
|
+
port: 3000,
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
Just change the string for the new name you wish for. Note that when doing so `htmv gen` will now need `--path` flag passed to know where to find them.
|
|
91
|
+
|
|
92
|
+
# Code generation
|
|
93
|
+
Do you often forget how to write boilerplate code? Why not just let HTMV do it for you?
|
|
94
|
+
As you know, HTMV comes with the CLI tool you used when creating the project. But it also has the command `htmv gen` which allows you to generate a basic template for a view or route.
|
|
95
|
+
|
|
96
|
+
The syntax is as follows:
|
|
97
|
+
```bash
|
|
98
|
+
bunx htmv@latest gen {TYPE} {NAME} {OPTIONS}
|
|
99
|
+
```
|
|
100
|
+
For example, to create a view called MyCoolView:
|
|
101
|
+
```bash
|
|
102
|
+
bunx htmv@latest gen view MyCoolView
|
|
103
|
+
```
|
|
104
|
+
Running this will generate a view in the `views` directory of your project. The other remaining type is `route` which works the same way but creates the route in your `routes` directory.
|
|
105
|
+
|
|
106
|
+
However, note that if you have changed the name of your folders HTMV will be unable to find them and you'll have to manually specify the folder you wish for the generated file to be placed in as follows:
|
|
107
|
+
```bash
|
|
108
|
+
bunx htmv@latest gen view MyCoolView --path cool_stuff/my_custom_views_folder
|
|
109
|
+
```
|
|
110
|
+
|
|
80
111
|
# Hot reloading
|
|
81
112
|
Having to restart the server every time you make a change can be quite tedious. HTMV takes care of this thanks to Bun. Just develop with `bun dev` and it should work out of the box! Note that this does not include hot reloading in the browser. As of now, you have to refresh the page to see new changes. It doesn't update in real time.
|
package/biome.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.3.3/schema.json",
|
|
3
|
+
"vcs": {
|
|
4
|
+
"enabled": true,
|
|
5
|
+
"clientKind": "git",
|
|
6
|
+
"useIgnoreFile": true
|
|
7
|
+
},
|
|
8
|
+
"files": {
|
|
9
|
+
"includes": ["**", "!!**/dist"]
|
|
10
|
+
},
|
|
11
|
+
"formatter": {
|
|
12
|
+
"enabled": true,
|
|
13
|
+
"indentStyle": "tab"
|
|
14
|
+
},
|
|
15
|
+
"linter": {
|
|
16
|
+
"enabled": true,
|
|
17
|
+
"rules": {
|
|
18
|
+
"recommended": true
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"javascript": {
|
|
22
|
+
"formatter": {
|
|
23
|
+
"quoteStyle": "double"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"assist": {
|
|
27
|
+
"enabled": true,
|
|
28
|
+
"actions": {
|
|
29
|
+
"source": {
|
|
30
|
+
"organizeImports": "on"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
package/bun.lock
CHANGED
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Elysia } from "elysia";
|
|
2
|
+
export declare function createApp(publicPath: string): Elysia<"", {
|
|
3
|
+
decorator: {};
|
|
4
|
+
store: {};
|
|
5
|
+
derive: {};
|
|
6
|
+
resolve: {};
|
|
7
|
+
}, {
|
|
8
|
+
typebox: {};
|
|
9
|
+
error: {};
|
|
10
|
+
}, {
|
|
11
|
+
schema: {};
|
|
12
|
+
standaloneSchema: {};
|
|
13
|
+
macro: {};
|
|
14
|
+
macroFn: {};
|
|
15
|
+
parser: {};
|
|
16
|
+
response: {};
|
|
17
|
+
}, {}, {
|
|
18
|
+
derive: {};
|
|
19
|
+
resolve: {};
|
|
20
|
+
schema: {};
|
|
21
|
+
standaloneSchema: {};
|
|
22
|
+
response: {};
|
|
23
|
+
}, {
|
|
24
|
+
derive: {};
|
|
25
|
+
resolve: {};
|
|
26
|
+
schema: {};
|
|
27
|
+
standaloneSchema: {};
|
|
28
|
+
response: {};
|
|
29
|
+
} & {
|
|
30
|
+
derive: {};
|
|
31
|
+
resolve: {};
|
|
32
|
+
schema: {};
|
|
33
|
+
standaloneSchema: {};
|
|
34
|
+
response: {};
|
|
35
|
+
}>;
|
package/dist/app.js
ADDED
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
query: Record<string, string>;
|
|
4
|
-
request: Request;
|
|
5
|
-
params: Record<string, string>;
|
|
6
|
-
};
|
|
7
|
-
type Paths = {
|
|
8
|
-
routes: string;
|
|
9
|
-
views: string;
|
|
10
|
-
public: string;
|
|
11
|
-
port: number;
|
|
12
|
-
};
|
|
1
|
+
import type { Paths } from "./types";
|
|
2
|
+
export { view } from "./views";
|
|
13
3
|
export declare function setup(paths: Paths): Promise<void>;
|
|
14
|
-
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,63 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
let viewsPath = "";
|
|
6
|
-
export async function view(view, props) {
|
|
7
|
-
if (viewsPath === "")
|
|
8
|
-
throw new Error("Views folder path not yet configured. Use `Htmv.setup` before rendering a view.");
|
|
9
|
-
const filePath = path.join(viewsPath, `${view}.html`);
|
|
10
|
-
const code = await fs.readFile(filePath, "utf-8");
|
|
11
|
-
const replacedCode = code.replace(/{(.+)}/g, (_, propName) => {
|
|
12
|
-
return props[propName];
|
|
13
|
-
});
|
|
14
|
-
return new Response(replacedCode, {
|
|
15
|
-
headers: { "Content-Type": "text/html; charset=utf-8" },
|
|
16
|
-
});
|
|
17
|
-
}
|
|
1
|
+
import { createApp } from "./app";
|
|
2
|
+
import { registerRoutes } from "./routing";
|
|
3
|
+
import { setViewsPath } from "./views";
|
|
4
|
+
export { view } from "./views";
|
|
18
5
|
export async function setup(paths) {
|
|
19
|
-
|
|
20
|
-
const app =
|
|
21
|
-
assets: paths.public,
|
|
22
|
-
}));
|
|
6
|
+
setViewsPath(paths.views);
|
|
7
|
+
const app = createApp(paths.public);
|
|
23
8
|
await registerRoutes(app, paths.routes);
|
|
24
9
|
app.listen(paths.port);
|
|
25
10
|
console.log("");
|
|
26
11
|
console.log(`HTMV running on port ${paths.port}! 🎉`);
|
|
27
12
|
console.log(`http://localhost:${paths.port}`);
|
|
28
13
|
}
|
|
29
|
-
async function registerRoutes(app, baseDir, prefix = "/") {
|
|
30
|
-
const entries = await fs.readdir(baseDir, { withFileTypes: true });
|
|
31
|
-
for (const entry of entries) {
|
|
32
|
-
const fullPath = path.join(baseDir, entry.name);
|
|
33
|
-
if (entry.isDirectory()) {
|
|
34
|
-
await registerRoutes(app, fullPath, path.join(prefix, entry.name));
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
37
|
-
if (entry.name !== "index.ts")
|
|
38
|
-
continue;
|
|
39
|
-
const module = (await import(fullPath));
|
|
40
|
-
const defaultFn = module.default;
|
|
41
|
-
if (defaultFn && typeof defaultFn === "function") {
|
|
42
|
-
app.all(prefix, async ({ request, query, params }) => {
|
|
43
|
-
const result = await defaultFn({ request, query, params });
|
|
44
|
-
return result;
|
|
45
|
-
});
|
|
46
|
-
console.log(`Registered ${fullPath} on ${prefix} route with method all`);
|
|
47
|
-
}
|
|
48
|
-
for (const propName in module) {
|
|
49
|
-
const prop = module[propName];
|
|
50
|
-
if (typeof prop !== "function")
|
|
51
|
-
continue;
|
|
52
|
-
const fn = prop;
|
|
53
|
-
const name = fn.name.toLowerCase();
|
|
54
|
-
if (!["get", "post", "put", "patch", "delete"].includes(name))
|
|
55
|
-
continue;
|
|
56
|
-
app[name](prefix, async ({ request, query, params }) => {
|
|
57
|
-
const result = await fn({ request, query, params });
|
|
58
|
-
return result;
|
|
59
|
-
});
|
|
60
|
-
console.log(`Registered ${fullPath} on ${prefix} route with method ${name}`);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
package/dist/routing.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export async function registerRoutes(app, baseDir, prefix = "/") {
|
|
4
|
+
const entries = await fs.readdir(baseDir, { withFileTypes: true });
|
|
5
|
+
for (const entry of entries) {
|
|
6
|
+
const fullPath = path.join(baseDir, entry.name);
|
|
7
|
+
if (entry.isDirectory()) {
|
|
8
|
+
await registerRoutes(app, fullPath, path.join(prefix, entry.name));
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
if (entry.name !== "index.ts")
|
|
12
|
+
continue;
|
|
13
|
+
const module = (await import(fullPath));
|
|
14
|
+
const defaultFn = module.default;
|
|
15
|
+
if (defaultFn && typeof defaultFn === "function") {
|
|
16
|
+
app.all(prefix, async ({ request, query, params }) => {
|
|
17
|
+
const result = await defaultFn({ request, query, params });
|
|
18
|
+
return result;
|
|
19
|
+
});
|
|
20
|
+
console.log(`Registered ${fullPath} on ${prefix} route with method all`);
|
|
21
|
+
}
|
|
22
|
+
for (const propName in module) {
|
|
23
|
+
const prop = module[propName];
|
|
24
|
+
if (typeof prop !== "function")
|
|
25
|
+
continue;
|
|
26
|
+
const fn = prop;
|
|
27
|
+
const name = fn.name.toLowerCase();
|
|
28
|
+
if (!["get", "post", "put", "patch", "delete"].includes(name))
|
|
29
|
+
continue;
|
|
30
|
+
app[name](prefix, async ({ request, query, params }) => {
|
|
31
|
+
const result = await fn({ request, query, params });
|
|
32
|
+
return result;
|
|
33
|
+
});
|
|
34
|
+
console.log(`Registered ${fullPath} on ${prefix} route with method ${name}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
package/dist/views.d.ts
ADDED
package/dist/views.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
let viewsPath = "";
|
|
4
|
+
export function setViewsPath(path) {
|
|
5
|
+
viewsPath = path;
|
|
6
|
+
}
|
|
7
|
+
export async function view(view, props) {
|
|
8
|
+
if (viewsPath === "")
|
|
9
|
+
throw new Error("Views folder path not yet configured. Use `Htmv.setup` before rendering a view.");
|
|
10
|
+
const filePath = path.join(viewsPath, `${view}.html`);
|
|
11
|
+
const code = await fs.readFile(filePath, "utf-8");
|
|
12
|
+
const replacedCode = code
|
|
13
|
+
.replace(/{(.+)}/g, (_, propName) => {
|
|
14
|
+
return props[propName];
|
|
15
|
+
})
|
|
16
|
+
.replace(/<Isset\s+(\w+)>([\s\S]*?)<\/Isset>/g, (_, propName, innerContent) => {
|
|
17
|
+
if (props[propName] !== undefined && props[propName] !== null)
|
|
18
|
+
return innerContent;
|
|
19
|
+
return "";
|
|
20
|
+
});
|
|
21
|
+
return new Response(replacedCode, {
|
|
22
|
+
headers: { "Content-Type": "text/html; charset=utf-8" },
|
|
23
|
+
});
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,21 +1,30 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
2
|
+
"name": "htmv",
|
|
3
|
+
"main": "dist/index.js",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"version": "0.0.26",
|
|
6
|
+
"devDependencies": {
|
|
7
|
+
"@biomejs/biome": "2.3.3",
|
|
8
|
+
"@types/bun": "latest"
|
|
9
|
+
},
|
|
10
|
+
"peerDependencies": {
|
|
11
|
+
"typescript": "^5"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@elysiajs/node": "^1.4.2",
|
|
15
|
+
"@elysiajs/static": "^1.4.6",
|
|
16
|
+
"elysia": "^1.4.18"
|
|
17
|
+
},
|
|
18
|
+
"bin": {
|
|
19
|
+
"htmv": "dist/cli/cli.js"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc",
|
|
23
|
+
"lint": "biome check",
|
|
24
|
+
"prepublishOnly": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/Fabrisdev/htmv"
|
|
29
|
+
}
|
|
21
30
|
}
|
package/src/app.ts
ADDED
package/src/index.ts
CHANGED
|
@@ -1,87 +1,16 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
1
|
+
import { createApp } from "./app";
|
|
2
|
+
import { registerRoutes } from "./routing";
|
|
3
|
+
import type { Paths } from "./types";
|
|
4
|
+
import { setViewsPath } from "./views";
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
export { view } from "./views";
|
|
7
7
|
|
|
8
|
-
export async function view(view: string, props: Record<string, unknown>) {
|
|
9
|
-
if (viewsPath === "")
|
|
10
|
-
throw new Error(
|
|
11
|
-
"Views folder path not yet configured. Use `Htmv.setup` before rendering a view.",
|
|
12
|
-
);
|
|
13
|
-
const filePath = path.join(viewsPath, `${view}.html`);
|
|
14
|
-
const code = await fs.readFile(filePath, "utf-8");
|
|
15
|
-
const replacedCode = code.replace(/{(.+)}/g, (_, propName) => {
|
|
16
|
-
return props[propName] as string;
|
|
17
|
-
});
|
|
18
|
-
return new Response(replacedCode, {
|
|
19
|
-
headers: { "Content-Type": "text/html; charset=utf-8" },
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type RouteParams = {
|
|
24
|
-
query: Record<string, string>;
|
|
25
|
-
request: Request;
|
|
26
|
-
params: Record<string, string>;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
type Paths = {
|
|
30
|
-
routes: string;
|
|
31
|
-
views: string;
|
|
32
|
-
public: string;
|
|
33
|
-
port: number;
|
|
34
|
-
};
|
|
35
8
|
export async function setup(paths: Paths) {
|
|
36
|
-
|
|
37
|
-
const app =
|
|
38
|
-
staticPlugin({
|
|
39
|
-
assets: paths.public,
|
|
40
|
-
}),
|
|
41
|
-
);
|
|
42
|
-
|
|
9
|
+
setViewsPath(paths.views);
|
|
10
|
+
const app = createApp(paths.public);
|
|
43
11
|
await registerRoutes(app, paths.routes);
|
|
44
12
|
app.listen(paths.port);
|
|
45
13
|
console.log("");
|
|
46
14
|
console.log(`HTMV running on port ${paths.port}! 🎉`);
|
|
47
15
|
console.log(`http://localhost:${paths.port}`);
|
|
48
16
|
}
|
|
49
|
-
|
|
50
|
-
async function registerRoutes(app: Elysia, baseDir: string, prefix = "/") {
|
|
51
|
-
const entries = await fs.readdir(baseDir, { withFileTypes: true });
|
|
52
|
-
for (const entry of entries) {
|
|
53
|
-
const fullPath = path.join(baseDir, entry.name);
|
|
54
|
-
if (entry.isDirectory()) {
|
|
55
|
-
await registerRoutes(app, fullPath, path.join(prefix, entry.name));
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
if (entry.name !== "index.ts") continue;
|
|
59
|
-
const module = (await import(fullPath)) as Record<string, unknown>;
|
|
60
|
-
const defaultFn = module.default;
|
|
61
|
-
if (defaultFn && typeof defaultFn === "function") {
|
|
62
|
-
app.all(prefix, async ({ request, query, params }) => {
|
|
63
|
-
const result = await defaultFn({ request, query, params });
|
|
64
|
-
return result;
|
|
65
|
-
});
|
|
66
|
-
console.log(`Registered ${fullPath} on ${prefix} route with method all`);
|
|
67
|
-
}
|
|
68
|
-
for (const propName in module) {
|
|
69
|
-
const prop = module[propName];
|
|
70
|
-
if (typeof prop !== "function") continue;
|
|
71
|
-
const fn = prop as RouteFn;
|
|
72
|
-
const name = fn.name.toLowerCase();
|
|
73
|
-
if (!["get", "post", "put", "patch", "delete"].includes(name)) continue;
|
|
74
|
-
app[name as "get"](prefix, async ({ request, query, params }) => {
|
|
75
|
-
const result = await fn({ request, query, params });
|
|
76
|
-
return result;
|
|
77
|
-
});
|
|
78
|
-
console.log(
|
|
79
|
-
`Registered ${fullPath} on ${prefix} route with method ${name}`,
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
type RouteFn = (
|
|
86
|
-
_: RouteParams,
|
|
87
|
-
) => Promise<Response> | Response | Promise<string> | string;
|
package/src/routing.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import type Elysia from "elysia";
|
|
4
|
+
import type { RouteFn } from "./types";
|
|
5
|
+
|
|
6
|
+
export async function registerRoutes(
|
|
7
|
+
app: Elysia,
|
|
8
|
+
baseDir: string,
|
|
9
|
+
prefix = "/",
|
|
10
|
+
) {
|
|
11
|
+
const entries = await fs.readdir(baseDir, { withFileTypes: true });
|
|
12
|
+
for (const entry of entries) {
|
|
13
|
+
const fullPath = path.join(baseDir, entry.name);
|
|
14
|
+
if (entry.isDirectory()) {
|
|
15
|
+
await registerRoutes(app, fullPath, path.join(prefix, entry.name));
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (entry.name !== "index.ts") continue;
|
|
19
|
+
const module = (await import(fullPath)) as Record<string, unknown>;
|
|
20
|
+
const defaultFn = module.default;
|
|
21
|
+
if (defaultFn && typeof defaultFn === "function") {
|
|
22
|
+
app.all(prefix, async ({ request, query, params }) => {
|
|
23
|
+
const result = await defaultFn({ request, query, params });
|
|
24
|
+
return result;
|
|
25
|
+
});
|
|
26
|
+
console.log(`Registered ${fullPath} on ${prefix} route with method all`);
|
|
27
|
+
}
|
|
28
|
+
for (const propName in module) {
|
|
29
|
+
const prop = module[propName];
|
|
30
|
+
if (typeof prop !== "function") continue;
|
|
31
|
+
const fn = prop as RouteFn;
|
|
32
|
+
const name = fn.name.toLowerCase();
|
|
33
|
+
if (!["get", "post", "put", "patch", "delete"].includes(name)) continue;
|
|
34
|
+
app[name as "get"](prefix, async ({ request, query, params }) => {
|
|
35
|
+
const result = await fn({ request, query, params });
|
|
36
|
+
return result;
|
|
37
|
+
});
|
|
38
|
+
console.log(
|
|
39
|
+
`Registered ${fullPath} on ${prefix} route with method ${name}`,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/types.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type RouteParams = {
|
|
2
|
+
query: Record<string, string>;
|
|
3
|
+
request: Request;
|
|
4
|
+
params: Record<string, string>;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type Paths = {
|
|
8
|
+
routes: string;
|
|
9
|
+
views: string;
|
|
10
|
+
public: string;
|
|
11
|
+
port: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type RouteFn = (
|
|
15
|
+
_: RouteParams,
|
|
16
|
+
) => Promise<Response> | Response | Promise<string> | string;
|
package/src/views.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
let viewsPath = "";
|
|
5
|
+
|
|
6
|
+
export function setViewsPath(path: string) {
|
|
7
|
+
viewsPath = path;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function view(view: string, props: Record<string, unknown>) {
|
|
11
|
+
if (viewsPath === "")
|
|
12
|
+
throw new Error(
|
|
13
|
+
"Views folder path not yet configured. Use `Htmv.setup` before rendering a view.",
|
|
14
|
+
);
|
|
15
|
+
const filePath = path.join(viewsPath, `${view}.html`);
|
|
16
|
+
const code = await fs.readFile(filePath, "utf-8");
|
|
17
|
+
const replacedCode = code
|
|
18
|
+
.replace(/{(.+)}/g, (_, propName) => {
|
|
19
|
+
return props[propName] as string;
|
|
20
|
+
})
|
|
21
|
+
.replace(
|
|
22
|
+
/<Isset\s+(\w+)>([\s\S]*?)<\/Isset>/g,
|
|
23
|
+
(_, propName, innerContent) => {
|
|
24
|
+
if (props[propName] !== undefined && props[propName] !== null)
|
|
25
|
+
return innerContent;
|
|
26
|
+
return "";
|
|
27
|
+
},
|
|
28
|
+
);
|
|
29
|
+
return new Response(replacedCode, {
|
|
30
|
+
headers: { "Content-Type": "text/html; charset=utf-8" },
|
|
31
|
+
});
|
|
32
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -1,33 +1,32 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
// Environment setup & latest features
|
|
4
|
+
"lib": ["ESNext"],
|
|
5
|
+
"target": "ESNext",
|
|
6
|
+
"module": "Preserve",
|
|
7
|
+
"moduleDetection": "force",
|
|
8
|
+
"allowJs": true,
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
// Bundler mode
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"verbatimModuleSyntax": true,
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
// Best practices
|
|
15
|
+
"strict": true,
|
|
16
|
+
"skipLibCheck": true,
|
|
17
|
+
"noFallthroughCasesInSwitch": true,
|
|
18
|
+
"noUncheckedIndexedAccess": true,
|
|
19
|
+
"noImplicitOverride": true,
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"include": ["src"]
|
|
21
|
+
// Some stricter flags (disabled by default)
|
|
22
|
+
"noUnusedLocals": false,
|
|
23
|
+
"noUnusedParameters": false,
|
|
24
|
+
"noPropertyAccessFromIndexSignature": false,
|
|
25
|
+
|
|
26
|
+
// Config for npm
|
|
27
|
+
"noEmit": false, // Changed to false to allow it to compile
|
|
28
|
+
"outDir": "./dist",
|
|
29
|
+
"declaration": true //generates .d.ts
|
|
30
|
+
},
|
|
31
|
+
"include": ["src"]
|
|
33
32
|
}
|