jeasx 2.7.3 → 2.7.5
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/{esbuild.config.js → build.js} +1 -1
- package/cli.js +7 -27
- package/env.js +3 -9
- package/package.json +4 -4
- package/serverless.js +35 -29
- package/serverless.js.map +2 -2
- package/serverless.ts +52 -46
- /package/{server.js → start.js} +0 -0
|
@@ -66,7 +66,7 @@ const BROWSER_OPTIONS = {
|
|
|
66
66
|
.filter((path) => /\[.+\]\.js$/.test(path))
|
|
67
67
|
.map((path) => path.slice("dist".length, -".js".length));
|
|
68
68
|
await writeFile(
|
|
69
|
-
join(CWD, "dist",
|
|
69
|
+
join(CWD, "dist", "[--metadata--].js"),
|
|
70
70
|
`export default ${JSON.stringify({ routes })};`,
|
|
71
71
|
);
|
|
72
72
|
}
|
package/cli.js
CHANGED
|
@@ -3,46 +3,26 @@ import fs from "node:fs/promises";
|
|
|
3
3
|
|
|
4
4
|
switch (process.argv[2]) {
|
|
5
5
|
case "start":
|
|
6
|
-
await start
|
|
6
|
+
await import("./start.js");
|
|
7
7
|
break;
|
|
8
8
|
|
|
9
9
|
case "build":
|
|
10
|
-
await build
|
|
10
|
+
await import("./build.js");
|
|
11
11
|
break;
|
|
12
12
|
|
|
13
13
|
case "dev":
|
|
14
|
-
|
|
14
|
+
process.env.NODE_ENV = "development";
|
|
15
|
+
await import("./build.js");
|
|
16
|
+
await import("./start.js");
|
|
15
17
|
break;
|
|
16
18
|
|
|
17
19
|
case "clean":
|
|
18
|
-
await
|
|
19
|
-
break;
|
|
20
|
-
|
|
21
|
-
case "help":
|
|
22
|
-
console.info(`Usage: jeasx [start|build|dev|clean|help]`);
|
|
20
|
+
await fs.rm("dist", { recursive: true, force: true, maxRetries: 3 });
|
|
23
21
|
break;
|
|
24
22
|
|
|
25
23
|
default:
|
|
26
|
-
console.
|
|
24
|
+
console.info(`💡 Usage: jeasx [start|build|dev|clean]`);
|
|
27
25
|
process.exit(1);
|
|
28
26
|
}
|
|
29
27
|
|
|
30
|
-
async function start() {
|
|
31
|
-
await import("./server.js");
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async function build() {
|
|
35
|
-
await import("./esbuild.config.js");
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async function dev() {
|
|
39
|
-
process.env.NODE_ENV = "development";
|
|
40
|
-
await build();
|
|
41
|
-
await start();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function clean() {
|
|
45
|
-
await fs.rm("dist", { recursive: true, force: true, maxRetries: 3 });
|
|
46
|
-
}
|
|
47
|
-
|
|
48
28
|
export {};
|
package/env.js
CHANGED
|
@@ -10,15 +10,9 @@ import { existsSync } from "node:fs";
|
|
|
10
10
|
* 5. .env.defaults
|
|
11
11
|
*/
|
|
12
12
|
export default function env() {
|
|
13
|
-
if (process.loadEnvFile) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
? [`.env.${process.env.NODE_ENV}.local`, `.env.${process.env.NODE_ENV}`]
|
|
17
|
-
: []),
|
|
18
|
-
".env.local",
|
|
19
|
-
".env",
|
|
20
|
-
".env.defaults",
|
|
21
|
-
]
|
|
13
|
+
if (typeof process.loadEnvFile === "function") {
|
|
14
|
+
const env = process.env.NODE_ENV;
|
|
15
|
+
[...(env ? [`.env.${env}.local`, `.env.${env}`] : []), ".env.local", ".env", ".env.defaults"]
|
|
22
16
|
.filter(existsSync)
|
|
23
17
|
.forEach(process.loadEnvFile);
|
|
24
18
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jeasx",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.5",
|
|
4
4
|
"description": "Jeasx - the ease of JSX with the power of SSR",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"async",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"jeasx": "cli.js"
|
|
25
25
|
},
|
|
26
26
|
"type": "module",
|
|
27
|
-
"main": "
|
|
27
|
+
"main": "start.js",
|
|
28
28
|
"scripts": {
|
|
29
29
|
"build": "esbuild --platform=node --format=esm --sourcemap=linked --outdir=. serverless.ts"
|
|
30
30
|
},
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
"@fastify/formbody": "8.0.2",
|
|
34
34
|
"@fastify/multipart": "10.0.0",
|
|
35
35
|
"@fastify/static": "9.1.3",
|
|
36
|
-
"@types/node": "
|
|
36
|
+
"@types/node": "26.0.1",
|
|
37
37
|
"esbuild": "0.28.1",
|
|
38
|
-
"fastify": "5.
|
|
38
|
+
"fastify": "5.9.0",
|
|
39
39
|
"jsx-async-runtime": "2.1.3"
|
|
40
40
|
},
|
|
41
41
|
"allowScripts": {
|
package/serverless.js
CHANGED
|
@@ -14,7 +14,9 @@ const NODE_ENV_IS_DEVELOPMENT = process.env.NODE_ENV === "development";
|
|
|
14
14
|
const MODULE_BY_ROUTE = {};
|
|
15
15
|
if (!NODE_ENV_IS_DEVELOPMENT) {
|
|
16
16
|
const { routes } = (await import(`file://${join(CWD, "dist", "[--metadata--].js")}`)).default;
|
|
17
|
-
|
|
17
|
+
for (const route of routes) {
|
|
18
|
+
MODULE_BY_ROUTE[route] = join(CWD, "dist", `${route}.js`);
|
|
19
|
+
}
|
|
18
20
|
}
|
|
19
21
|
const FASTIFY_SERVER = CONFIG.FASTIFY_SERVER ?? ((fastify2) => fastify2);
|
|
20
22
|
var serverless_default = FASTIFY_SERVER(
|
|
@@ -59,36 +61,38 @@ async function handler(request, reply) {
|
|
|
59
61
|
if (module === void 0 && !NODE_ENV_IS_DEVELOPMENT) {
|
|
60
62
|
continue;
|
|
61
63
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
try {
|
|
65
|
+
if (typeof module === "string") {
|
|
66
|
+
module = MODULE_BY_ROUTE[route] = await import(`file://${module}`);
|
|
67
|
+
} else if (module === void 0 && NODE_ENV_IS_DEVELOPMENT) {
|
|
64
68
|
const modulePath = join(CWD, "dist", `${route}.js`);
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
if (require.cache[modulePath]) {
|
|
68
|
-
delete require.cache[modulePath];
|
|
69
|
-
}
|
|
70
|
-
module = await import(`file://${modulePath}`);
|
|
71
|
-
} else {
|
|
72
|
-
const mtime = (await stat(modulePath)).mtime.getTime();
|
|
73
|
-
module = await import(`file://${modulePath}?${mtime}`);
|
|
74
|
-
}
|
|
75
|
-
} else {
|
|
76
|
-
module = await import(`file://${modulePath}`);
|
|
77
|
-
MODULE_BY_ROUTE[route] = module;
|
|
78
|
-
}
|
|
79
|
-
} catch (e) {
|
|
80
|
-
switch (e.code) {
|
|
81
|
-
case "ENOENT":
|
|
82
|
-
case "ENOTDIR":
|
|
83
|
-
case "ERR_MODULE_NOT_FOUND":
|
|
84
|
-
continue;
|
|
85
|
-
default:
|
|
86
|
-
throw e;
|
|
69
|
+
if (typeof require === "function" && require.cache[modulePath]) {
|
|
70
|
+
delete require.cache[modulePath];
|
|
87
71
|
}
|
|
72
|
+
const mtime = (await stat(modulePath)).mtime.getTime();
|
|
73
|
+
module = await import(`file://${modulePath}?${mtime}`);
|
|
74
|
+
}
|
|
75
|
+
} catch (e) {
|
|
76
|
+
switch (e.code) {
|
|
77
|
+
case "ENOENT":
|
|
78
|
+
case "ENOTDIR":
|
|
79
|
+
case "ERR_MODULE_NOT_FOUND":
|
|
80
|
+
continue;
|
|
81
|
+
default:
|
|
82
|
+
throw e;
|
|
88
83
|
}
|
|
89
84
|
}
|
|
85
|
+
if (!module || typeof module !== "object") {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
90
88
|
request.route = route;
|
|
91
|
-
response = typeof module.default === "function" ?
|
|
89
|
+
response = typeof module.default === "function" ? (
|
|
90
|
+
// Call functions with context as `this` and props as parameters,
|
|
91
|
+
await module.default.call(context, props)
|
|
92
|
+
) : (
|
|
93
|
+
// otherwise return default export.
|
|
94
|
+
module.default
|
|
95
|
+
);
|
|
92
96
|
if (reply.sent) {
|
|
93
97
|
return;
|
|
94
98
|
} else if (route.endsWith("/[404]")) {
|
|
@@ -123,9 +127,11 @@ function generateRoutes(path) {
|
|
|
123
127
|
const routes = [];
|
|
124
128
|
const segments = [""];
|
|
125
129
|
let current = "";
|
|
126
|
-
for (const segment of path.split("/")
|
|
127
|
-
|
|
128
|
-
|
|
130
|
+
for (const segment of path.split("/")) {
|
|
131
|
+
if (segment !== "") {
|
|
132
|
+
current += `/${segment}`;
|
|
133
|
+
segments.push(current);
|
|
134
|
+
}
|
|
129
135
|
}
|
|
130
136
|
segments.reverse();
|
|
131
137
|
for (let i = segments.length - 1; i >= 0; i--) {
|
package/serverless.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["serverless.ts"],
|
|
4
|
-
"sourcesContent": ["import fastifyCookie, { FastifyCookieOptions } from \"@fastify/cookie\";\nimport fastifyFormbody, { FastifyFormbodyOptions } from \"@fastify/formbody\";\nimport fastifyMultipart, { FastifyMultipartOptions } from \"@fastify/multipart\";\nimport fastifyStatic, { FastifyStaticOptions } from \"@fastify/static\";\nimport fastify, {\n FastifyInstance,\n FastifyReply,\n FastifyRequest,\n FastifyServerOptions,\n} from \"fastify\";\nimport { jsxToString } from \"jsx-async-runtime\";\nimport { stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport env from \"./env.js\";\n\nenv();\n\nconst CWD = process.cwd();\nconst CONFIG = (await import(`file://${join(CWD, \"jeasx.config.js\")}`)).default;\nconst NODE_ENV_IS_DEVELOPMENT = process.env.NODE_ENV === \"development\";\n\n//
|
|
5
|
-
"mappings": "AAAA,OAAO,mBAA6C;AACpD,OAAO,qBAAiD;AACxD,OAAO,sBAAmD;AAC1D,OAAO,mBAA6C;AACpD,OAAO,aAKA;AACP,SAAS,mBAAmB;AAC5B,SAAS,YAAY;AACrB,SAAS,YAAY;AACrB,OAAO,SAAS;AAEhB,IAAI;AAEJ,MAAM,MAAM,QAAQ,IAAI;AACxB,MAAM,UAAU,MAAM,OAAO,UAAU,KAAK,KAAK,iBAAiB,CAAC,KAAK;AACxE,MAAM,0BAA0B,QAAQ,IAAI,aAAa;AAGzD,MAAM,
|
|
4
|
+
"sourcesContent": ["import fastifyCookie, { FastifyCookieOptions } from \"@fastify/cookie\";\nimport fastifyFormbody, { FastifyFormbodyOptions } from \"@fastify/formbody\";\nimport fastifyMultipart, { FastifyMultipartOptions } from \"@fastify/multipart\";\nimport fastifyStatic, { FastifyStaticOptions } from \"@fastify/static\";\nimport fastify, {\n FastifyInstance,\n FastifyReply,\n FastifyRequest,\n FastifyServerOptions,\n} from \"fastify\";\nimport { jsxToString } from \"jsx-async-runtime\";\nimport { stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport env from \"./env.js\";\n\nenv();\n\nconst CWD = process.cwd();\nconst CONFIG = (await import(`file://${join(CWD, \"jeasx.config.js\")}`)).default;\nconst NODE_ENV_IS_DEVELOPMENT = process.env.NODE_ENV === \"development\";\n\n// Mapping for route modules used in non-development environments.\nconst MODULE_BY_ROUTE: Record<string, { default: Function } | string> = {};\n\n// Initialize the mapping with absolute paths for all known modules.\n// Modules are lazily loaded on their first request for a specific route.\nif (!NODE_ENV_IS_DEVELOPMENT) {\n const { routes } = (await import(`file://${join(CWD, \"dist\", \"[--metadata--].js\")}`)).default as {\n routes: string[];\n };\n // Map route identifiers to their absolute file system paths in the build directory.\n for (const route of routes) {\n MODULE_BY_ROUTE[route] = join(CWD, \"dist\", `${route}.js`);\n }\n}\n\ndeclare module \"fastify\" {\n interface FastifyRequest {\n path: string; // Path without query parameters\n route: string; // Path to resolved route handler\n }\n}\n\n// Enhance Fastify server from userland\nconst FASTIFY_SERVER = (CONFIG.FASTIFY_SERVER ?? ((fastify) => fastify)) as (\n fastify: FastifyInstance,\n) => FastifyInstance;\n\n// Create and export a Fastify instance\nexport default FASTIFY_SERVER(\n fastify({\n ...(CONFIG.FASTIFY_SERVER_OPTIONS?.() as FastifyServerOptions),\n }),\n)\n // Create encapsulation context\n .register((fastify) => {\n fastify\n .register(fastifyCookie, {\n ...(CONFIG.FASTIFY_COOKIE_OPTIONS?.() as FastifyCookieOptions),\n })\n .register(fastifyFormbody, {\n ...(CONFIG.FASTIFY_FORMBODY_OPTIONS?.() as FastifyFormbodyOptions),\n })\n .register(fastifyMultipart, {\n ...(CONFIG.FASTIFY_MULTIPART_OPTIONS?.() as FastifyMultipartOptions),\n })\n .register(fastifyStatic, {\n root: [\"public\", \"dist\"].map((dir) => join(CWD, dir)),\n wildcard: false,\n globIgnore: [\"/**/\\\\[*\\\\].js?(.map)\"],\n ...(CONFIG.FASTIFY_STATIC_OPTIONS?.() as FastifyStaticOptions),\n })\n .decorateRequest(\"route\", \"\")\n .decorateRequest(\"path\", \"\")\n .addHook(\"onRequest\", async (request) => {\n // Extract path from url\n const index = request.url.indexOf(\"?\");\n request.path = index === -1 ? request.url : request.url.slice(0, index);\n })\n .all(\"*\", async (request: FastifyRequest, reply: FastifyReply) => {\n try {\n const payload = await handler(request, reply);\n if (\n reply.getHeader(\"content-type\") === undefined &&\n (typeof payload === \"string\" || Buffer.isBuffer(payload))\n ) {\n reply.type(\"text/html; charset=utf-8\");\n }\n return payload;\n } catch (error) {\n request.log.error(error);\n throw error;\n }\n });\n });\n\n/**\n * Resolves route module based on the request path and execute it.\n */\nasync function handler(request: FastifyRequest, reply: FastifyReply) {\n let response: unknown;\n\n // Global context object for route handlers\n const context = {};\n\n // Default props for route handlers\n const props = { request, reply };\n\n try {\n // Execute route handlers for current request\n for (const route of generateRoutes(request.path)) {\n // Resolve module or path to module\n let module = MODULE_BY_ROUTE[route];\n\n // Skip processing if the route path was not initialized.\n if (module === undefined && !NODE_ENV_IS_DEVELOPMENT) {\n continue;\n }\n\n // Module was not loaded yet?\n try {\n if (typeof module === \"string\") {\n // Production: Load and cache module only via pre-calculated path.\n // This avoids potential path traversal vulnerabilities caused\n // by unexpected `route` values.\n module = MODULE_BY_ROUTE[route] = await import(`file://${module}`);\n } else if (module === undefined && NODE_ENV_IS_DEVELOPMENT) {\n // Only map module paths depending on `route` during development.\n const modulePath = join(CWD, \"dist\", `${route}.js`);\n if (typeof require === \"function\" && require.cache[modulePath]) {\n // Bun: Remove module from cache before importing\n // as query parameter for import is ignored.\n delete require.cache[modulePath];\n }\n // Use timestamp as query parameter to update modules.\n const mtime = (await stat(modulePath)).mtime.getTime();\n // Dynamic imports are restricted to development environments;\n // therefore, production-level path validation is not required here.\n module = await import(`file://${modulePath}?${mtime}`);\n }\n } catch (e) {\n switch (e.code) {\n case \"ENOENT\":\n case \"ENOTDIR\":\n case \"ERR_MODULE_NOT_FOUND\":\n continue;\n default:\n // Module exists, but fails to load.\n throw e;\n }\n }\n\n // Ensure module is a valid object before processing.\n if (!module || typeof module !== \"object\") {\n continue;\n }\n\n // Store current route in request.\n request.route = route;\n\n response =\n typeof module.default === \"function\"\n ? // Call functions with context as `this` and props as parameters,\n await module.default.call(context, props)\n : // otherwise return default export.\n module.default;\n\n if (reply.sent) {\n return;\n } else if (route.endsWith(\"/[404]\")) {\n // Preserve existing status if a 404 page is requested directly.\n // If no status is defined, set status to 404 automatically.\n if (reply.statusCode === 200 && !request.path.endsWith(\"/404\")) {\n reply.status(404);\n }\n break;\n } else if (typeof response === \"string\" || Buffer.isBuffer(response) || isJSX(response)) {\n break;\n } else if (\n route.endsWith(\"/[...guard]\") &&\n (response === undefined || typeof response === \"object\")\n ) {\n // Add object entries from guard to props\n Object.assign(props, response);\n continue;\n } else if (reply.statusCode === 404) {\n continue;\n } else {\n break;\n }\n }\n return await renderJSX(context, response);\n } catch (error) {\n const errorHandler = context[\"errorHandler\"];\n if (typeof errorHandler === \"function\") {\n reply.status(500);\n response = await errorHandler.call(context, error);\n return await renderJSX(context, response);\n } else {\n throw error;\n }\n }\n}\n\n/**\n * Generates all possible routes based on the given input path.\n *\n * Example routes for \"/a/b/c\":\n *\n * [\n * \"/[...guard]\",\"/a/[...guard]\",\"/a/b/[...guard]\",\"/a/b/c/[...guard]\",\n * \"/a/b/[c]\",\"/a/b/c/[index]\",\n * \"/a/b/c/[...path]\",\"/a/b/[...path]\",\"/a/[...path]\",\"/[...path]\",\n * \"/a/b/c/[404]\",\"/a/b/[404]\",\"/a/[404]\",\"/[404]\"\n * ]\n */\nfunction generateRoutes(path: string): string[] {\n const routes = [];\n\n // Transform given path into array of all its segments.\n // \"/a/b/c\" => [\"/a/b/c\", \"/a/b\", \"/a\", \"\"]\n const segments = [\"\"];\n let current = \"\";\n for (const segment of path.split(\"/\")) {\n // Ignore redundant slashes.\n if (segment !== \"\") {\n current += `/${segment}`;\n segments.push(current);\n }\n }\n segments.reverse();\n\n // [...guard]s are evaluated from top to bottom\n for (let i = segments.length - 1; i >= 0; i--) {\n routes.push(`${segments[i]}/[...guard]`);\n }\n\n // \"/a/b/c\" => [\"/a/b/[c]\", \"/a/b/c/[index]\"]\n const edgeSegment = segments[0];\n const lastSlash = edgeSegment.lastIndexOf(\"/\") + 1;\n if (lastSlash > 0) {\n routes.push(`${edgeSegment.substring(0, lastSlash)}[${edgeSegment.substring(lastSlash)}]`);\n }\n routes.push(`${edgeSegment}/[index]`);\n\n for (let i = 0; i < segments.length; i++) {\n routes.push(`${segments[i]}/[...path]`);\n }\n\n for (let i = 0; i < segments.length; i++) {\n routes.push(`${segments[i]}/[404]`);\n }\n\n return routes;\n}\n\n/**\n * Determines if a given object is a JSX element.\n */\nfunction isJSX(obj: unknown): boolean {\n return !!obj && typeof obj === \"object\" && \"type\" in obj && \"props\" in obj;\n}\n\n/**\n * Renders JSX to string and applies optional response handler.\n */\nasync function renderJSX(context: object, response: unknown) {\n const payload = isJSX(response) ? await jsxToString.call(context, response) : response;\n\n // Post-process the payload with an optional response handler\n const responseHandler = context[\"responseHandler\"];\n return typeof responseHandler === \"function\"\n ? await responseHandler.call(context, payload)\n : payload;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["fastify"]
|
|
7
7
|
}
|
package/serverless.ts
CHANGED
|
@@ -19,17 +19,19 @@ const CWD = process.cwd();
|
|
|
19
19
|
const CONFIG = (await import(`file://${join(CWD, "jeasx.config.js")}`)).default;
|
|
20
20
|
const NODE_ENV_IS_DEVELOPMENT = process.env.NODE_ENV === "development";
|
|
21
21
|
|
|
22
|
-
//
|
|
23
|
-
const MODULE_BY_ROUTE: Record<string, { default: Function }> = {};
|
|
22
|
+
// Mapping for route modules used in non-development environments.
|
|
23
|
+
const MODULE_BY_ROUTE: Record<string, { default: Function } | string> = {};
|
|
24
24
|
|
|
25
|
-
// Initialize the
|
|
25
|
+
// Initialize the mapping with absolute paths for all known modules.
|
|
26
26
|
// Modules are lazily loaded on their first request for a specific route.
|
|
27
|
-
// Only routes explicitly initialized with `null` will be loaded.
|
|
28
27
|
if (!NODE_ENV_IS_DEVELOPMENT) {
|
|
29
28
|
const { routes } = (await import(`file://${join(CWD, "dist", "[--metadata--].js")}`)).default as {
|
|
30
29
|
routes: string[];
|
|
31
30
|
};
|
|
32
|
-
|
|
31
|
+
// Map route identifiers to their absolute file system paths in the build directory.
|
|
32
|
+
for (const route of routes) {
|
|
33
|
+
MODULE_BY_ROUTE[route] = join(CWD, "dist", `${route}.js`);
|
|
34
|
+
}
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
declare module "fastify" {
|
|
@@ -107,60 +109,61 @@ async function handler(request: FastifyRequest, reply: FastifyReply) {
|
|
|
107
109
|
try {
|
|
108
110
|
// Execute route handlers for current request
|
|
109
111
|
for (const route of generateRoutes(request.path)) {
|
|
110
|
-
// Resolve module
|
|
112
|
+
// Resolve module or path to module
|
|
111
113
|
let module = MODULE_BY_ROUTE[route];
|
|
112
114
|
|
|
113
|
-
// Skip
|
|
114
|
-
// This avoids potential path traversal vulnerabilities caused
|
|
115
|
-
// by unexpected `route` values.
|
|
115
|
+
// Skip processing if the route path was not initialized.
|
|
116
116
|
if (module === undefined && !NODE_ENV_IS_DEVELOPMENT) {
|
|
117
117
|
continue;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
// Module was not loaded yet?
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
try {
|
|
122
|
+
if (typeof module === "string") {
|
|
123
|
+
// Production: Load and cache module only via pre-calculated path.
|
|
124
|
+
// This avoids potential path traversal vulnerabilities caused
|
|
125
|
+
// by unexpected `route` values.
|
|
126
|
+
module = MODULE_BY_ROUTE[route] = await import(`file://${module}`);
|
|
127
|
+
} else if (module === undefined && NODE_ENV_IS_DEVELOPMENT) {
|
|
128
|
+
// Only map module paths depending on `route` during development.
|
|
123
129
|
const modulePath = join(CWD, "dist", `${route}.js`);
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (require.cache[modulePath]) {
|
|
129
|
-
delete require.cache[modulePath];
|
|
130
|
-
}
|
|
131
|
-
module = await import(`file://${modulePath}`);
|
|
132
|
-
} else {
|
|
133
|
-
// Node: Use timestamp as query parameter to update modules.
|
|
134
|
-
const mtime = (await stat(modulePath)).mtime.getTime();
|
|
135
|
-
module = await import(`file://${modulePath}?${mtime}`);
|
|
136
|
-
}
|
|
137
|
-
} else {
|
|
138
|
-
// Load and cache module for non-development
|
|
139
|
-
module = await import(`file://${modulePath}`);
|
|
140
|
-
MODULE_BY_ROUTE[route] = module;
|
|
141
|
-
}
|
|
142
|
-
} catch (e) {
|
|
143
|
-
switch (e.code) {
|
|
144
|
-
case "ENOENT":
|
|
145
|
-
case "ENOTDIR":
|
|
146
|
-
case "ERR_MODULE_NOT_FOUND":
|
|
147
|
-
continue;
|
|
148
|
-
default:
|
|
149
|
-
// Module exists, but fails to load.
|
|
150
|
-
throw e;
|
|
130
|
+
if (typeof require === "function" && require.cache[modulePath]) {
|
|
131
|
+
// Bun: Remove module from cache before importing
|
|
132
|
+
// as query parameter for import is ignored.
|
|
133
|
+
delete require.cache[modulePath];
|
|
151
134
|
}
|
|
135
|
+
// Use timestamp as query parameter to update modules.
|
|
136
|
+
const mtime = (await stat(modulePath)).mtime.getTime();
|
|
137
|
+
// Dynamic imports are restricted to development environments;
|
|
138
|
+
// therefore, production-level path validation is not required here.
|
|
139
|
+
module = await import(`file://${modulePath}?${mtime}`);
|
|
140
|
+
}
|
|
141
|
+
} catch (e) {
|
|
142
|
+
switch (e.code) {
|
|
143
|
+
case "ENOENT":
|
|
144
|
+
case "ENOTDIR":
|
|
145
|
+
case "ERR_MODULE_NOT_FOUND":
|
|
146
|
+
continue;
|
|
147
|
+
default:
|
|
148
|
+
// Module exists, but fails to load.
|
|
149
|
+
throw e;
|
|
152
150
|
}
|
|
153
151
|
}
|
|
154
152
|
|
|
155
|
-
//
|
|
153
|
+
// Ensure module is a valid object before processing.
|
|
154
|
+
if (!module || typeof module !== "object") {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Store current route in request.
|
|
156
159
|
request.route = route;
|
|
157
160
|
|
|
158
|
-
// Call functions with 'this' context and props as parameters
|
|
159
|
-
// otherwise return default export
|
|
160
161
|
response =
|
|
161
162
|
typeof module.default === "function"
|
|
162
|
-
?
|
|
163
|
-
|
|
163
|
+
? // Call functions with context as `this` and props as parameters,
|
|
164
|
+
await module.default.call(context, props)
|
|
165
|
+
: // otherwise return default export.
|
|
166
|
+
module.default;
|
|
164
167
|
|
|
165
168
|
if (reply.sent) {
|
|
166
169
|
return;
|
|
@@ -218,9 +221,12 @@ function generateRoutes(path: string): string[] {
|
|
|
218
221
|
// "/a/b/c" => ["/a/b/c", "/a/b", "/a", ""]
|
|
219
222
|
const segments = [""];
|
|
220
223
|
let current = "";
|
|
221
|
-
for (const segment of path.split("/")
|
|
222
|
-
|
|
223
|
-
|
|
224
|
+
for (const segment of path.split("/")) {
|
|
225
|
+
// Ignore redundant slashes.
|
|
226
|
+
if (segment !== "") {
|
|
227
|
+
current += `/${segment}`;
|
|
228
|
+
segments.push(current);
|
|
229
|
+
}
|
|
224
230
|
}
|
|
225
231
|
segments.reverse();
|
|
226
232
|
|
/package/{server.js → start.js}
RENAMED
|
File without changes
|