jeasx 1.7.2 → 1.8.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jeasx",
3
- "version": "1.7.2",
3
+ "version": "1.8.0",
4
4
  "description": "Jeasx - the ease of JSX with the power of SSR",
5
5
  "keywords": [
6
6
  "jsx",
@@ -28,10 +28,10 @@
28
28
  "@fastify/formbody": "8.0.2",
29
29
  "@fastify/multipart": "9.0.3",
30
30
  "@fastify/static": "8.1.1",
31
- "@types/node": "22.14.1",
32
- "esbuild": "0.25.2",
31
+ "@types/node": "22.15.17",
32
+ "esbuild": "0.25.4",
33
33
  "fastify": "5.3.2",
34
- "jsx-async-runtime": "1.0.0"
34
+ "jsx-async-runtime": "1.0.1"
35
35
  },
36
36
  "scripts": {
37
37
  "build": "esbuild --platform=node --format=esm --sourcemap=linked --sources-content=false --outdir=. serverless.ts"
package/serverless.js CHANGED
@@ -11,6 +11,7 @@ env();
11
11
  const NODE_ENV_IS_DEVELOPMENT = process.env.NODE_ENV === "development";
12
12
  const CWD = process.cwd();
13
13
  const FASTIFY_STATIC_HEADERS = process.env.FASTIFY_STATIC_HEADERS && JSON.parse(process.env.FASTIFY_STATIC_HEADERS);
14
+ const JEASX_ROUTE_CACHE_LIMIT = process.env.JEASX_ROUTE_CACHE_LIMIT && JSON.parse(process.env.JEASX_ROUTE_CACHE_LIMIT);
14
15
  var serverless_default = Fastify({
15
16
  logger: true,
16
17
  disableRequestLogging: JSON.parse(
@@ -36,6 +37,7 @@ var serverless_default = Fastify({
36
37
  }
37
38
  } : void 0
38
39
  }).decorateRequest("route", "").decorateRequest("path", "").addHook("onRequest", async (request, reply) => {
40
+ reply.header("Content-Type", "text/html; charset=utf-8");
39
41
  const index = request.url.indexOf("?");
40
42
  request.path = index === -1 ? request.url : request.url.slice(0, index);
41
43
  }).all("*", async (request, reply) => {
@@ -46,66 +48,76 @@ var serverless_default = Fastify({
46
48
  throw error;
47
49
  }
48
50
  });
49
- const modules = {};
51
+ const modules = /* @__PURE__ */ new Map();
50
52
  async function handler(request, reply) {
51
53
  let response;
52
54
  const context = {};
53
- const path = request.path;
54
- for (const route of generateRoutes(path)) {
55
- const modulePath = join(CWD, "dist", `routes${route}.js`);
56
- let module = modules[modulePath];
57
- if (module === null) {
58
- continue;
59
- }
60
- if (module === void 0) {
61
- try {
62
- if (NODE_ENV_IS_DEVELOPMENT) {
63
- if (typeof require === "function") {
64
- if (require.cache[modulePath]) {
65
- delete require.cache[modulePath];
55
+ try {
56
+ for (const route of generateRoutes(request.path)) {
57
+ let module = modules.get(route);
58
+ if (module === null) {
59
+ continue;
60
+ }
61
+ if (module === void 0) {
62
+ try {
63
+ const modulePath = join(CWD, "dist", `routes${route}.js`);
64
+ if (NODE_ENV_IS_DEVELOPMENT) {
65
+ if (typeof require === "function") {
66
+ if (require.cache[modulePath]) {
67
+ delete require.cache[modulePath];
68
+ }
69
+ module = await import(`file://${modulePath}`);
70
+ } else {
71
+ const mtime = (await stat(modulePath)).mtime.getTime();
72
+ module = await import(`file://${modulePath}?${mtime}`);
66
73
  }
67
- module = await import(`file://${modulePath}`);
68
74
  } else {
69
- const mtime = (await stat(modulePath)).mtime.getTime();
70
- module = await import(`file://${modulePath}?${mtime}`);
75
+ module = await import(`file://${modulePath}`);
76
+ modules.set(route, module);
77
+ }
78
+ } catch {
79
+ if (!NODE_ENV_IS_DEVELOPMENT) {
80
+ modules.set(route, null);
81
+ }
82
+ continue;
83
+ } finally {
84
+ if (typeof JEASX_ROUTE_CACHE_LIMIT === "number" && modules.size > JEASX_ROUTE_CACHE_LIMIT) {
85
+ modules.delete(modules.keys().next().value);
71
86
  }
72
- } else {
73
- module = modules[modulePath] = await import(`file://${modulePath}`);
74
- }
75
- } catch {
76
- if (!NODE_ENV_IS_DEVELOPMENT) {
77
- modules[modulePath] = null;
78
87
  }
88
+ }
89
+ request.route = route;
90
+ response = await module.default.call(context, {
91
+ request,
92
+ reply,
93
+ ...typeof response === "object" ? response : {}
94
+ });
95
+ if (reply.sent) {
96
+ return;
97
+ } else if (typeof response === "string" || Buffer.isBuffer(response) || isJSX(response)) {
98
+ break;
99
+ } else if (route.endsWith("/[...guard]") && (response === void 0 || typeof response === "object")) {
79
100
  continue;
101
+ } else if (route.endsWith("/[404]")) {
102
+ reply.status(404);
103
+ break;
104
+ } else if (reply.statusCode === 404) {
105
+ continue;
106
+ } else {
107
+ break;
80
108
  }
81
109
  }
82
- request.route = route;
83
- response = await module.default.call(context, {
84
- request,
85
- reply,
86
- ...typeof response === "object" ? response : {}
87
- });
88
- if (reply.sent) {
89
- return;
90
- } else if (typeof response === "string" || Buffer.isBuffer(response) || isJSX(response)) {
91
- break;
92
- } else if (route.endsWith("/[...guard]") && (response === void 0 || typeof response === "object")) {
93
- continue;
94
- } else if (route.endsWith("/[404]")) {
95
- reply.status(404);
96
- break;
97
- } else if (reply.statusCode === 404) {
98
- continue;
110
+ return await renderJSX(context, response);
111
+ } catch (error) {
112
+ const errorHandler = context["errorHandler"];
113
+ if (typeof errorHandler === "function") {
114
+ reply.status(500);
115
+ response = await errorHandler.call(context, error);
116
+ return await renderJSX(context, response);
99
117
  } else {
100
- break;
118
+ throw error;
101
119
  }
102
120
  }
103
- if (!reply.hasHeader("Content-Type")) {
104
- reply.header("Content-Type", "text/html; charset=utf-8");
105
- }
106
- const payload = isJSX(response) ? await jsxToString.call(context, response) : response;
107
- const responseHandler = context["response"];
108
- return typeof responseHandler === "function" ? await responseHandler(payload) : payload;
109
121
  }
110
122
  function generateRoutes(path) {
111
123
  const segments = generateSegments(path);
@@ -137,6 +149,11 @@ function generateEdges(path) {
137
149
  function isJSX(obj) {
138
150
  return !!obj && typeof obj === "object" && "type" in obj && "props" in obj;
139
151
  }
152
+ async function renderJSX(context, response) {
153
+ const payload = isJSX(response) ? await jsxToString.call(context, response) : response;
154
+ const responseHandler = context["responseHandler"];
155
+ return typeof responseHandler === "function" ? await responseHandler.call(context, payload) : payload;
156
+ }
140
157
  export {
141
158
  serverless_default as default
142
159
  };
package/serverless.js.map CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["serverless.ts"],
4
- "mappings": "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",
4
+ "mappings": "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",
5
5
  "names": []
6
6
  }
package/serverless.ts CHANGED
@@ -17,6 +17,10 @@ const FASTIFY_STATIC_HEADERS =
17
17
  process.env.FASTIFY_STATIC_HEADERS &&
18
18
  JSON.parse(process.env.FASTIFY_STATIC_HEADERS);
19
19
 
20
+ const JEASX_ROUTE_CACHE_LIMIT =
21
+ process.env.JEASX_ROUTE_CACHE_LIMIT &&
22
+ JSON.parse(process.env.JEASX_ROUTE_CACHE_LIMIT);
23
+
20
24
  declare module "fastify" {
21
25
  interface FastifyRequest {
22
26
  path: string; // Path without query parameters
@@ -61,6 +65,9 @@ export default Fastify({
61
65
  .decorateRequest("route", "")
62
66
  .decorateRequest("path", "")
63
67
  .addHook("onRequest", async (request, reply) => {
68
+ // Set default content type to text/html
69
+ reply.header("Content-Type", "text/html; charset=utf-8");
70
+ // Extract path from url
64
71
  const index = request.url.indexOf("?");
65
72
  request.path = index === -1 ? request.url : request.url.slice(0, index);
66
73
  })
@@ -74,7 +81,7 @@ export default Fastify({
74
81
  });
75
82
 
76
83
  // Cache for resolved route modules, 'null' means no module exists.
77
- const modules: { [path: string]: { default: Function } | null } = {};
84
+ const modules = new Map<string, { default: Function }>();
78
85
 
79
86
  /**
80
87
  * Resolves route module based on the request path and execute it.
@@ -85,97 +92,99 @@ async function handler(request: FastifyRequest, reply: FastifyReply) {
85
92
  // Global context object for route handlers
86
93
  const context = {};
87
94
 
88
- // Current request path
89
- const path = request.path;
90
-
91
- // Execute route handlers for current request
92
- for (const route of generateRoutes(path)) {
93
- const modulePath = join(CWD, "dist", `routes${route}.js`);
95
+ try {
96
+ // Execute route handlers for current request
97
+ for (const route of generateRoutes(request.path)) {
98
+ // Resolve module via cache
99
+ let module = modules.get(route);
94
100
 
95
- // Resolve module via cache
96
- let module = modules[modulePath];
97
-
98
- // Module was cached as not found?
99
- if (module === null) {
100
- continue;
101
- }
101
+ // Module was cached as not found?
102
+ if (module === null) {
103
+ continue;
104
+ }
102
105
 
103
- // Module was not loaded yet?
104
- if (module === undefined) {
105
- try {
106
- if (NODE_ENV_IS_DEVELOPMENT) {
107
- if (typeof require === "function") {
108
- // Bun: Remove module from cache before importing
109
- // as query parameter for import is ignored (see Node.js).
110
- if (require.cache[modulePath]) {
111
- delete require.cache[modulePath];
106
+ // Module was not loaded yet?
107
+ if (module === undefined) {
108
+ try {
109
+ const modulePath = join(CWD, "dist", `routes${route}.js`);
110
+ if (NODE_ENV_IS_DEVELOPMENT) {
111
+ if (typeof require === "function") {
112
+ // Bun: Remove module from cache before importing
113
+ // as query parameter for import is ignored (see Node.js).
114
+ if (require.cache[modulePath]) {
115
+ delete require.cache[modulePath];
116
+ }
117
+ module = await import(`file://${modulePath}`);
118
+ } else {
119
+ // Node.js: Use timestamp as query parameter to update modules.
120
+ const mtime = (await stat(modulePath)).mtime.getTime();
121
+ module = await import(`file://${modulePath}?${mtime}`);
112
122
  }
113
- module = await import(`file://${modulePath}`);
114
123
  } else {
115
- // Node.js: Use timestamp as query parameter to update modules.
116
- const mtime = (await stat(modulePath)).mtime.getTime();
117
- module = await import(`file://${modulePath}?${mtime}`);
124
+ // Load and cache module for non-development
125
+ module = await import(`file://${modulePath}`);
126
+ modules.set(route, module);
127
+ }
128
+ } catch {
129
+ if (!NODE_ENV_IS_DEVELOPMENT) {
130
+ // Cache module as not found
131
+ modules.set(route, null);
132
+ }
133
+ continue;
134
+ } finally {
135
+ // Remove oldest entry from cache if limit is reached
136
+ if (
137
+ typeof JEASX_ROUTE_CACHE_LIMIT === "number" &&
138
+ modules.size > JEASX_ROUTE_CACHE_LIMIT
139
+ ) {
140
+ modules.delete(modules.keys().next().value);
118
141
  }
119
- } else {
120
- // Load and cache module for non-development
121
- module = modules[modulePath] = await import(`file://${modulePath}`);
122
- }
123
- } catch {
124
- if (!NODE_ENV_IS_DEVELOPMENT) {
125
- // Cache module as not found
126
- modules[modulePath] = null;
127
142
  }
143
+ }
144
+
145
+ // Store current route in request
146
+ request.route = route;
147
+
148
+ // Call the handler with request, reply and optional props
149
+ response = await module.default.call(context, {
150
+ request,
151
+ reply,
152
+ ...(typeof response === "object" ? response : {}),
153
+ });
154
+
155
+ if (reply.sent) {
156
+ return;
157
+ } else if (
158
+ typeof response === "string" ||
159
+ Buffer.isBuffer(response) ||
160
+ isJSX(response)
161
+ ) {
162
+ break;
163
+ } else if (
164
+ route.endsWith("/[...guard]") &&
165
+ (response === undefined || typeof response === "object")
166
+ ) {
167
+ continue;
168
+ } else if (route.endsWith("/[404]")) {
169
+ reply.status(404);
170
+ break;
171
+ } else if (reply.statusCode === 404) {
128
172
  continue;
173
+ } else {
174
+ break;
129
175
  }
130
176
  }
131
-
132
- // Store current route in request
133
- request.route = route;
134
-
135
- // Call the handler with request, reply and optional props
136
- response = await module.default.call(context, {
137
- request,
138
- reply,
139
- ...(typeof response === "object" ? response : {}),
140
- });
141
-
142
- if (reply.sent) {
143
- return;
144
- } else if (
145
- typeof response === "string" ||
146
- Buffer.isBuffer(response) ||
147
- isJSX(response)
148
- ) {
149
- break;
150
- } else if (
151
- route.endsWith("/[...guard]") &&
152
- (response === undefined || typeof response === "object")
153
- ) {
154
- continue;
155
- } else if (route.endsWith("/[404]")) {
156
- reply.status(404);
157
- break;
158
- } else if (reply.statusCode === 404) {
159
- continue;
177
+ return await renderJSX(context, response);
178
+ } catch (error) {
179
+ const errorHandler = context["errorHandler"];
180
+ if (typeof errorHandler === "function") {
181
+ reply.status(500);
182
+ response = await errorHandler.call(context, error);
183
+ return await renderJSX(context, response);
160
184
  } else {
161
- break;
185
+ throw error;
162
186
  }
163
187
  }
164
-
165
- // Make sure a Content-Type header is set
166
- if (!reply.hasHeader("Content-Type")) {
167
- reply.header("Content-Type", "text/html; charset=utf-8");
168
- }
169
-
170
- const payload = isJSX(response)
171
- ? await jsxToString.call(context, response)
172
- : response;
173
-
174
- // Post-process the payload with an optional response handler
175
- const responseHandler = context["response"];
176
- return typeof responseHandler === "function"
177
- ? await responseHandler(payload)
178
- : payload;
179
188
  }
180
189
 
181
190
  /**
@@ -240,3 +249,18 @@ function generateEdges(path: string): string[] {
240
249
  function isJSX(obj: unknown): boolean {
241
250
  return !!obj && typeof obj === "object" && "type" in obj && "props" in obj;
242
251
  }
252
+
253
+ /**
254
+ * Renders JSX to string and applies optional response handler.
255
+ */
256
+ async function renderJSX(context: object, response: unknown) {
257
+ const payload = isJSX(response)
258
+ ? await jsxToString.call(context, response)
259
+ : response;
260
+
261
+ // Post-process the payload with an optional response handler
262
+ const responseHandler = context["responseHandler"];
263
+ return typeof responseHandler === "function"
264
+ ? await responseHandler.call(context, payload)
265
+ : payload;
266
+ }