htmx-router 1.0.0-alpha.5 → 1.0.0-pre1

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 (58) hide show
  1. package/example/eventdim-react/package.json +67 -0
  2. package/example/eventdim-react/server.js +90 -0
  3. package/example/island-react/global.d.ts +8 -0
  4. package/example/island-react/package.json +38 -0
  5. package/example/island-react/server.js +58 -0
  6. package/global.d.ts +7 -0
  7. package/package.json +10 -8
  8. package/readme.md +17 -212
  9. package/bin/cli/config.d.ts +0 -10
  10. package/bin/cli/config.js +0 -4
  11. package/bin/cli/index.d.ts +0 -2
  12. package/bin/cli/index.js +0 -66
  13. package/bin/client/entry.d.ts +0 -1
  14. package/bin/client/entry.js +0 -12
  15. package/bin/client/index.d.ts +0 -7
  16. package/bin/client/index.js +0 -132
  17. package/bin/client/mount.d.ts +0 -2
  18. package/bin/client/mount.js +0 -116
  19. package/bin/client/watch.d.ts +0 -1
  20. package/bin/client/watch.js +0 -11
  21. package/bin/helper.d.ts +0 -2
  22. package/bin/helper.js +0 -34
  23. package/bin/index.d.ts +0 -11
  24. package/bin/index.js +0 -18
  25. package/bin/request/http.d.ts +0 -10
  26. package/bin/request/http.js +0 -41
  27. package/bin/request/index.d.ts +0 -16
  28. package/bin/request/index.js +0 -6
  29. package/bin/request/native.d.ts +0 -9
  30. package/bin/request/native.js +0 -46
  31. package/bin/response.d.ts +0 -9
  32. package/bin/response.js +0 -46
  33. package/bin/router.d.ts +0 -49
  34. package/bin/router.js +0 -217
  35. package/bin/types.d.ts +0 -10
  36. package/bin/types.js +0 -1
  37. package/bin/util/cookies.d.ts +0 -25
  38. package/bin/util/cookies.js +0 -60
  39. package/bin/util/css.d.ts +0 -13
  40. package/bin/util/css.js +0 -55
  41. package/bin/util/dynamic.d.ts +0 -8
  42. package/bin/util/dynamic.js +0 -40
  43. package/bin/util/endpoint.d.ts +0 -13
  44. package/bin/util/endpoint.js +0 -32
  45. package/bin/util/event-source.d.ts +0 -16
  46. package/bin/util/event-source.js +0 -85
  47. package/bin/util/hash.d.ts +0 -4
  48. package/bin/util/hash.js +0 -10
  49. package/bin/util/index.d.ts +0 -1
  50. package/bin/util/index.js +0 -7
  51. package/bin/util/parameters.d.ts +0 -10
  52. package/bin/util/parameters.js +0 -17
  53. package/bin/util/path-builder.d.ts +0 -1
  54. package/bin/util/path-builder.js +0 -43
  55. package/bin/util/response.d.ts +0 -11
  56. package/bin/util/response.js +0 -46
  57. package/bin/util/shell.d.ts +0 -120
  58. package/bin/util/shell.js +0 -251
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "eventdim-react",
3
+ "private": "true",
4
+ "version": "1.0.0",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "prepare": "npx htmx-router && prisma generate && prisma migrate deploy",
9
+ "docker": "docker compose up -d",
10
+ "dev": "node ./server.js",
11
+ "build": "run-s build:*",
12
+ "build:router": "npx htmx-router",
13
+ "build:prisma": "npx prisma generate",
14
+ "build:client": "vite build",
15
+ "build:server": "vite build --ssr app/entry.server.ts --outDir dist/server",
16
+ "validate": "run-s validate:*",
17
+ "validate:typecheck": "tsc --noEmit",
18
+ "validate:lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
19
+ "preview": "cross-env NODE_ENV=production node ./server.js"
20
+ },
21
+ "keywords": [],
22
+ "author": "",
23
+ "license": "ISC",
24
+ "description": "",
25
+ "dependencies": {
26
+ "@fortawesome/free-brands-svg-icons": "^6.7.2",
27
+ "@fortawesome/free-solid-svg-icons": "^6.7.2",
28
+ "@fortawesome/react-fontawesome": "^0.2.2",
29
+ "@prisma/client": "^6.1.0",
30
+ "bcryptjs": "^2.4.3",
31
+ "cbor2": "^1.8.0",
32
+ "cross-env": "^7.0.3",
33
+ "dotenv": "^16.4.7",
34
+ "express": "^4.21.2",
35
+ "htmx-router": "^1.0.0-alpha.5",
36
+ "morgan": "^1.10.0",
37
+ "react": "^19.0.0",
38
+ "react-dom": "^19.0.0",
39
+ "tiny-invariant": "^1.3.3",
40
+ "zxcvbn": "^4.4.2"
41
+ },
42
+ "devDependencies": {
43
+ "@types/bcryptjs": "^2.4.6",
44
+ "@types/express": "^4.17.21",
45
+ "@types/nodemailer": "^6.4.15",
46
+ "@types/react": "^18.2.20",
47
+ "@types/react-dom": "^18.2.7",
48
+ "@types/zxcvbn": "^4.4.4",
49
+ "@typescript-eslint/eslint-plugin": "^6.7.4",
50
+ "@typescript-eslint/parser": "^6.7.4",
51
+ "eslint": "^8.38.0",
52
+ "eslint-import-resolver-typescript": "^3.6.1",
53
+ "eslint-plugin-import": "^2.28.1",
54
+ "eslint-plugin-jsx-a11y": "^6.7.1",
55
+ "eslint-plugin-react": "^7.33.2",
56
+ "eslint-plugin-react-hooks": "^4.6.0",
57
+ "npm-run-all": "^4.1.5",
58
+ "prisma": "^6.1.0",
59
+ "typed-htmx": "^0.3.1",
60
+ "typescript": "^5.5.4",
61
+ "vite-tsconfig-paths": "^5.1.3",
62
+ "vite": "^6.0.1"
63
+ },
64
+ "engines": {
65
+ "node": ">=20.0.0"
66
+ }
67
+ }
@@ -0,0 +1,90 @@
1
+ /// <reference types="node" />
2
+ /* eslint-disable */
3
+ import 'dotenv/config'
4
+ import * as path from "path";
5
+ import { createRequestHandler } from 'htmx-router';
6
+ import { renderToString } from 'react-dom/server';
7
+ import express from 'express';
8
+ import morgan from "morgan";
9
+
10
+ const port = process.env.PORT || 3000;
11
+ const app = express();
12
+
13
+ const viteDevServer =
14
+ process.env.NODE_ENV === "production"
15
+ ? null
16
+ : await import("vite").then((vite) =>
17
+ vite.createServer({
18
+ server: { middlewareMode: true },
19
+ appType: 'custom'
20
+ })
21
+ );
22
+
23
+ app.use(
24
+ viteDevServer
25
+ ? viteDevServer.middlewares
26
+ : express.static("./dist/client")
27
+ );
28
+
29
+ // logging
30
+ app.use(morgan("tiny"));
31
+
32
+ const build = viteDevServer
33
+ ? () => viteDevServer.ssrLoadModule('./app/entry.server.ts')
34
+ : await import('./dist/server/entry.server.js');
35
+
36
+ app.use('*', createRequestHandler.http({
37
+ build, viteDevServer,
38
+ render: (res) => {
39
+ const headers = new Headers();
40
+ headers.set("Content-Type", "text/html; charset=UTF-8");
41
+ headers.set("Cache-Control", "no-cache");
42
+
43
+ const stream = renderToString(res);
44
+ return new Response(stream, { headers });
45
+ }
46
+ }));
47
+
48
+ // Start http server
49
+ app.listen(port, () => {
50
+ console.log(`Server started at http://localhost:${port}`)
51
+ })
52
+
53
+
54
+ // Reload pages on file change
55
+ if (viteDevServer) {
56
+ const focus = path.resolve("./app");
57
+ viteDevServer.watcher.on('change', (file) => {
58
+ if (!file.startsWith(focus)) return;
59
+ console.log(`File changed: ${path.relative("./app", file)}`);
60
+
61
+ console.log('Triggering full page reload');
62
+ viteDevServer.ws.send({ type: 'full-reload' });
63
+ });
64
+ }
65
+
66
+ const shutdown = () => {
67
+ console.log("Shutting down server...");
68
+
69
+ // Close the server gracefully
70
+ server.close((err) => {
71
+ if (err) {
72
+ console.error("Error during server shutdown:", err);
73
+ process.exit(1);
74
+ }
75
+ console.log("Server shut down gracefully.");
76
+ process.exit(0);
77
+ });
78
+ };
79
+
80
+ process.on('SIGTERM', shutdown);
81
+ process.on('SIGHUP', shutdown);
82
+
83
+
84
+ process .on('unhandledRejection', (reason, p) => {
85
+ console.error(reason, 'Unhandled Rejection at Promise', p);
86
+ })
87
+ .on('uncaughtException', err => {
88
+ console.error(err, 'Uncaught Exception thrown');
89
+ process.exit(1);
90
+ });
@@ -0,0 +1,8 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ declare global {
4
+ namespace JSX {
5
+ type Element = ReactNode;
6
+ interface HTMLAttributes extends HtmxAttributes {}
7
+ }
8
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "type": "module",
3
+ "scripts": {
4
+ "prepare": "npx htmx-router",
5
+ "dev": "node ./server.js",
6
+ "build": "run-s build:*",
7
+ "build:router": "npx htmx-router",
8
+ "build:client": "vite build",
9
+ "build:server": "vite build --ssr app/entry.server.ts --outDir dist/server",
10
+ "preview": "cross-env NODE_ENV=production node ./server.js",
11
+ "validate": "npx tsc -noEmit"
12
+ },
13
+ "license": "MIT",
14
+ "dependencies": {
15
+ "cross-env": "^7.0.3",
16
+ "dotenv": "^16.3.1",
17
+ "express": "^4.21.1",
18
+ "morgan": "^1.10.0",
19
+ "npm-run-all": "^4.1.5",
20
+ "htmx-router": "^1.0.0-alpha.1",
21
+ "react": "^19.0.0",
22
+ "react-dom": "^19.0.0",
23
+ "serve-static": "^1.16.2",
24
+ "tsconfig-paths": "^4.2.0"
25
+ },
26
+ "devDependencies": {
27
+ "@types/express": "^5.0.0",
28
+ "@types/node": "^20.4.5",
29
+ "@types/react-dom": "^19.0.2",
30
+ "@types/react": "^19.0.1",
31
+ "@types/serve-static": "^1.15.7",
32
+ "ts-node": "^10.9.1",
33
+ "typed-htmx": "^0.3.1",
34
+ "typescript": "^5.1.6",
35
+ "vite-tsconfig-paths": "^5.1.3",
36
+ "vite": "^6.0.1"
37
+ }
38
+ }
@@ -0,0 +1,58 @@
1
+ import { createRequestHandler } from 'htmx-router';
2
+ import { renderToString } from 'react-dom/server';
3
+ import express from 'express';
4
+ import morgan from "morgan";
5
+
6
+ const port = process.env.PORT || 5173;
7
+ const app = express();
8
+
9
+
10
+ const viteDevServer =
11
+ process.env.NODE_ENV === "production"
12
+ ? null
13
+ : await import("vite").then((vite) =>
14
+ vite.createServer({
15
+ server: { middlewareMode: true },
16
+ appType: 'custom'
17
+ })
18
+ );
19
+
20
+ app.use(
21
+ viteDevServer
22
+ ? viteDevServer.middlewares
23
+ : express.static("./dist/client")
24
+ );
25
+
26
+ // logging
27
+ app.use(morgan("tiny"));
28
+
29
+ const build = viteDevServer
30
+ ? () => viteDevServer.ssrLoadModule('./app/entry.server.ts')
31
+ : await import('./dist/server/entry.server.js');
32
+
33
+ app.use('*', createRequestHandler.http({
34
+ build, viteDevServer,
35
+ render: (res) => {
36
+ const headers = new Headers();
37
+ headers.set("Content-Type", "text/html; charset=UTF-8");
38
+
39
+ const stream = renderToString(res);
40
+ return new Response(stream, { headers });
41
+ }
42
+ }));
43
+
44
+
45
+ // Start http server
46
+ app.listen(port, () => {
47
+ console.log(`Server started at http://localhost:${port}`)
48
+ })
49
+
50
+
51
+ // Reload pages on file change
52
+ if (viteDevServer)
53
+ viteDevServer.watcher.on('change', (file) => {
54
+ console.log(`File changed: ${file}`);
55
+
56
+ console.log('Triggering full page reload');
57
+ viteDevServer.ws.send({ type: 'full-reload' });
58
+ });
package/global.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ declare namespace JSX {
2
+ interface Element {}
3
+ interface IntrinsicElements {
4
+ [elementName: string]: any;
5
+ }
6
+ interface Fragment {}
7
+ }
package/package.json CHANGED
@@ -1,15 +1,17 @@
1
1
  {
2
2
  "name": "htmx-router",
3
- "version": "1.0.0-alpha.5",
4
- "description": "A simple SSR framework with dynamic+client islands",
5
- "keywords": ["htmx", "router", ""],
6
- "main": "./bin/index.js",
3
+ "version": "1.0.0-pre1",
4
+ "description": "A lightweight SSR framework with server+client islands",
5
+ "keywords": [
6
+ "htmx", "router", "client islands", "ssr", "vite"
7
+ ],
8
+ "main": "./index.js",
7
9
  "type": "module",
8
10
  "scripts": {
9
- "build": "tsc && tsc-alias"
11
+ "build": "tsc"
10
12
  },
11
13
  "bin": {
12
- "htmx-router": "bin/cli/index.js"
14
+ "htmx-router": "cli/index.js"
13
15
  },
14
16
  "repository": {
15
17
  "type": "git",
@@ -23,12 +25,12 @@
23
25
  "homepage": "https://github.com/AjaniBilby/htmx-router#readme",
24
26
  "dependencies": {
25
27
  "es-module-lexer": "^1.5.4",
26
- "vite": "^6.0.1"
28
+ "vite": "^6.0.6"
27
29
  },
28
30
  "devDependencies": {
29
31
  "@types/node": "^20.4.5",
32
+ "chalk": "^5.4.1",
30
33
  "ts-node": "^10.9.1",
31
- "tsc-alias": "^1.8.10",
32
34
  "typescript": "^5.1.6"
33
35
  }
34
36
  }
package/readme.md CHANGED
@@ -1,217 +1,22 @@
1
- # htmx Router
1
+ # HTMX Router
2
2
 
3
- A simple file based router with support for dynamic + client islands, route-less endpoints, and built in CSS sheet generation.
3
+ A lightweight file based router built on vite+htmx for SSR generation of full pages and html partials
4
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)
5
+ Features:
22
6
 
