htmx-router 0.2.0 → 1.0.0-alpha.0
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/bin/cli/config.d.ts +10 -0
- package/bin/cli/config.js +4 -0
- package/bin/cli/index.js +54 -10
- package/bin/client/index.d.ts +4 -0
- package/bin/client/index.js +100 -0
- package/bin/client/mount.d.ts +2 -0
- package/bin/client/mount.js +75 -0
- package/bin/client/watch.d.ts +1 -0
- package/bin/client/watch.js +19 -0
- package/bin/helper.d.ts +1 -2
- package/bin/helper.js +25 -14
- package/bin/index.d.ts +8 -6
- package/bin/index.js +7 -16
- package/bin/request/http.d.ts +10 -0
- package/bin/request/http.js +46 -0
- package/bin/request/index.d.ts +16 -0
- package/bin/request/index.js +6 -0
- package/bin/request/native.d.ts +9 -0
- package/bin/request/native.js +56 -0
- package/bin/router.d.ts +41 -16
- package/bin/router.js +176 -236
- package/bin/types.d.ts +10 -0
- package/bin/types.js +1 -0
- package/bin/util/cookies.d.ts +22 -0
- package/bin/util/cookies.js +57 -0
- package/bin/util/css.d.ts +9 -0
- package/bin/util/css.js +47 -0
- package/bin/util/dynamic.d.ts +5 -0
- package/bin/util/dynamic.js +26 -0
- package/bin/util/endpoint.d.ts +9 -0
- package/bin/util/endpoint.js +28 -0
- package/bin/util/event-source.d.ts +16 -0
- package/bin/util/event-source.js +85 -0
- package/bin/util/hash.d.ts +1 -0
- package/bin/util/hash.js +7 -0
- package/bin/util/index.d.ts +1 -0
- package/bin/util/index.js +7 -0
- package/bin/util/parameters.d.ts +7 -0
- package/bin/util/parameters.js +14 -0
- package/bin/util/shell.d.ts +32 -0
- package/bin/util/shell.js +1 -0
- package/package.json +9 -7
- package/readme.md +149 -213
- package/bin/404-route.d.ts +0 -2
- package/bin/404-route.js +0 -8
- package/bin/cli/dynamic.d.ts +0 -2
- package/bin/cli/dynamic.js +0 -47
- package/bin/cli/static.d.ts +0 -2
- package/bin/cli/static.js +0 -49
- package/bin/components.d.ts +0 -8
- package/bin/components.js +0 -11
- package/bin/render-args.d.ts +0 -35
- package/bin/render-args.js +0 -120
- package/bin/shared.d.ts +0 -28
- package/bin/shared.js +0 -28
package/readme.md
CHANGED
|
@@ -1,281 +1,217 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- [
|
|
12
|
-
|
|
13
|
-
- [
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- [
|
|
18
|
-
- [
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
If the url path `/user/1234/history` is requested, the router will create an outlet chain to your file tree (*this chain is actually just an array of modules, and the library works based on stack operations to reduce recursion and increase response times*).
|
|
40
|
-
```
|
|
41
|
-
root.tsx
|
|
42
|
-
routes
|
|
43
|
-
├── _index.tsx
|
|
44
|
-
├── user.tsx
|
|
45
|
-
├── user.static-path.tsx
|
|
46
|
-
└── user.$userID.tsx
|
|
1
|
+
# htmx Router
|
|
2
|
+
|
|
3
|
+
A simple file based router with support for dynamic + client islands, route-less endpoints, and built in CSS sheet generation.
|
|
4
|
+
|
|
5
|
+
- [htmx Router](#htmx-router)
|
|
6
|
+
- [Setup](#setup)
|
|
7
|
+
- [Routing](#routing)
|
|
8
|
+
- [Route Module](#route-module)
|
|
9
|
+
- [Nested Route Rendering](#nested-route-rendering)
|
|
10
|
+
- [JSX Rendering](#jsx-rendering)
|
|
11
|
+
- [Route Contexts](#route-contexts)
|
|
12
|
+
- [Params](#params)
|
|
13
|
+
- [Cookies](#cookies)
|
|
14
|
+
- [Headers](#headers)
|
|
15
|
+
- [Request](#request)
|
|
16
|
+
- [URL](#url)
|
|
17
|
+
- [Style Sheets](#style-sheets)
|
|
18
|
+
- [Route-less Endpoint](#route-less-endpoint)
|
|
19
|
+
- [Islands](#islands)
|
|
20
|
+
- [Dynamic](#dynamic)
|
|
21
|
+
- [Client](#client)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## Setup
|
|
25
|
+
|
|
26
|
+
Create a `htmx-router.json` in the root of your project defining where you want your router file to be placed, and where your routes are.
|
|
27
|
+
You can also define where you want to create your client component bindings, and for what target framework.
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"router": {
|
|
31
|
+
"folder": "./source/routes",
|
|
32
|
+
"output": "./source/router.tsx"
|
|
33
|
+
},
|
|
34
|
+
"client": {
|
|
35
|
+
"adapter": "react",
|
|
36
|
+
"source": "./source/client.tsx"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
47
39
|
```
|
|
48
40
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
Since there is no `user.$userID.history.tsx` file, if `user.$userID.tsx` calls [Outlet](#outlet) it will trigger a 404 error, which is actually generated by an internal hidden route placed at the end of an Outlet chain when it is not able to match the rest of a given URL.
|
|
41
|
+
Once you have done this, run `npx htmx-router` to generate the artifacts to start development.
|
|
42
|
+
We recommand you copy the setup from `examples/react` for your `server.js`, `entry.server.ts`, and `entry.client.ts`.
|
|
52
43
|
|
|
53
|
-
|
|
44
|
+
Don't forget that in all rendered routes you must include the `<RouterHeader/>` component in your head for hydration and `StyleClass`s to apply affectively.
|
|
54
45
|
|
|
55
|
-
### Module Layout
|
|
56
46
|
|
|
57
|
-
|
|
47
|
+
## Routing
|
|
58
48
|
|
|
59
|
-
|
|
49
|
+
Routing applies in a depth first order, where it will match in order:
|
|
50
|
+
1. The `_index`
|
|
51
|
+
2. Static sub-routes
|
|
52
|
+
3. Url path-param wildcards
|
|
53
|
+
4. Catchall slug
|
|
60
54
|
|
|
61
|
-
|
|
62
|
-
export async function Auth({shared}: RenderArgs) {
|
|
63
|
-
if (!shared.auth?.isAdmin) throw new ErrorResponse(401, 'Unauthorised', "Unauthorised Access");
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
55
|
+
This allows for easy overriding and fallback behaviour. For instance with the routes.
|
|
66
56
|
```
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
#### Render Function
|
|
71
|
-
|
|
72
|
-
```ts
|
|
73
|
-
export async function Render(routeName: string, {}: RenderArgs): Promise<string>
|
|
57
|
+
/user/$id.tsx
|
|
58
|
+
/user/me.tsx
|
|
59
|
+
/user/$.tsx
|
|
74
60
|
```
|
|
75
61
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
> **Note** that a render function may not always run for a given request, and may sometimes be ommited when the router determines the client already has this information in their DOM, and instead only response with the new data and where it should be inserted.
|
|
79
|
-
>
|
|
80
|
-
> For authorisation checks which must always run for a given URL, please use the [Auth](#auth) function
|
|
62
|
+
It will match on the `/user/me` route if applicable, and otherwise will fallback to attempt to match on `/user/$id`, and if the wildcard route fails, it will try the generic slug route `/user/$.tsx`.
|
|
81
63
|
|
|
82
|
-
|
|
64
|
+
If a route returns `null` the router will continue the depth first search, allowing for dynamic flow through of the routes.
|
|
83
65
|
|
|
84
|
-
|
|
66
|
+
### Route Module
|
|
85
67
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
```
|
|
89
|
-
export
|
|
68
|
+
A route module must define a `parameters` export, which defines how the url path params should be parsed when attempting to match the route.
|
|
69
|
+
You can use any function which takes a string, and returns something as the parser. You can also simply use JS-Builtin functions for this, and there is a special case with the `Number` function so it will reject on `NaN` values.
|
|
70
|
+
```js
|
|
71
|
+
export const parameters = { id: Number };
|
|
90
72
|
```
|
|
91
73
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
## Router
|
|
97
|
-
|
|
98
|
-
The router itself can be generated via two different ways through the CLI from this library, dynamically or statically.
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
npx htmx-router ./source/website --dynamic
|
|
74
|
+
A route can additionally define a loader, which is called on `GET` and `HEAD` requests
|
|
75
|
+
```ts
|
|
76
|
+
export async function loader({}: RouteContext<typeof parameters>);
|
|
102
77
|
```
|
|
103
78
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
- **Static**: The static build will read your directories and statically import all of your routes into itself, allowing for easy bundling with tools such as `esbuild`
|
|
107
|
-
- **Dynamic**: Will instead generate a file will on startup will read your directory every time, and dynamically import your routes, which will make it unsuitable for use with webpackers, but allows for quick revisions and working well with tools such as `nodemon`.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
Once your router is generated you can simply import it and use it like the example below:
|
|
79
|
+
With the `action` function being called for all other methods
|
|
111
80
|
```ts
|
|
112
|
-
|
|
113
|
-
const out = await Router.render(req, res, url);
|
|
114
|
-
if (out instanceof Redirect) {
|
|
115
|
-
res.statusCode = 302;
|
|
116
|
-
res.setHeader('Location', out.location);
|
|
117
|
-
return res.end();
|
|
118
|
-
} else if (out instanceof Override) {
|
|
119
|
-
res.end(out.data);
|
|
120
|
-
} else {
|
|
121
|
-
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
|
|
122
|
-
res.end("<!DOCTYPE html>"+out);
|
|
123
|
-
}
|
|
81
|
+
export async function action({}: RouteContext<typeof parameters>);
|
|
124
82
|
```
|
|
125
83
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
## Types
|
|
129
|
-
|
|
130
|
-
### RenderArgs
|
|
131
|
-
|
|
132
|
-
This class has been designed to work well with object unpacking for ease of use, typically it's used in a style like this for routes that only need information about the `req` and `res` objects.
|
|
133
|
-
|
|
84
|
+
If any value is thrown by the parameter parsing, or the render functions (`loader`/`action`) it will boil up, attempting first to call an error function is supplied in the route, and otherwise boiling up to the nearest slug route's `error` function.
|
|
134
85
|
```ts
|
|
135
|
-
export async function
|
|
136
|
-
return "Hello World";
|
|
137
|
-
}
|
|
86
|
+
export async function error(ctx: GenericContext, error: unknown);
|
|
138
87
|
```
|
|
139
88
|
|
|
140
|
-
|
|
89
|
+
### Nested Route Rendering
|
|
141
90
|
|
|
142
|
-
|
|
91
|
+
The router will not do nested layouts, if that behaviour is required we recommend using the slug-shell pattern.
|
|
92
|
+
Where you define a slug route, and export a `shell` function which takes the `JSX` rendered result from the sub route, and renders the upper route around it.
|
|
143
93
|
|
|
144
|
-
|
|
94
|
+
This allows flexibility at runtime on how nested route rendering behaves, and can also allow you to ensure you are not reloading data from the db which is already loaded by a sub-route based on how you parse up data through your slug shells.
|
|
145
95
|
|
|
146
|
-
|
|
96
|
+
We recommend you look at [Predictable Bot](https://github.com/AjaniBilby/predictable) as an example of this pattern performed simply.
|
|
147
97
|
|
|
148
|
-
This function will update the title that will be generated by the [renderHeadHTML](#renderheadhtml) function, as well as the trigger value for the title updater when using the [Link](#link) component.
|
|
149
98
|
|
|
150
|
-
|
|
99
|
+
### JSX Rendering
|
|
151
100
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
|
|
101
|
+
htmx-router is jsx templating agnostic for SSR, instead only requiring a definition provided when creating your request handler, allowing you to BYO JSX templating.
|
|
102
|
+
```js
|
|
103
|
+
// @kitajs/html
|
|
104
|
+
app.use('*', createRequestHandler.http({
|
|
105
|
+
build,
|
|
106
|
+
viteDevServer,
|
|
107
|
+
render: (res) => {
|
|
108
|
+
const headers = new Headers();
|
|
109
|
+
headers.set("Content-Type", "text/html; charset=UTF-8");
|
|
110
|
+
return new Response(String(res), { headers });
|
|
111
|
+
}
|
|
112
|
+
}));
|
|
164
113
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
114
|
+
// React
|
|
115
|
+
app.use('*', createRequestHandler.http({
|
|
116
|
+
build,
|
|
117
|
+
viteDevServer,
|
|
118
|
+
render: (res) => {
|
|
119
|
+
const headers = new Headers();
|
|
120
|
+
headers.set("Content-Type", "text/html; charset=UTF-8");
|
|
121
|
+
return new Response(renderToString(res), { headers });
|
|
122
|
+
}
|
|
123
|
+
}));
|
|
173
124
|
```
|
|
174
125
|
|
|
175
|
-
|
|
126
|
+
## Route Contexts
|
|
176
127
|
|
|
177
|
-
|
|
128
|
+
There are two kinds of route context, the `RouteContext<T>` which is the resolved route with parameterization, and the `GenericContext` which is used by error functions, and dynamic loads.
|
|
178
129
|
|
|
179
|
-
|
|
130
|
+
### Params
|
|
180
131
|
|
|
181
|
-
|
|
132
|
+
In the `GenericContext` this will simply be an object with string key value pairs for the parameters, and only the `RouteContext<T>` for `loader` and `action` will have the parameters pre-parsed by your `parameters` definition.
|
|
182
133
|
|
|
183
|
-
|
|
134
|
+
### Cookies
|
|
184
135
|
|
|
185
|
-
|
|
136
|
+
The `RouteContext` and `GenericContext`s both provide a `cookie` object, with the cookie's pre-parsed from the request headers.
|
|
137
|
+
It also has a built in `set(name, value, options)` function which will add the appropriate headers to the response for the cookie changes.
|
|
186
138
|
|
|
187
|
-
|
|
139
|
+
### Headers
|
|
188
140
|
|
|
189
|
-
This
|
|
141
|
+
This is a header object useful for adding response headers when you haven't fully finished generating your response yet.
|
|
142
|
+
These headers will merge with the response object created by the provided `render` function, with response headers overriding any conflicting `ctx.headers` values.
|
|
190
143
|
|
|
191
|
-
|
|
192
|
-
```ts
|
|
193
|
-
import type { IncomingMessage } from "node:http";
|
|
194
|
-
import * as cookie from "cookie";
|
|
144
|
+
### Request
|
|
195
145
|
|
|
196
|
-
|
|
197
|
-
if (shared.cookie) return shared.cookie;
|
|
146
|
+
This is the original request object, including request headers.
|
|
198
147
|
|
|
199
|
-
|
|
200
|
-
return shared.cookies;
|
|
201
|
-
}
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
```ts
|
|
205
|
-
import type { GetCookies } from "../shared/cookie.ts";
|
|
148
|
+
### URL
|
|
206
149
|
|
|
207
|
-
|
|
208
|
-
const cookies = GetCookies(shared);
|
|
209
|
-
// do stuff....
|
|
210
|
-
}
|
|
211
|
-
```
|
|
150
|
+
The parsed `URL` object of the incoming request.
|
|
212
151
|
|
|
213
|
-
|
|
152
|
+
## Style Sheets
|
|
214
153
|
|
|
215
|
-
|
|
154
|
+
htmx-router includes a `StyleClass` object, which can be used to define CSS classes without needing a unique name.
|
|
155
|
+
StyleClasses should only be defined at the top level of a file, and not created within a function, or dynamically during runtime.
|
|
216
156
|
|
|
217
157
|
```ts
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
data : any;
|
|
158
|
+
const myClass = new StyleClass(`myClass`, `
|
|
159
|
+
.this:hover {
|
|
160
|
+
background-color: red;
|
|
222
161
|
}
|
|
162
|
+
`).name;
|
|
223
163
|
```
|
|
224
164
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
If a render function throws a value of this type, it will boil all the way up to the original render call allowing it to bypass any parent manipulation.
|
|
165
|
+
## Route-less Endpoint
|
|
228
166
|
|
|
167
|
+
This should be defined at the top level of your file, these endpoints can optionally be given an name which will help for debugging network requests, but they do not need to be unique.
|
|
229
168
|
```ts
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
169
|
+
const endpoint_url = new Endpoint((ctx: GenericContext) => {
|
|
170
|
+
return new Response("Hello World");
|
|
171
|
+
}, "hello-world").url;
|
|
233
172
|
```
|
|
234
173
|
|
|
235
|
-
|
|
174
|
+
## Islands
|
|
236
175
|
|
|
237
|
-
|
|
176
|
+
> Tip: Don't forget to wrap your islands in a hx-preserve to prevent losing state. And use `display: contents;` to make that wrapping div transparent for grid and other layout features.
|
|
238
177
|
|
|
239
|
-
|
|
240
|
-
export class Redirect {
|
|
241
|
-
location: string;
|
|
242
|
-
}
|
|
243
|
-
```
|
|
178
|
+
### Dynamic
|
|
244
179
|
|
|
245
|
-
|
|
180
|
+
A dynamic component takes params which will be converted into the props of the loader function, these props may only be string key string value pairs as they are encoded the the query string to allow for browser side caching.
|
|
246
181
|
|
|
247
|
-
|
|
182
|
+
The body of a dynamic component is the pre-rendered infill that will display while the client is loading the dynamic content.
|
|
248
183
|
|
|
249
|
-
```ts
|
|
250
|
-
export function Link(props: {
|
|
251
|
-
to: string,
|
|
252
|
-
target?: string,
|
|
253
|
-
style?: string
|
|
254
|
-
}, contents: string[])
|
|
255
|
-
```
|
|
256
184
|
```tsx
|
|
257
|
-
|
|
258
|
-
|
|
185
|
+
async function MyProfile(params: {}, ctx: GenericContext): Promise<JSX.Element> {
|
|
186
|
+
ctx.headers.set('Cache-Control', "private, max-age=120");
|
|
187
|
+
const userID = ctx.cookie.get('userID');
|
|
188
|
+
if (!userID) return <></>;
|
|
259
189
|
|
|
260
|
-
|
|
190
|
+
const user = await GetUser(userID);
|
|
191
|
+
if (!user) return <></>;
|
|
261
192
|
|
|
262
|
-
|
|
193
|
+
return <a href={`/user/${userID}`}>
|
|
194
|
+
<div safe>{user.name}</div>
|
|
195
|
+
</a>
|
|
196
|
+
}
|
|
263
197
|
|
|
264
|
-
|
|
198
|
+
export async function loader({ params }: RouteContext<typeof parameters>) {
|
|
199
|
+
return <Dynamic params={{}} loader={MyProfile}>
|
|
200
|
+
put your ssr pre-rendered skeleton here
|
|
201
|
+
</Dynamic>
|
|
202
|
+
}
|
|
203
|
+
```
|
|
265
204
|
|
|
205
|
+
### Client
|
|
266
206
|
|
|
267
|
-
|
|
207
|
+
Import all of the components you want to be able to use on the client side into your `client.tsx`, if you are running a dev server this file will automatically generate the clientized version, otherwise use the `npx htmx-router` command to regenerate these artifacts.
|
|
268
208
|
|
|
269
|
-
|
|
209
|
+
Once a component has been clientized you can import it as use it like normal, however the body is now overwritten to it will render immediately on the server, and then all props will parsed to the client for it to be rendered properly in the browser.
|
|
270
210
|
|
|
271
|
-
This is a helper function allowing you to give it a [CSS.Properties](https://www.npmjs.com/package/csstype) type, and render it into a string for [kitajs/html](https://kitajs.github.io/html/) to use.
|
|
272
211
|
```tsx
|
|
273
|
-
<
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
I AM BIG
|
|
280
|
-
</div>
|
|
281
|
-
```
|
|
212
|
+
<Client.Counter>
|
|
213
|
+
<button>No yet hydrated...</button> {/* this will be overwritten in the browser once hydrated */}
|
|
214
|
+
</Client.Counter>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
It is very important that you ensure your `Client` component has a single child element, if there are multiple child components the browser will only mount to the last child causing artifacting.
|
package/bin/404-route.d.ts
DELETED
package/bin/404-route.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Render = void 0;
|
|
4
|
-
const shared_1 = require("./shared");
|
|
5
|
-
async function Render(rn, { req }) {
|
|
6
|
-
throw new shared_1.ErrorResponse(404, "Resource Not Found", req.url || "/");
|
|
7
|
-
}
|
|
8
|
-
exports.Render = Render;
|
package/bin/cli/dynamic.d.ts
DELETED
package/bin/cli/dynamic.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.BuildDynamic = void 0;
|
|
5
|
-
const fs_1 = require("fs");
|
|
6
|
-
function BuildDynamic(cwd) {
|
|
7
|
-
const rootMatcher = new RegExp(/^root\.(j|t)sx?$/);
|
|
8
|
-
const root = (0, fs_1.readdirSync)(cwd)
|
|
9
|
-
.filter(x => rootMatcher.test(x))[0];
|
|
10
|
-
if (!root) {
|
|
11
|
-
console.log(`Missing root.jsx/tsx`);
|
|
12
|
-
process.exit(1);
|
|
13
|
-
}
|
|
14
|
-
let script = `import { RouteTree, IsAllowedExt } from "htmx-router";\n`;
|
|
15
|
-
script +=
|
|
16
|
-
`import { readdirSync } from "fs";
|
|
17
|
-
import { extname, join, relative, resolve } from "path";
|
|
18
|
-
|
|
19
|
-
function readDirRecursively(dir: string) {
|
|
20
|
-
const files = readdirSync(dir, { withFileTypes: true });
|
|
21
|
-
|
|
22
|
-
let filePaths: string[] = [];
|
|
23
|
-
for (const file of files) {
|
|
24
|
-
if (file.isDirectory()) {
|
|
25
|
-
filePaths = [...filePaths, ...readDirRecursively(join(dir, file.name))];
|
|
26
|
-
} else {
|
|
27
|
-
filePaths.push(join(dir, file.name));
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return filePaths;
|
|
32
|
-
}\n`;
|
|
33
|
-
script += `\nexport const Router = new RouteTree();\n`;
|
|
34
|
-
script += `import * as RootRoute from "./root";\n`;
|
|
35
|
-
script += `Router.assignRoot(RootRoute);\n\n`;
|
|
36
|
-
script += "const ctx = resolve(`${__dirname}/routes`);\n";
|
|
37
|
-
script += "const files = readDirRecursively(ctx);\n";
|
|
38
|
-
script += "for (const file of files){\n";
|
|
39
|
-
script += "\tconst ext = extname(file);\n";
|
|
40
|
-
script += "\tif (!IsAllowedExt(ext)) continue;\n";
|
|
41
|
-
script += "\tconst url = relative(ctx, file.slice(0, file.lastIndexOf(\".\")).replace(/\\\\/g, \"/\"));\n";
|
|
42
|
-
script += `\timport(file).then((mod) => Router.ingest(url, mod, [false]));\n`;
|
|
43
|
-
script += "}\n";
|
|
44
|
-
(0, fs_1.writeFileSync)(`${cwd}/router.ts`, script);
|
|
45
|
-
console.log(`Finished Building`);
|
|
46
|
-
}
|
|
47
|
-
exports.BuildDynamic = BuildDynamic;
|
package/bin/cli/static.d.ts
DELETED
package/bin/cli/static.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.BuildStatic = void 0;
|
|
5
|
-
const fs_1 = require("fs");
|
|
6
|
-
const path_1 = require("path");
|
|
7
|
-
const router_1 = require("../router");
|
|
8
|
-
function readDirRecursively(dir) {
|
|
9
|
-
const files = (0, fs_1.readdirSync)(dir, { withFileTypes: true });
|
|
10
|
-
let filePaths = [];
|
|
11
|
-
for (const file of files) {
|
|
12
|
-
if (file.isDirectory()) {
|
|
13
|
-
filePaths = [...filePaths, ...readDirRecursively((0, path_1.join)(dir, file.name))];
|
|
14
|
-
}
|
|
15
|
-
else {
|
|
16
|
-
filePaths.push((0, path_1.join)(dir, file.name));
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return filePaths;
|
|
20
|
-
}
|
|
21
|
-
function BuildStatic(cwd) {
|
|
22
|
-
const rootMatcher = new RegExp(/^root\.(j|t)sx?$/);
|
|
23
|
-
const root = (0, fs_1.readdirSync)(cwd)
|
|
24
|
-
.filter(x => rootMatcher.test(x))[0];
|
|
25
|
-
if (!root) {
|
|
26
|
-
console.log(`Missing root.jsx/tsx`);
|
|
27
|
-
process.exit(1);
|
|
28
|
-
}
|
|
29
|
-
const DIR = './routes';
|
|
30
|
-
const files = readDirRecursively(`${cwd}/routes`)
|
|
31
|
-
.filter(x => (0, router_1.IsAllowedExt)((0, path_1.extname)(x)))
|
|
32
|
-
.map(x => (0, path_1.relative)(cwd, x.slice(0, x.lastIndexOf("."))).replace(/\\/g, "/"))
|
|
33
|
-
.sort();
|
|
34
|
-
let script = `import { RouteTree } from "htmx-router";\n`;
|
|
35
|
-
for (let i = 0; i < files.length; i++) {
|
|
36
|
-
const file = files[i];
|
|
37
|
-
script += `import * as Route${i} from "./${file}";\n`;
|
|
38
|
-
}
|
|
39
|
-
script += `import * as RootRoute from "./root";\n`;
|
|
40
|
-
script += `\nexport const Router = new RouteTree();\n`;
|
|
41
|
-
for (let i = 0; i < files.length; i++) {
|
|
42
|
-
const file = files[i];
|
|
43
|
-
script += `Router.ingest("${file.slice(DIR.length - 1)}", Route${i}, [false]);\n`;
|
|
44
|
-
}
|
|
45
|
-
script += `Router.assignRoot(RootRoute);\n`;
|
|
46
|
-
(0, fs_1.writeFileSync)(`${cwd}/router.ts`, script);
|
|
47
|
-
console.log(`Build with routes;\n` + files.map(x => ` - ${x}`).join("\n"));
|
|
48
|
-
}
|
|
49
|
-
exports.BuildStatic = BuildStatic;
|
package/bin/components.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type * as CSS from 'csstype';
|
|
2
|
-
import html from '@kitajs/html';
|
|
3
|
-
export declare function Link(props: html.PropsWithChildren<{
|
|
4
|
-
to: string;
|
|
5
|
-
class?: string;
|
|
6
|
-
style?: string | CSS.Properties<0 | (string & {}), string & {}>;
|
|
7
|
-
target?: string;
|
|
8
|
-
}>): string;
|
package/bin/components.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.Link = void 0;
|
|
7
|
-
const html_1 = __importDefault(require("@kitajs/html"));
|
|
8
|
-
function Link(props) {
|
|
9
|
-
return html_1.default.createElement("a", { target: props.target || "", class: props.class || "", style: props.style || "", href: props.to, "hx-get": props.to, "hx-headers": '{"Cache-Control": "no-cache"}' }, props.children);
|
|
10
|
-
}
|
|
11
|
-
exports.Link = Link;
|
package/bin/render-args.d.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
import type http from "node:http";
|
|
3
|
-
import { RouteLeaf } from "./router";
|
|
4
|
-
type MetaHTML = {
|
|
5
|
-
[key: string]: string;
|
|
6
|
-
};
|
|
7
|
-
export declare enum MaskType {
|
|
8
|
-
show = 0,
|
|
9
|
-
headless = 1,
|
|
10
|
-
hide = 2
|
|
11
|
-
}
|
|
12
|
-
export declare class RenderArgs {
|
|
13
|
-
req: http.IncomingMessage;
|
|
14
|
-
res: http.ServerResponse;
|
|
15
|
-
title: string;
|
|
16
|
-
params: MetaHTML;
|
|
17
|
-
url: URL;
|
|
18
|
-
shared: {
|
|
19
|
-
[key: string]: any;
|
|
20
|
-
};
|
|
21
|
-
links: MetaHTML[];
|
|
22
|
-
meta: MetaHTML[];
|
|
23
|
-
_outletChain: RouteLeaf[];
|
|
24
|
-
_maskChain: MaskType[];
|
|
25
|
-
_maxChain: number;
|
|
26
|
-
constructor(req: http.IncomingMessage, res: http.ServerResponse, url: URL);
|
|
27
|
-
setTitle: (value: string) => void;
|
|
28
|
-
addLinks: (links: MetaHTML[], override?: boolean) => void;
|
|
29
|
-
addMeta: (links: MetaHTML[], override?: boolean) => void;
|
|
30
|
-
Outlet: () => Promise<string>;
|
|
31
|
-
_addOutlet(route: RouteLeaf): void;
|
|
32
|
-
_applyMask(mask: boolean[], depth: number): void;
|
|
33
|
-
renderHeadHTML: () => string;
|
|
34
|
-
}
|
|
35
|
-
export {};
|