htmx-router 1.0.0-alpha.6 → 1.0.0-pre2

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 (56) hide show
  1. package/cli/index.js +38 -0
  2. package/cookies.d.ts +7 -3
  3. package/cookies.js +30 -10
  4. package/css.d.ts +10 -2
  5. package/css.js +21 -16
  6. package/defer.d.ts +14 -0
  7. package/defer.js +80 -0
  8. package/endpoint.d.ts +12 -5
  9. package/endpoint.js +11 -5
  10. package/event-source.js +6 -13
  11. package/index.d.ts +7 -7
  12. package/internal/compile/manifest.js +6 -5
  13. package/internal/component/defer.js +19 -0
  14. package/internal/component/index.d.ts +4 -0
  15. package/internal/component/index.js +4 -0
  16. package/internal/mount.d.ts +9 -1
  17. package/internal/mount.js +13 -6
  18. package/internal/request/http.d.ts +1 -1
  19. package/internal/request/index.d.ts +2 -1
  20. package/internal/request/native.d.ts +1 -1
  21. package/internal/request/native.js +1 -1
  22. package/internal/router.d.ts +15 -0
  23. package/internal/router.js +24 -0
  24. package/package.json +6 -8
  25. package/readme.md +20 -3
  26. package/router.d.ts +18 -36
  27. package/router.js +33 -58
  28. package/shell.d.ts +1 -1
  29. package/shell.js +15 -7
  30. package/util/parameters.d.ts +7 -4
  31. package/util/parameters.js +1 -14
  32. package/dynamic.d.ts +0 -5
  33. package/dynamic.js +0 -42
  34. package/example/eventdim-react/package.json +0 -67
  35. package/example/eventdim-react/server.js +0 -90
  36. package/example/island-react/global.d.ts +0 -8
  37. package/example/island-react/package.json +0 -38
  38. package/example/island-react/server.js +0 -58
  39. package/internal/cli/index.js +0 -15
  40. package/internal/compile/router.d.ts +0 -1
  41. package/internal/compile/router.js +0 -51
  42. package/internal/component/dynamic.js +0 -18
  43. package/internal/hash.d.ts +0 -4
  44. package/internal/hash.js +0 -10
  45. package/request/http.d.ts +0 -10
  46. package/request/http.js +0 -59
  47. package/request/index.d.ts +0 -13
  48. package/request/index.js +0 -3
  49. package/request/native.d.ts +0 -9
  50. package/request/native.js +0 -46
  51. package/vite/code-splitting.d.ts +0 -4
  52. package/vite/code-splitting.js +0 -14
  53. /package/{internal/cli → cli}/config.d.ts +0 -0
  54. /package/{internal/cli → cli}/config.js +0 -0
  55. /package/{internal/cli → cli}/index.d.ts +0 -0
  56. /package/internal/component/{dynamic.d.ts → defer.d.ts} +0 -0
package/package.json CHANGED
@@ -1,19 +1,17 @@
1
1
  {
2
2
  "name": "htmx-router",
3
- "version": "1.0.0-alpha.6",
4
- "description": "A simple SSR framework with dynamic+client islands",
3
+ "version": "1.0.0-pre2",
4
+ "description": "A lightweight SSR framework with server+client islands",
5
5
  "keywords": [
6
- "htmx",
7
- "router",
8
- ""
6
+ "htmx", "router", "client islands", "ssr", "vite"
9
7
  ],
10
8
  "main": "./index.js",
11
9
  "type": "module",
12
10
  "scripts": {
13
- "build": "tsc && tsc-alias"
11
+ "build": "tsc"
14
12
  },
15
13
  "bin": {
16
- "htmx-router": "./cli/index.js"
14
+ "htmx-router": "cli/index.js"
17
15
  },
18
16
  "repository": {
19
17
  "type": "git",
@@ -31,8 +29,8 @@
31
29
  },
32
30
  "devDependencies": {
33
31
  "@types/node": "^20.4.5",
32
+ "chalk": "^5.4.1",
34
33
  "ts-node": "^10.9.1",
35
- "tsc-alias": "^1.8.10",
36
34
  "typescript": "^5.1.6"
37
35
  }
38
36
  }
