htmx-router 0.1.3 → 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.
Files changed (55) hide show
  1. package/bin/cli/config.d.ts +10 -0
  2. package/bin/cli/config.js +4 -0
  3. package/bin/cli/index.js +54 -10
  4. package/bin/client/index.d.ts +4 -0
  5. package/bin/client/index.js +100 -0
  6. package/bin/client/mount.d.ts +2 -0
  7. package/bin/client/mount.js +75 -0
  8. package/bin/client/watch.d.ts +1 -0
  9. package/bin/client/watch.js +19 -0
  10. package/bin/helper.d.ts +1 -2
  11. package/bin/helper.js +25 -14
  12. package/bin/index.d.ts +8 -6
  13. package/bin/index.js +7 -16
  14. package/bin/request/http.d.ts +10 -0
  15. package/bin/request/http.js +46 -0
  16. package/bin/request/index.d.ts +16 -0
  17. package/bin/request/index.js +6 -0
  18. package/bin/request/native.d.ts +9 -0
  19. package/bin/request/native.js +56 -0
  20. package/bin/router.d.ts +41 -16
  21. package/bin/router.js +176 -236
  22. package/bin/types.d.ts +10 -0
  23. package/bin/types.js +1 -0
  24. package/bin/util/cookies.d.ts +22 -0
  25. package/bin/util/cookies.js +57 -0
  26. package/bin/util/css.d.ts +9 -0
  27. package/bin/util/css.js +47 -0
  28. package/bin/util/dynamic.d.ts +5 -0
  29. package/bin/util/dynamic.js +26 -0
  30. package/bin/util/endpoint.d.ts +9 -0
  31. package/bin/util/endpoint.js +28 -0
  32. package/bin/util/event-source.d.ts +16 -0
  33. package/bin/util/event-source.js +85 -0
  34. package/bin/util/hash.d.ts +1 -0
  35. package/bin/util/hash.js +7 -0
  36. package/bin/util/index.d.ts +1 -0
  37. package/bin/util/index.js +7 -0
  38. package/bin/util/parameters.d.ts +7 -0
  39. package/bin/util/parameters.js +14 -0
  40. package/bin/util/shell.d.ts +32 -0
  41. package/bin/util/shell.js +1 -0
  42. package/package.json +9 -7
  43. package/readme.md +149 -211
  44. package/bin/404-route.d.ts +0 -2
  45. package/bin/404-route.js +0 -8
  46. package/bin/cli/dynamic.d.ts +0 -2
  47. package/bin/cli/dynamic.js +0 -47
  48. package/bin/cli/static.d.ts +0 -2
  49. package/bin/cli/static.js +0 -49
  50. package/bin/components.d.ts +0 -6
  51. package/bin/components.js +0 -31
  52. package/bin/render-args.d.ts +0 -35
  53. package/bin/render-args.js +0 -140
  54. package/bin/shared.d.ts +0 -28
  55. package/bin/shared.js +0 -28