7
+ - BYO jsx templating
8
+ - File base routing
9
+ - Typesafe url path parameters
10
+ - Dynamic route fallthrough
11
+ - Server + Client Islands
12
+ - Route-less points
13
+ - CSS sheet generation
14
+ - [html](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta), [opengraph](https://ogp.me/), and [json+ld](https://json-ld.org/) metadata generation
15
+ - Bundle splitting for filtering out server code from the client
16
+ - Server-Side EventSource creation for SSE event dispatch
23
17
 
24
- ## Setup
18
+ ## Documentation
19
+ See https://htmx-router.ajanibilby.com/
25
20
 
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
- ```
40
-
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`.
43
-
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.
45
-
46
-
47
- ## Routing
48
-
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
54
-
55
- This allows for easy overriding and fallback behaviour. For instance with the routes.
56
- ```
57
- /user/$id.tsx
58
- /user/me.tsx
59
- /user/$.tsx
60
- ```
61
-
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`.
63
-
64
- If a route returns `null` the router will continue the depth first search, allowing for dynamic flow through of the routes.
65
-
66
- ### Route Module
67
-
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 };
72
- ```
73
-
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>);
77
- ```
78
-
79
- With the `action` function being called for all other methods
80
- ```ts
81
- export async function action({}: RouteContext<typeof parameters>);
82
- ```
83
-
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.
85
- ```ts
86
- export async function error(ctx: GenericContext, error: unknown);
87
- ```
88
-
89
- ### Nested Route Rendering
90
-
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.
93
-
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.
95
-
96
- We recommend you look at [Predictable Bot](https://github.com/AjaniBilby/predictable) as an example of this pattern performed simply.
97
-
98
-
99
- ### JSX Rendering
100
-
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
- }));
113
-
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
- ```
125
-
126
- ## Route Contexts
127
-
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.
129
-
130
- ### Params
131
-
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.
133
-
134
- ### Cookies
135
-
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.
138
-
139
- ### Headers
140
-
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.
143
-
144
- ### Request
145
-
146
- This is the original request object, including request headers.
147
-
148
- ### URL
149
-
150
- The parsed `URL` object of the incoming request.
151
-
152
- ## Style Sheets
153
-
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.
156
-
157
- ```ts
158
- const myClass = new StyleClass(`myClass`, `
159
- .this:hover {
160
- background-color: red;
161
- }
162
- `).name;
163
- ```
164
-
165
- ## Route-less Endpoint
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.
168
- ```ts
169
- const endpoint_url = new Endpoint((ctx: GenericContext) => {
170
- return new Response("Hello World");
171
- }, "hello-world").url;
172
- ```
173
-
174
- ## Islands
175
-
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.
177
-
178
- ### Dynamic
179
-
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.
181
-
182
- The body of a dynamic component is the pre-rendered infill that will display while the client is loading the dynamic content.
183
-
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 <></>;
189
-
190
- const user = await GetUser(userID);
191
- if (!user) return <></>;
192
-
193
- return <a href={`/user/${userID}`}>
194
- <div safe>{user.name}</div>
195
- </a>
196
- }
197
-
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
- ```
204
-
205
- ### Client
206
-
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.
210
-
211
- ```tsx
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.
21
+ ## API
22
+ See https://htmx-router.ajanibilby.com/api
@@ -1,10 +0,0 @@
1
- export declare function ReadConfig(): Promise<{
2
- client?: {
3
- adapter: string;
4
- source: string;
5
- };
6
- router: {
7
- folder: string;
8
- output: string;
9
- };
10
- }>;
package/bin/cli/config.js DELETED
@@ -1,4 +0,0 @@
1
- import { readFile } from "fs/promises";
2
- export async function ReadConfig() {
3
- return JSON.parse(await readFile(process.argv[2] || "./htmx-config.json", "utf-8"));
4
- }
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
package/bin/cli/index.js DELETED
@@ -1,66 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- import { writeFile } from "fs/promises";
4
- import { relative } from "path";
5
- import { GenerateClient } from "../client/index.js";
6
- import { ReadConfig } from "../cli/config.js";
7
- const config = await ReadConfig();
8
- console.info("Building router");
9
- const routes = relative(config.router.output, config.router.folder).replaceAll("\\", "/").slice(1);
10
- await writeFile(config.router.output, `/*------------------------------------------
11
- * Generated by htmx-router *
12
- * Warn: Any changes will be overwritten *
13
- -------------------------------------------*/
14
- /* eslint-disable @typescript-eslint/no-explicit-any */
15
-
16
- import { GenericContext, RouteTree } from "htmx-router/bin/router";
17
- import { GetClientEntryURL } from 'htmx-router/bin/client/entry';
18
- import { DynamicReference } from "htmx-router/bin/util/dynamic";
19
- import { GetMountUrl } from 'htmx-router/bin/client/mount';
20
- import { GetSheetUrl } from 'htmx-router/bin/util/css';
21
- import { RouteModule } from "htmx-router";
22
- import { resolve } from "path";
23
-
24
- (globalThis as any).HTMX_ROUTER_ROOT = resolve('${config.router.folder.replaceAll("\\", "/")}');
25
- const modules = import.meta.glob('${routes}/**/*.{ts,tsx}', { eager: true });
26
-
27
- export const tree = new RouteTree();
28
- for (const path in modules) {
29
- const tail = path.lastIndexOf(".");
30
- const url = path.slice(${routes.length + 1}, tail);
31
- tree.ingest(url, modules[path] as RouteModule<any>);
32
- }
33
-
34
- export function Dynamic<T extends Record<string, string>>(props: {
35
- params?: T,
36
- loader: (ctx: GenericContext, params?: T) => Promise<JSX.Element>
37
- children?: JSX.Element
38
- }): JSX.Element {
39
- return <div
40
- hx-get={DynamicReference(props.loader, props.params)}
41
- hx-trigger="load"
42
- hx-swap="outerHTML transition:true"
43
- style={{ display: "contents" }}
44
- >{props.children ? props.children : ""}</div>
45
- }
46
-
47
- let headCache: JSX.Element | null = null;
48
- const isProduction = process.env.NODE_ENV === "production";
49
- const clientEntry = await GetClientEntryURL();
50
- export function Scripts() {
51
- if (headCache) return headCache;
52
-
53
- const res = <>
54
- <link href={GetSheetUrl()} rel="stylesheet"></link>
55
- { isProduction ? "" : <script type="module" src="/@vite/client"></script> }
56
- <script type="module" src={clientEntry}></script>
57
- <script src={GetMountUrl()}></script>
58
- </>;
59
-
60
- if (isProduction) headCache = res;
61
- return res;
62
- }`);
63
- if (config.client) {
64
- console.info("Building client islands");
65
- await GenerateClient(config.client, true);
66
- }
@@ -1 +0,0 @@
1
- export declare function GetClientEntryURL(): Promise<any>;
@@ -1,12 +0,0 @@
1
- import { readFile } from "fs/promises";
2
- export async function GetClientEntryURL() {
3
- if (process.env.NODE_ENV !== "production")
4
- return "/app/entry.client.ts";
5
- const config = JSON.parse(await readFile("./dist/client/.vite/manifest.json", "utf8"));
6
- for (const key in config) {
7
- const def = config[key];
8
- if (!def.isEntry)
9
- continue;
10
- return def.file;
11
- }
12
- }
@@ -1,7 +0,0 @@
1
- /**
2
- * Builds the SSR and client side mounter for client components
3
- */
4
- export declare function GenerateClient(config: {
5
- adapter: string;
6
- source: string;
7
- }, force?: boolean): Promise<void>;