package/readme.md CHANGED
@@ -1,5 +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; client/server bundle spitting, 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
- > Apologies the docs have not yet been updated for this version, please wait for the final release for actual docs.
5
+ Features:
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
17
+
18
+ ## Documentation
19
+ See https://htmx-router.ajanibilby.com/
20
+
21
+ ## API
22
+ See https://htmx-router.ajanibilby.com/api
package/router.d.ts CHANGED
@@ -1,36 +1,27 @@
1
- import { Parameterized, ParameterShaper } from './util/parameters.js';
1
+ import type { GenericContext } from "./internal/router.js";
2
+ import { Parameterized, ParameterPrelude, ParameterShaper } from './util/parameters.js';
2
3
  import { RouteModule } from "./index.js";
3
4
  import { Cookies } from './cookies.js';
4
- export declare function GenerateRouteTree(modules: Record<string, unknown>): RouteTree;
5
- export declare class GenericContext {
6
- request: Request;
7
- headers: Headers;
8
- cookie: Cookies;
9
- params: {
10
- [key: string]: string;
11
- };
12
- url: URL;
13
- render: (res: JSX.Element) => Response;
14
- constructor(request: GenericContext["request"], url: GenericContext["url"], renderer: GenericContext["render"]);
15
- shape<T extends ParameterShaper>(shape: T): RouteContext<T>;
16
- }
5
+ export declare function GenerateRouteTree(props: {
6
+ modules: Record<string, unknown>;
7
+ scope: string;
8
+ }): RouteTree;
17
9
  export declare class RouteContext<T extends ParameterShaper = {}> {
18
- request: Request;
19
- headers: Headers;
20
- cookie: Cookies;
21
- params: Parameterized<T>;
22
- url: URL;
10
+ readonly request: Request;
11
+ readonly headers: Headers;
12
+ readonly cookie: Cookies;
13
+ readonly params: Parameterized<T>;
14
+ readonly url: URL;
23
15
  render: (res: JSX.Element) => Response;
24
- constructor(base: GenericContext, shape: T);
16
+ constructor(base: GenericContext | RouteContext, params: ParameterPrelude<T>, shape: T);
25
17
  }