package/readme.md CHANGED
@@ -1,279 +1,217 @@
1
- # htmX Router
2
-
3
- > A [remix.js](https://remix.run/docs/en/main/guides/routing) style file path router for htmX websites
4
-
5
- This library attempts to be as unopinionated as possible allowing for multiple escape hatches in-case there are certain times you want a different style of behaviour.
6
-
7
- This library does not rely on any heavy weight dependencies such as react, instead opting to be built on the lighter weight [typed-html](https://www.npmjs.com/package/typed-html) library for it's JSX rendering, and using [csstype](https://www.npmjs.com/package/csstype), just as a type interface to improve developer ergonomics.
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
+ }
39
+ ```
8
40
 
9
- You can also see an example site running this library [here](https://github.com/AjaniBilby/predictable) with source code as an extra helpful example. Please be mindful the slow loading of this site is actually due to Discord APIs, and the rendering is taking less than 2ms on a raspberry pi on my floor.
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`.
10
43
 
11
- - [htmX Router](#htmx-router)
12
- - [Routes](#routes)
13
- - [Module Layout](#module-layout)
14
- - [Auth Function](#auth-function)
15
- - [Render Function](#render-function)
16
- - [CatchError Function](#catcherror-function)
17
- - [Router](#router)
18
- - [Types](#types)
19
- - [RenderArgs](#renderargs)
20
- - [Outlet](#outlet)
21
- - [setTitle](#settitle)
22
- - [addMeta](#addmeta)
23
- - [addLinks](#addlinks)
24
- - [renderHeadHTML](#renderheadhtml)
25
- - [shared](#shared)
26
- - [ErrorResponse](#errorresponse)
27
- - [Override](#override)
28
- - [Redirect](#redirect)
29
- - [Components](#components)
30
- - [Link](#link)
31
- - [StyleCSS](#stylecss)
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.
32
45
 
33
- ## Routes
34
46
 
35
- There are two requirements for this package behave correctly, you need a `root.jsx`/`.tsx`, in the same folder as a `routes` sub-folder. Plus any given route **must** have use the `route name` provided as the top element's `id` - which will be explained more in later.
47
+ ## Routing
36
48
 
37
- URLs are resolved based on the file structure of yur `routes` folder, you can choose to use nested folders or `.`s to have all of your files in a single folder if you choose - all examples will use the `.` layout for simplicity.
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
38
54
 
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*).
55
+ This allows for easy overriding and fallback behaviour. For instance with the routes.
40
56
  ```
41
- root.tsx
42
- routes
43
- ├── _index.tsx
44
- ├── user.tsx
45
- ├── user.static-path.tsx
46
- └── user.$userID.tsx
57
+ /user/$id.tsx
58
+ /user/me.tsx
59
+ /user/$.tsx
47
60
  ```
48
61
 
49
- Given the file tree above, when the root function calls it's [Outlet](#outlet) function, that will trigger `user` to render, and when user calls it's [Outlet](#outlet) function, that will trigger `user.$userID.tsx` to render as this sub route didn't match with any of the static options available, so instead it matched with the wild card route.
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.
52
-
53
- If we request `/user`, the root route will render, and the `user.tsx` route will render, with `user.tsx`'s [Outlet](#outlet) function actually returning a blank string. If we instead want this request to throw an error we should add a `user._index.tsx` file which on render always throws a `ErrorResponse`.
54
-
55
- ### Module Layout
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`.
56
63
 
57
- The router will look for three functions when reading a module, [Render](#render), [CatchError](#catcherror), and [Auth](#auth). Any combination of all or none of these functions are allowed, with the only exception being your `root.tsx` which must have a [Render](#render) and a [CatchError](#catcherror) function.
64
+ If a route returns `null` the router will continue the depth first search, allowing for dynamic flow through of the routes.
58
65
 
59
- #### Auth Function
66
+ ### Route Module
60
67
 
61
- ```ts
62
- export async function Auth({shared}: RenderArgs) {
63
- if (!shared.auth?.isAdmin) throw new ErrorResponse(401, 'Unauthorised', "Unauthorised Access");
64
- return;
65
- }
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 };
66
72
  ```
67
73
 
68
- This function is ran on all routes resolved by this file, and it's child routes - no matter if the route itself is masked or not rendered. This function must return nothing, and instead signals failure via throwing an error. With the successful case being nothing was thrown.
69
-
70
- #### Render Function
71
-
74
+ A route can additionally define a loader, which is called on `GET` and `HEAD` requests
72
75
  ```ts
73
- export async function Render(routeName: string, {}: RenderArgs): Promise<string>
76
+ export async function loader({}: RouteContext<typeof parameters>);
74
77
  ```
75
78
 
76
- The render function should follow the above function signature, the routeName string must be consumed if you're rendering a valid output, with the top most HTML element having the id assigned to this value. This helps the router dynamically insert new routes when using the [Link](#link) component.
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
81
-
82
- Optionally this function can also `throw`, in this case the thrown value will boil up until it hits a [CatchError](#catcherror), unless it throws certain types from this library such as `Redirect` or `Override` which will boil all the way to the top without triggering any [CatchError](#catcherror) functions.
83
-
84
- This allows a given route to return an arbitrary response without being nested within it's parent routes.
85
-
86
- #### CatchError Function
87
-
79
+ With the `action` function being called for all other methods
88
80
  ```ts
89
- export async function CatchError(rn: string, {}: RenderArgs, e: ErrorResponse): Promise<string>
90
- ```
91
-
92
- This function behaves almost identically to [Render](#render) in the way that it's results will be embed within it's parents unless an `Override` is thrown, and it takes a `routeName` which must be used in the root `HTML` element as the id. And it is given [RenderArgs](#renderargs).
93
-
94
- However this function **must not** call the [Outlet](#outlet) function within the [RenderArgs](#renderargs), as that will attempt to consume a failed child route as it's result.
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
81
+ export async function action({}: RouteContext<typeof parameters>);
102
82
  ```
103
83
 
104
- When the command is ran it will generate a router based on the directory provided which should contain your root file and routes folder. This command will generate a `router.ts` which we recommend you git ignore from your project.
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:
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.
111
85
  ```ts
112
- const url = new URL(req.url || "/", "http://localhost");
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
- }
86
+ export async function error(ctx: GenericContext, error: unknown);
124
87
  ```
125
88
 
126
- The `Router.render` function may output three possible types [Redirect](#redirect), [Override](#override), or a simple string. These two non-string types are to allow the boil up of overrides within routes, and allows you do handle them specific to your server environment.
89
+ ### Nested Route Rendering
127
90
 
128
- ## Types
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.
129
93
 
130
- ### RenderArgs
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.
131
95
 
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.
96
+ We recommend you look at [Predictable Bot](https://github.com/AjaniBilby/predictable) as an example of this pattern performed simply.
133
97
 
134
- ```ts
135
- export async function Render(rn: string, {req, res}: RenderArgs) {
136
- return "Hello World";
137
- }
138
- ```
139
98
 
140
- However it also includes a bunch of functions to help with higher order features.
99
+ ### JSX Rendering
141
100
 
142
- #### Outlet
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
+ }));
143
113
 
144
- The outlet function will call the child route to render, this function is asynchronous and will always return a string, or else it will throw. If there is nothing left in the outlet chain, it will return an empty string.
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
+ }));
124
+ ```
145
125
 
146
- #### setTitle
126
+ ## Route Contexts
147
127
 
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.
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.
149
129
 
150
- You should consider when you call this function in conjunction to your [Outlet](#outlet) function, because if you run setTitle, after the outlet has been called it will override the title set by the child.
130
+ ### Params
151
131
 
152
- ```tsx
153
- export async function Render(rn: string, {setTitle, Outlet}: RenderArgs) {
154
- setTitle("Admin Panel");
155
-
156
- return <div id={rn}>
157
- <h1><Link to="/admin" style="color: inherit">
158
- Admin Panel
159
- </Link></h1>
160
- {await Outlet()}
161
- </div>;
162
- }
163
- ```
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.
164
133
 
165
- #### addMeta
134
+ ### Cookies
166
135
 
167
- This function allows you to add meta tags which will be rendered by the [renderHeadHTML](#renderheadhtml) function.
168
- ```ts
169
- addMeta([
170
- { property: "og:title", content: `${guild.name} - Predictions` },
171
- { property: "og:image", content: banner }
172
- ], true);
173
- ```
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.
174
138
 
175
- If the second argument of this function is set to `true` this function will override any meta tags currently set, replacing them with the inputted tags instead.
139
+ ### Headers
176
140
 
177
- #### addLinks
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.
178
143
 
179
- This function behaves identically to [addMeta](#addmeta) but instead designed for link tags.
144
+ ### Request
180
145
 
181
- #### renderHeadHTML
146
+ This is the original request object, including request headers.
182
147
 
183
- This renders out the set meta and link tags for use in the `root.tsx` module, it also includes an embed script for updating the title for dynamic loads from the [Link](#link) component.
148
+ ### URL
184
149
 
185
- #### shared
150
+ The parsed `URL` object of the incoming request.
186
151
 
187
- There is also a blank object attached to all [RenderArgs](#renderargs) for sharing information between routes.
152
+ ## Style Sheets
188
153
 
189
- This can be used for various purposes, but one example is to hold onto decoded cookie values so that each session doesn't need to recompute them if they already have.
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.
190
156
 
191
- Such an example would look like this
192
157
  ```ts
193
- import type { IncomingMessage } from "node:http";
194
- import * as cookie from "cookie";
195
-
196
- export function GetCookies(req: IncomingMessage, shared: any): Record<string, string> {
197
- if (shared.cookie) return shared.cookie;
198
-
199
- shared.cookies = cookie.parse(req.headers.cookie || "");
200
- return shared.cookies;
158
+ const myClass = new StyleClass(`myClass`, `
159
+ .this:hover {
160
+ background-color: red;
201
161
  }
162
+ `).name;
202
163
  ```
203
164
 
204
- ```ts
205
- import type { GetCookies } from "../shared/cookie.ts";
165
+ ## Route-less Endpoint
206
166
 
207
- export function GetCookies(rn: string, {shared}: RenderArgs) {
208
- const cookies = GetCookies(shared);
209
- // do stuff....
210
- }
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.
168
+ ```ts
169
+ const endpoint_url = new Endpoint((ctx: GenericContext) => {
170
+ return new Response("Hello World");
171
+ }, "hello-world").url;
211
172
  ```
212
173
 
213
- ### ErrorResponse
174
+ ## Islands
214
175
 
215
- This class is a way of HTTP-ifying error objects, and other error states, if an error is thrown by a [Render](#render) or an [Auth](#auth) function that isn't already wrapped by this type, the error will then become wrapped by this type.
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.
216
177
 
217
- ```ts
218
- export class ErrorResponse {
219
- code : number;
220
- status : string;
221
- data : any;
222
- }
223
- ```
178
+ ### Dynamic
224
179
 
225
- ### Override
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.
226
181
 
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.
182
+ The body of a dynamic component is the pre-rendered infill that will display while the client is loading the dynamic content.
228
183
 
229
- ```ts
230
- export class Override {
231
- data : string | Buffer | Uint8Array;
232
- }
233
- ```
184
+ ```tsx
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 <></>;
234
189
 
235
- ### Redirect
190
+ const user = await GetUser(userID);
191
+ if (!user) return <></>;
236
192
 
237
- This type behaves identically to override by is intended for boiling up specifically http redirect responses.
193
+ return <a href={`/user/${userID}`}>
194
+ <div safe>{user.name}</div>
195
+ </a>
196
+ }
238
197
 
239
- ```ts
240
- export class Redirect {
241
- location: string;
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>
242
202
  }
243
203
  ```
244
204
 
245
- ## Components
205
+ ### Client
246
206
 
247
- ### Link
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.
208
+
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.
248
210
 
249
- ```ts
250
- export function Link(props: {
251
- to: string,
252
- target?: string,
253
- style?: string
254
- }, contents: string[])
255
- ```
256
211
  ```tsx
257
- <Link to={`/user/${id}`}>View Profile</Link>
212
+ <Client.Counter>
213
+ <button>No yet hydrated...</button> {/* this will be overwritten in the browser once hydrated */}
214
+ </Client.Counter>
258
215
  ```
259
216
 
260
- This component overrides a normal `<a>`, adding extra headers telling the server route it is coming from, based on this information the server can determine the minimal route that needs to be rendered to send back to the client and calculates just that sub route.
261
-
262
- Sending it back to the client telling htmX where to insert the new content.
263
-
264
- This element will encode as a standard `<a>` with some extra html attributes, meaning it won't affect SEO and bots attempting to scrape your website.
265
-
266
-
267
- ## StyleCSS
268
-
269
- 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 [typed-html](https://www.npmjs.com/package/typed-html) to use.
270
- ```tsx
271
- <div style={StyleCSS({
272
- height: "100%",
273
- width: "100%",
274
- fontWeight: "bold",
275
- fontSize: "5em",
276
- })}>
277
- I AM BIG
278
- </div>
279
- ```
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.
@@ -1,2 +0,0 @@
1
- import { RenderArgs } from "./render-args";
2
- export declare function Render(rn: string, { req }: RenderArgs): Promise<string>;
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;
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export declare function BuildDynamic(cwd: string): void;
@@ -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;
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export declare function BuildStatic(cwd: string): void;
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;
@@ -1,6 +0,0 @@
1
- export declare function Link(props: {
2
- to: string;
3
- class?: string;
4
- target?: string;
5
- style?: string;
6
- }, contents: string[]): string;
package/bin/components.js DELETED
@@ -1,31 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.Link = void 0;
27
- const elements = __importStar(require("typed-html"));
28
- function Link(props, contents) {
29
- return elements.createElement("a", { target: props.target || "", class: props.class || "", style: props.style || "", href: props.to, "hx-get": props.to, "hx-headers": '{"Cache-Control": "no-cache"}' }, contents);
30
- }
31
- exports.Link = Link;
@@ -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 {};