26
18
  export declare class RouteTree {
27
- root: boolean;
28
- nested: Map<string, RouteTree>;
29
- index: RouteLeaf | null;
30
- slug: RouteLeaf | null;
31
- wild: RouteTree | null;
32
- wildCard: string;
33
- constructor(root?: boolean);
19
+ private nested;
20
+ private index;
21
+ private slug;
22
+ private wild;
23
+ private wildCard;
24
+ constructor();
34
25
  ingest(path: string | string[], module: RouteModule<any>): void;
35
26
  resolve(fragments: string[], ctx: GenericContext): Promise<Response | null>;
36
27
  private _resolve;
@@ -38,14 +29,5 @@ export declare class RouteTree {
38
29
  private resolveNext;
39
30
  private resolveWild;
40
31
  private resolveSlug;
41
- private resolveNative;
42
32
  private unwrap;
43
33
  }
44
- declare class RouteLeaf {
45
- module: RouteModule<any>;
46
- constructor(module: RouteModule<any>);
47
- resolve(ctx: GenericContext): Promise<Response | null>;
48
- error(ctx: GenericContext, e: unknown): Promise<Response>;
49
- private renderWrapper;
50
- }
51
- export {};
package/router.js CHANGED
@@ -1,43 +1,29 @@
1
1
  import { ServerOnlyWarning } from "./internal/util.js";
2
2
  ServerOnlyWarning("router");
3
+ // builtin routes
3
4
  import * as endpoint from './endpoint.js';
4
- import * as dynamic from './dynamic.js';
5
+ import * as dynamic from './defer.js';
5
6
  import * as mount from './internal/mount.js';
6
7
  import * as css from './css.js';
7
- import { Parameterize } from './util/parameters.js';
8
- import { Cookies } from './cookies.js';
9
- export function GenerateRouteTree(modules) {
8
+ export function GenerateRouteTree(props) {
9
+ if (!props.scope.endsWith("/"))
10
+ props.scope += "/";
10
11
  const tree = new RouteTree();
11
- for (const path in modules) {
12
- const mod = modules[path];
12
+ for (const path in props.modules) {
13
+ const mod = props.modules[path];
13
14
  const tail = path.lastIndexOf(".");
14
- const url = path.slice(9, tail);
15
+ const url = path.slice(props.scope.length, tail);
15
16
  tree.ingest(url, mod);
16
17
  if (mod.route)
17
18
  mod.route(url);
18
19
  }
20
+ // ingest router builtins
21
+ tree.ingest(endpoint.path, endpoint);
22
+ tree.ingest(dynamic.path, dynamic);
23
+ tree.ingest(mount.path, mount);
24
+ tree.ingest(css.path, css);
19
25
  return tree;
20
26
  }
21
- export class GenericContext {
22
- request;
23
- headers; // response headers
24
- cookie;
25
- params;
26
- url;
27
- render;
28
- constructor(request, url, renderer) {
29
- this.cookie = new Cookies(request.headers);
30
- this.headers = new Headers();
31
- this.request = request;
32
- this.params = {};
33
- this.url = url;
34
- this.render = renderer;
35
- this.headers.set("x-powered-by", "htmx-router");
36
- }
37
- shape(shape) {
38
- return new RouteContext(this, shape);
39
- }
40
- }
41
27
  export class RouteContext {
42
28
  request;
43
29
  headers; // response headers
@@ -45,31 +31,39 @@ export class RouteContext {
45
31
  params;
46
32
  url;
47
33
  render;
48
- constructor(base, shape) {
49
- this.params = Parameterize(base.params, shape);
34
+ constructor(base, params, shape) {
50
35
  this.cookie = base.cookie;
51
36
  this.headers = base.headers;
52
37
  this.request = base.request;
53
38
  this.render = base.render;
54
39
  this.url = base.url;
40
+ this.params = {};
41
+ for (const key in shape) {
42
+ if (!(key in params))
43
+ console.warn(`Parameter ${key} not present in route, but defined in parameters`);
44
+ const func = shape[key];
45
+ const val = func(params[key] || "");
46
+ // NaN moment
47
+ if (func === Number && typeof val === "number" && isNaN(val))
48
+ throw new Error("Invalid Number");
49
+ this.params[key] = val;
50
+ }
55
51
  }
56
52
  }
57
53
  export class RouteTree {
58
- root;
59
54
  nested;
60
55
  // Leaf nodes
61
- index; // about._index
62
- // Wild card route
56
+ index; // _index.tsx
57
+ // Wild card routes
63
58
  slug; // $
64
59
  wild; // e.g. $userID
65
60
  wildCard;
66
- constructor(root = true) {
67
- this.root = root;
61
+ constructor() {
68
62
  this.nested = new Map();
63
+ this.index = null;
69
64
  this.wildCard = "";
70
- this.slug = null;
71
65
  this.wild = null;
72
- this.index = null;
66
+ this.slug = null;
73
67
  }
74
68
  ingest(path, module) {
75
69
  if (!Array.isArray(path))
@@ -87,7 +81,7 @@ export class RouteTree {
87
81
  // Check wildcard isn't being changed
88
82
  if (!this.wild) {
89
83
  this.wildCard = wildCard;
90
- this.wild = new RouteTree(false);
84
+ this.wild = new RouteTree();
91
85
  }
92
86
  else if (wildCard !== this.wildCard) {
93
87
  throw new Error(`Redefinition of wild card ${this.wildCard} to ${wildCard}`);
@@ -98,7 +92,7 @@ export class RouteTree {
98
92
  }
99
93
  let next = this.nested.get(path[0]);
100
94
  if (!next) {
101
- next = new RouteTree(false);
95
+ next = new RouteTree();
102
96
  this.nested.set(path[0], next);
103
97
  }
104
98
  path.splice(0, 1);
@@ -115,8 +109,7 @@ export class RouteTree {
115
109
  }
116
110
  }
117
111
  async _resolve(fragments, ctx) {
118
- let res = await this.resolveNative(fragments, ctx)
119
- || await this.resolveIndex(fragments, ctx)
112
+ let res = await this.resolveIndex(fragments, ctx)
120
113
  || await this.resolveNext(fragments, ctx)
121
114
  || await this.resolveWild(fragments, ctx)
122
115
  || await this.resolveSlug(fragments, ctx);
@@ -161,15 +154,6 @@ export class RouteTree {
161
154
  : null;
162
155
  return res;
163
156
  }
164
- async resolveNative(fragments, ctx) {
165
- if (!this.root)
166
- return null;
167
- if (fragments.length < 2)
168
- return null;
169
- if (fragments[0] != "_")
170
- return null;
171
- return await ResolveNatively(fragments, ctx);
172
- }
173
157
  async unwrap(ctx, res) {
174
158
  if (!this.slug)
175
159
  throw res;
@@ -220,12 +204,3 @@ class RouteLeaf {
220
204
  return null;
221
205
  }
222
206
  }
223
- async function ResolveNatively(fragments, ctx) {
224
- switch (fragments[1]) {
225
- case "dynamic": return dynamic._resolve(fragments, ctx);
226
- case "endpoint": return endpoint._resolve(fragments, ctx);
227
- case "mount": return mount._resolve(fragments);
228
- case "style": return css._resolve(fragments);
229
- }
230
- return null;
231
- }
package/shell.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export type ShellOptions<D = {}> = D & MetaDescriptor;
2
- export declare function ApplyMetaDescriptorDefaults(options: ShellOptions, defaults: Readonly<Partial<ShellOptions>>): void;
2
+ export declare function ApplyMetaDefaults(options: ShellOptions, defaults: Readonly<Partial<ShellOptions>>): void;
3
3
  export type InferShellOptions<F> = F extends (jsx: any, options: infer U) => any ? U : never;
4
4
  export type MetaDescriptor = {
5
5
  title?: string;
package/shell.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ServerOnlyWarning } from "./internal/util.js";
2
2
  ServerOnlyWarning("shell");
3
- export function ApplyMetaDescriptorDefaults(options, defaults) {
3
+ export function ApplyMetaDefaults(options, defaults) {
4
4
  if (defaults.title && !options.title)
5
5
  options.title = defaults.title;
6
6
  if (defaults.description && !options.description)
@@ -26,14 +26,22 @@ export function RenderMetaDescriptor(options) {
26
26
  for (const json of options.jsonLD) {
27
27
  out += `<script type="application/ld+json">${JSON.stringify(json)}</script>\n`;
28
28
  }
29
- // Auto apply og:title + og:description if not present
30
- if (options.title && !options.og?.title)
31
- out += `<meta property="og:title" content="${EscapeHTML(options.title)}">\n`;
32
- if (options.description && !options.og?.description)
33
- out += `<meta property="og:description" content="${EscapeHTML(options.description)}">\n`;
34
29
  // Apply open graphs
35
- if (options.og)
30
+ if (options.og) {
31
+ // Infer from meta if not present
32
+ if (!options.og.title)
33
+ options.og.title = options.title;
34
+ if (!options.og.description)
35
+ options.og.title = options.description;
36
36
  out += RenderOpenGraph(options.og);
37
+ }
38
+ else {
39
+ // Auto apply og:title + og:description if og not present
40
+ if (options.title)
41
+ out += `<meta property="og:title" content="${EscapeHTML(options.title)}">\n`;
42
+ if (options.description)
43
+ out += `<meta property="og:description" content="${EscapeHTML(options.description)}">\n`;
44
+ }
37
45
  return out;
38
46
  }
39
47
  function RenderOpenGraph(og) {
@@ -1,7 +1,10 @@
1
+ export type ParameterShaper = Record<string, (val: string) => any>;
2
+ export type ParameterPrelude<T extends ParameterShaper> = {
3
+ [K in keyof T]: string;
4
+ };
1
5
  export type Parameterized<T extends ParameterShaper> = {
2
6
  [K in keyof T]: ReturnType<T[K]>;
3
7
  };
4
- export type ParameterShaper = Record<string, (val: string) => any>;
5
- export declare function Parameterize<T extends ParameterShaper>(params: {
6
- [key: string]: string;
7
- }, shape: T): Parameterized<T>;
8
+ export type Parameterizer<T extends ParameterShaper> = {
9
+ [K in keyof T]: (val: ReturnType<T[K]>) => string;
10
+ };
@@ -1,14 +1 @@
1
- export function Parameterize(params, shape) {
2
- const out = {};
3
- for (const key in shape) {
4
- if (!(key in params))
5
- console.warn(`Parameter ${key} not present in route, but defined in parameters`);
6
- const func = shape[key];
7
- const val = func(params[key] || "");
8
- // NaN moment
9
- if (func === Number && typeof val === "number" && isNaN(val))
10
- throw new Error("Invalid Number");
11
- out[key] = val;
12
- }
13
- return out;
14
- }
1
+ export {};
package/dynamic.d.ts DELETED
@@ -1,5 +0,0 @@
1
- import type { GenericContext } from "./router.js";
2
- type Loader<T> = (ctx: GenericContext, params: T) => Promise<JSX.Element | Response>;
3
- export declare function DynamicReference<T extends Record<string, string>>(loader: Loader<T>, params?: T): string;
4
- export declare function _resolve(fragments: string[], ctx: GenericContext): Promise<Response | null>;
5
- export {};
package/dynamic.js DELETED
@@ -1,42 +0,0 @@
1
- import { ServerOnlyWarning } from "./internal/util.js";
2
- ServerOnlyWarning("dynamic-ref");
3
- import { QuickHash } from "./internal/util.js";
4
- const registry = new Map();
5
- const index = new Map();
6
- function Register(load) {
7
- const existing = index.get(load);
8
- if (existing)
9
- return existing;
10
- const hash = QuickHash(String(load));
11
- const name = `${encodeURIComponent(load.name)}-${hash}`;
12
- registry.set(name, load);
13
- const url = `/_/dynamic/${name}`;
14
- index.set(load, url);
15
- return url;
16
- }
17
- export function DynamicReference(loader, params) {
18
- let url = Register(loader);
19
- if (params) {
20
- const query = new URLSearchParams();
21
- if (params)
22
- for (const key in params)
23
- query.set(key, params[key]);
24
- url += "?" + query.toString();
25
- }
26
- return url;
27
- }
28
- export async function _resolve(fragments, ctx) {
29
- if (!fragments[2])
30
- return null;
31
- const endpoint = registry.get(fragments[2]);
32
- if (!endpoint)
33
- return null;
34
- const props = {};
35
- for (const [key, value] of ctx.url.searchParams)
36
- props[key] = value;
37
- ctx.headers.set("X-Partial", "true");
38
- const res = await endpoint(ctx, props);
39
- if (res instanceof Response)
40
- return res;
41
- return ctx.render(res);
42
- }
@@ -1,67 +0,0 @@
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
- }
@@ -1,90 +0,0 @@
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
- });
@@ -1,8 +0,0 @@
1
- import { ReactNode } from 'react';
2
-
3
- declare global {
4
- namespace JSX {
5
- type Element = ReactNode;
6
- interface HTMLAttributes extends HtmxAttributes {}
7
- }
8
- }
@@ -1,38 +0,0 @@
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
- }