elegance-js 2.1.7 → 2.1.10
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/dist/build.d.ts +2 -0
- package/dist/build.mjs +49 -60
- package/dist/client/client.mjs +36 -165
- package/dist/client/processPageElements.mjs +7 -7
- package/dist/client/render.mjs +1 -2
- package/dist/client/watcher.mjs +1 -2
- package/dist/compile_docs.mjs +8 -224
- package/dist/components/Link.mjs +3 -51
- package/dist/global.d.ts +4 -1
- package/dist/helpers/ObjectAttributeType.mjs +0 -1
- package/dist/helpers/camelToKebab.mjs +1 -2
- package/dist/index.mjs +3 -215
- package/dist/internal/deprecate.mjs +1 -2
- package/dist/log.mjs +2 -3
- package/dist/page_compiler.d.ts +1 -3
- package/dist/page_compiler.mjs +85 -780
- package/dist/server/generateHTMLTemplate.mjs +2 -194
- package/dist/server/layout.mjs +3 -4
- package/dist/server/loadHook.mjs +5 -11
- package/dist/server/observe.mjs +3 -3
- package/dist/server/render.mjs +3 -149
- package/dist/server/server.mjs +128 -1230
- package/dist/server/state.mjs +13 -34
- package/dist/shared/bindServerElements.mjs +1 -143
- package/dist/shared/serverElements.mjs +8 -9
- package/package.json +2 -2
package/dist/server/server.mjs
CHANGED
|
@@ -1,1184 +1,34 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
console.error(`Elegance.JS: ${getTimestamp()} ${color("[ERROR]:", 31)}`, ...args);
|
|
33
|
-
}
|
|
34
|
-
var log = {
|
|
35
|
-
info: logInfo,
|
|
36
|
-
warn: logWarn,
|
|
37
|
-
error: logError
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
// src/server/server.ts
|
|
41
|
-
import { gzip, deflate } from "zlib";
|
|
42
|
-
import { promisify } from "util";
|
|
43
|
-
|
|
44
|
-
// src/page_compiler.ts
|
|
45
|
-
import fs from "fs";
|
|
46
|
-
import path from "path";
|
|
47
|
-
import { registerLoader, setArcTsConfig } from "ts-arc";
|
|
48
|
-
import esbuild from "esbuild";
|
|
49
|
-
import { fileURLToPath } from "url";
|
|
50
|
-
|
|
51
|
-
// src/shared/serverElements.ts
|
|
52
|
-
var createBuildableElement = (tag) => {
|
|
53
|
-
return (options2, ...children) => ({
|
|
54
|
-
tag,
|
|
55
|
-
options: options2 || {},
|
|
56
|
-
children
|
|
57
|
-
});
|
|
58
|
-
};
|
|
59
|
-
var createChildrenlessBuildableElement = (tag) => {
|
|
60
|
-
return (options2) => ({
|
|
61
|
-
tag,
|
|
62
|
-
options: options2 || {},
|
|
63
|
-
children: null
|
|
64
|
-
});
|
|
65
|
-
};
|
|
66
|
-
var childrenlessElementTags = [
|
|
67
|
-
"area",
|
|
68
|
-
"base",
|
|
69
|
-
"br",
|
|
70
|
-
"col",
|
|
71
|
-
"embed",
|
|
72
|
-
"hr",
|
|
73
|
-
"img",
|
|
74
|
-
"input",
|
|
75
|
-
"link",
|
|
76
|
-
"meta",
|
|
77
|
-
"source",
|
|
78
|
-
"track",
|
|
79
|
-
"path",
|
|
80
|
-
"rect"
|
|
81
|
-
];
|
|
82
|
-
var elementTags = [
|
|
83
|
-
"a",
|
|
84
|
-
"address",
|
|
85
|
-
"article",
|
|
86
|
-
"aside",
|
|
87
|
-
"audio",
|
|
88
|
-
"blockquote",
|
|
89
|
-
"body",
|
|
90
|
-
"button",
|
|
91
|
-
"canvas",
|
|
92
|
-
"caption",
|
|
93
|
-
"colgroup",
|
|
94
|
-
"data",
|
|
95
|
-
"datalist",
|
|
96
|
-
"dd",
|
|
97
|
-
"del",
|
|
98
|
-
"details",
|
|
99
|
-
"dialog",
|
|
100
|
-
"div",
|
|
101
|
-
"dl",
|
|
102
|
-
"dt",
|
|
103
|
-
"fieldset",
|
|
104
|
-
"figcaption",
|
|
105
|
-
"figure",
|
|
106
|
-
"footer",
|
|
107
|
-
"form",
|
|
108
|
-
"h1",
|
|
109
|
-
"h2",
|
|
110
|
-
"h3",
|
|
111
|
-
"h4",
|
|
112
|
-
"h5",
|
|
113
|
-
"h6",
|
|
114
|
-
"head",
|
|
115
|
-
"header",
|
|
116
|
-
"hgroup",
|
|
117
|
-
"html",
|
|
118
|
-
"iframe",
|
|
119
|
-
"ins",
|
|
120
|
-
"label",
|
|
121
|
-
"legend",
|
|
122
|
-
"li",
|
|
123
|
-
"main",
|
|
124
|
-
"map",
|
|
125
|
-
"meter",
|
|
126
|
-
"nav",
|
|
127
|
-
"noscript",
|
|
128
|
-
"object",
|
|
129
|
-
"ol",
|
|
130
|
-
"optgroup",
|
|
131
|
-
"option",
|
|
132
|
-
"output",
|
|
133
|
-
"p",
|
|
134
|
-
"picture",
|
|
135
|
-
"pre",
|
|
136
|
-
"progress",
|
|
137
|
-
"q",
|
|
138
|
-
"section",
|
|
139
|
-
"select",
|
|
140
|
-
"summary",
|
|
141
|
-
"table",
|
|
142
|
-
"tbody",
|
|
143
|
-
"td",
|
|
144
|
-
"template",
|
|
145
|
-
"textarea",
|
|
146
|
-
"tfoot",
|
|
147
|
-
"th",
|
|
148
|
-
"thead",
|
|
149
|
-
"time",
|
|
150
|
-
"tr",
|
|
151
|
-
"ul",
|
|
152
|
-
"video",
|
|
153
|
-
"span",
|
|
154
|
-
"script",
|
|
155
|
-
"abbr",
|
|
156
|
-
"b",
|
|
157
|
-
"bdi",
|
|
158
|
-
"bdo",
|
|
159
|
-
"cite",
|
|
160
|
-
"code",
|
|
161
|
-
"dfn",
|
|
162
|
-
"em",
|
|
163
|
-
"i",
|
|
164
|
-
"kbd",
|
|
165
|
-
"mark",
|
|
166
|
-
"rp",
|
|
167
|
-
"rt",
|
|
168
|
-
"ruby",
|
|
169
|
-
"s",
|
|
170
|
-
"samp",
|
|
171
|
-
"small",
|
|
172
|
-
"strong",
|
|
173
|
-
"sub",
|
|
174
|
-
"sup",
|
|
175
|
-
"u",
|
|
176
|
-
"wbr",
|
|
177
|
-
"title",
|
|
178
|
-
"svg"
|
|
179
|
-
];
|
|
180
|
-
var elements = {};
|
|
181
|
-
var childrenlessElements = {};
|
|
182
|
-
for (const element of elementTags) {
|
|
183
|
-
elements[element] = createBuildableElement(element);
|
|
184
|
-
}
|
|
185
|
-
for (const element of childrenlessElementTags) {
|
|
186
|
-
childrenlessElements[element] = createChildrenlessBuildableElement(element);
|
|
187
|
-
}
|
|
188
|
-
var allElements = {
|
|
189
|
-
...elements,
|
|
190
|
-
...childrenlessElements
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
// src/shared/bindServerElements.ts
|
|
194
|
-
Object.assign(globalThis, elements);
|
|
195
|
-
Object.assign(globalThis, childrenlessElements);
|
|
196
|
-
|
|
197
|
-
// src/server/render.ts
|
|
198
|
-
var renderRecursively = (element) => {
|
|
199
|
-
let returnString = "";
|
|
200
|
-
if (typeof element === "boolean") return returnString;
|
|
201
|
-
else if (typeof element === "number" || typeof element === "string") {
|
|
202
|
-
return returnString + element;
|
|
203
|
-
} else if (Array.isArray(element)) {
|
|
204
|
-
return returnString + element.join(", ");
|
|
205
|
-
}
|
|
206
|
-
returnString += `<${element.tag}`;
|
|
207
|
-
if (typeof element.options === "object") {
|
|
208
|
-
const {
|
|
209
|
-
tag: elementTag,
|
|
210
|
-
options: elementOptions,
|
|
211
|
-
children: elementChildren
|
|
212
|
-
} = element.options;
|
|
213
|
-
if (elementTag !== void 0 && elementOptions !== void 0 && elementChildren !== void 0) {
|
|
214
|
-
const children = element.children;
|
|
215
|
-
element.children = [
|
|
216
|
-
element.options,
|
|
217
|
-
...children
|
|
218
|
-
];
|
|
219
|
-
element.options = {};
|
|
220
|
-
} else {
|
|
221
|
-
for (const [attrName, attrValue] of Object.entries(element.options)) {
|
|
222
|
-
if (typeof attrValue === "object") {
|
|
223
|
-
throw `Attr ${attrName}, for element ${element.tag} has obj type. Got: ${JSON.stringify(element, null, 2)}`;
|
|
224
|
-
}
|
|
225
|
-
returnString += ` ${attrName.toLowerCase()}="${attrValue}"`;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
} else if (typeof element.options !== "object" && element.options !== void 0) {
|
|
229
|
-
element.children = [element.options, ...element.children || []];
|
|
230
|
-
}
|
|
231
|
-
if (element.children === null) {
|
|
232
|
-
returnString += "/>";
|
|
233
|
-
return returnString;
|
|
234
|
-
}
|
|
235
|
-
returnString += ">";
|
|
236
|
-
for (const child of element.children) {
|
|
237
|
-
returnString += renderRecursively(child);
|
|
238
|
-
}
|
|
239
|
-
returnString += `</${element.tag}>`;
|
|
240
|
-
return returnString;
|
|
241
|
-
};
|
|
242
|
-
var serverSideRenderPage = async (page, pathname) => {
|
|
243
|
-
if (!page) {
|
|
244
|
-
throw `No Page Provided.`;
|
|
245
|
-
}
|
|
246
|
-
if (typeof page === "function") {
|
|
247
|
-
throw `Unbuilt page provided to ssr page.`;
|
|
248
|
-
}
|
|
249
|
-
const bodyHTML = renderRecursively(page);
|
|
250
|
-
return {
|
|
251
|
-
bodyHTML
|
|
252
|
-
};
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
// src/server/generateHTMLTemplate.ts
|
|
256
|
-
var generateHTMLTemplate = async ({
|
|
257
|
-
pageURL,
|
|
258
|
-
head: head2,
|
|
259
|
-
serverData = null,
|
|
260
|
-
addPageScriptTag = true,
|
|
261
|
-
name,
|
|
262
|
-
requiredClientModules = {},
|
|
263
|
-
environment
|
|
264
|
-
}) => {
|
|
265
|
-
let StartTemplate = `<meta name="viewport" content="width=device-width, initial-scale=1.0">`;
|
|
266
|
-
if (environment === "production") {
|
|
267
|
-
StartTemplate += `<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">`;
|
|
268
|
-
}
|
|
269
|
-
StartTemplate += '<meta charset="UTF-8">';
|
|
270
|
-
for (const [globalName] of Object.entries(requiredClientModules)) {
|
|
271
|
-
StartTemplate += `<script data-module="true" src="/shipped/${globalName}.js" defer="true"></script>`;
|
|
272
|
-
}
|
|
273
|
-
if (addPageScriptTag === true) {
|
|
274
|
-
const sanitized = pageURL === "" ? "/" : `/${pageURL}`;
|
|
275
|
-
StartTemplate += `<script data-page="true" type="module" data-pathname="${sanitized}" src="${sanitized.endsWith("/") ? sanitized : sanitized + "/"}${name}_data.js" defer="true"></script>`;
|
|
276
|
-
}
|
|
277
|
-
StartTemplate += `<script type="module" src="/client.js" defer="true"></script>`;
|
|
278
|
-
let builtHead;
|
|
279
|
-
if (head2.constructor.name === "AsyncFunction") {
|
|
280
|
-
builtHead = await head2();
|
|
281
|
-
} else {
|
|
282
|
-
builtHead = head2();
|
|
283
|
-
}
|
|
284
|
-
let HTMLTemplate = renderRecursively(builtHead);
|
|
285
|
-
if (serverData) {
|
|
286
|
-
HTMLTemplate += serverData;
|
|
287
|
-
}
|
|
288
|
-
return {
|
|
289
|
-
internals: StartTemplate,
|
|
290
|
-
builtMetadata: HTMLTemplate
|
|
291
|
-
};
|
|
292
|
-
};
|
|
293
|
-
|
|
294
|
-
// src/server/loadHook.ts
|
|
295
|
-
var resetLoadHooks = () => globalThis.__SERVER_CURRENT_LOADHOOKS__ = [];
|
|
296
|
-
var getLoadHooks = () => globalThis.__SERVER_CURRENT_LOADHOOKS__;
|
|
297
|
-
|
|
298
|
-
// src/server/state.ts
|
|
299
|
-
if (!globalThis.__SERVER_CURRENT_STATE_ID__) {
|
|
300
|
-
globalThis.__SERVER_CURRENT_STATE_ID__ = 1;
|
|
301
|
-
}
|
|
302
|
-
var initializeState = () => globalThis.__SERVER_CURRENT_STATE__ = [];
|
|
303
|
-
var getState = () => {
|
|
304
|
-
return globalThis.__SERVER_CURRENT_STATE__;
|
|
305
|
-
};
|
|
306
|
-
var initializeObjectAttributes = () => globalThis.__SERVER_CURRENT_OBJECT_ATTRIBUTES__ = [];
|
|
307
|
-
var getObjectAttributes = () => {
|
|
308
|
-
return globalThis.__SERVER_CURRENT_OBJECT_ATTRIBUTES__;
|
|
309
|
-
};
|
|
310
|
-
|
|
311
|
-
// src/server/layout.ts
|
|
312
|
-
var resetLayouts = () => globalThis.__SERVER_CURRENT_LAYOUTS__ = /* @__PURE__ */ new Map();
|
|
313
|
-
if (!globalThis.__SERVER_CURRENT_LAYOUT_ID__) globalThis.__SERVER_CURRENT_LAYOUT_ID__ = 1;
|
|
314
|
-
|
|
315
|
-
// src/page_compiler.ts
|
|
316
|
-
var __filename = fileURLToPath(import.meta.url);
|
|
317
|
-
var __dirname = path.dirname(__filename);
|
|
318
|
-
setArcTsConfig(__dirname);
|
|
319
|
-
registerLoader();
|
|
320
|
-
var packageDir = process.env.PACKAGE_PATH;
|
|
321
|
-
if (packageDir === void 0) {
|
|
322
|
-
packageDir = path.resolve(__dirname, "..");
|
|
323
|
-
}
|
|
324
|
-
var clientPath = path.resolve(packageDir, "./dist/client/client.mjs");
|
|
325
|
-
var watcherPath = path.resolve(packageDir, "./dist/client/watcher.mjs");
|
|
326
|
-
var shippedModules = /* @__PURE__ */ new Map();
|
|
327
|
-
var modulesToShip = [];
|
|
328
|
-
var yellow = (text) => {
|
|
329
|
-
return `\x1B[38;2;238;184;68m${text}`;
|
|
330
|
-
};
|
|
331
|
-
var black = (text) => {
|
|
332
|
-
return `\x1B[38;2;0;0;0m${text}`;
|
|
333
|
-
};
|
|
334
|
-
var bgYellow = (text) => {
|
|
335
|
-
return `\x1B[48;2;238;184;68m${text}`;
|
|
336
|
-
};
|
|
337
|
-
var bold = (text) => {
|
|
338
|
-
return `\x1B[1m${text}`;
|
|
339
|
-
};
|
|
340
|
-
var underline = (text) => {
|
|
341
|
-
return `\x1B[4m${text}`;
|
|
342
|
-
};
|
|
343
|
-
var white = (text) => {
|
|
344
|
-
return `\x1B[38;2;255;247;229m${text}`;
|
|
345
|
-
};
|
|
346
|
-
var log2 = (...text) => {
|
|
347
|
-
if (options.quiet) return;
|
|
348
|
-
return console.log(text.map((text2) => `${text2}\x1B[0m`).join(""));
|
|
349
|
-
};
|
|
350
|
-
var options = JSON.parse(process.env.OPTIONS);
|
|
351
|
-
var DIST_DIR = process.env.DIST_DIR;
|
|
352
|
-
var PAGE_MAP = /* @__PURE__ */ new Map();
|
|
353
|
-
var LAYOUT_MAP = /* @__PURE__ */ new Map();
|
|
354
|
-
var getAllSubdirectories = (dir, baseDir = dir) => {
|
|
355
|
-
let directories = [];
|
|
356
|
-
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
357
|
-
for (const item of items) {
|
|
358
|
-
if (item.isDirectory()) {
|
|
359
|
-
const fullPath = path.join(dir, item.name);
|
|
360
|
-
const relativePath = path.relative(baseDir, fullPath);
|
|
361
|
-
directories.push(relativePath);
|
|
362
|
-
directories = directories.concat(getAllSubdirectories(fullPath, baseDir));
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
return directories;
|
|
366
|
-
};
|
|
367
|
-
var buildClient = async (DIST_DIR2) => {
|
|
368
|
-
let clientString = "window.__name = (func) => func; ";
|
|
369
|
-
clientString += fs.readFileSync(clientPath, "utf-8");
|
|
370
|
-
if (options.hotReload !== void 0) {
|
|
371
|
-
clientString += `const watchServerPort = ${options.hotReload.port}`;
|
|
372
|
-
clientString += fs.readFileSync(watcherPath, "utf-8");
|
|
373
|
-
}
|
|
374
|
-
const transformedClient = await esbuild.transform(clientString, {
|
|
375
|
-
minify: options.environment === "production",
|
|
376
|
-
drop: options.environment === "production" ? ["console", "debugger"] : void 0,
|
|
377
|
-
keepNames: false,
|
|
378
|
-
format: "iife",
|
|
379
|
-
platform: "node",
|
|
380
|
-
loader: "ts"
|
|
381
|
-
});
|
|
382
|
-
fs.writeFileSync(
|
|
383
|
-
path.join(DIST_DIR2, "/client.js"),
|
|
384
|
-
transformedClient.code
|
|
385
|
-
);
|
|
386
|
-
};
|
|
387
|
-
var elementKey = 0;
|
|
388
|
-
var processOptionAsObjectAttribute = (element, optionName, optionValue, objectAttributes) => {
|
|
389
|
-
const lcOptionName = optionName.toLowerCase();
|
|
390
|
-
const options2 = element.options;
|
|
391
|
-
let key = options2.key;
|
|
392
|
-
if (key == void 0) {
|
|
393
|
-
key = elementKey += 1;
|
|
394
|
-
options2.key = key;
|
|
395
|
-
}
|
|
396
|
-
if (!optionValue.type) {
|
|
397
|
-
throw `ObjectAttributeType is missing from object attribute. ${element.tag}: ${optionName}/${optionValue}`;
|
|
398
|
-
}
|
|
399
|
-
let optionFinal = lcOptionName;
|
|
400
|
-
switch (optionValue.type) {
|
|
401
|
-
case 1 /* STATE */:
|
|
402
|
-
const SOA = optionValue;
|
|
403
|
-
if (typeof SOA.value === "function") {
|
|
404
|
-
delete options2[optionName];
|
|
405
|
-
break;
|
|
406
|
-
}
|
|
407
|
-
if (lcOptionName === "innertext" || lcOptionName === "innerhtml") {
|
|
408
|
-
element.children = [SOA.value];
|
|
409
|
-
delete options2[optionName];
|
|
410
|
-
} else {
|
|
411
|
-
delete options2[optionName];
|
|
412
|
-
options2[lcOptionName] = SOA.value;
|
|
413
|
-
}
|
|
414
|
-
break;
|
|
415
|
-
case 2 /* OBSERVER */:
|
|
416
|
-
const OOA = optionValue;
|
|
417
|
-
const firstValue = OOA.update(...OOA.initialValues);
|
|
418
|
-
if (lcOptionName === "innertext" || lcOptionName === "innerhtml") {
|
|
419
|
-
element.children = [firstValue];
|
|
420
|
-
delete options2[optionName];
|
|
421
|
-
} else {
|
|
422
|
-
delete options2[optionName];
|
|
423
|
-
options2[lcOptionName] = firstValue;
|
|
424
|
-
}
|
|
425
|
-
optionFinal = optionName;
|
|
426
|
-
break;
|
|
427
|
-
case 4 /* REFERENCE */:
|
|
428
|
-
options2["ref"] = optionValue.value;
|
|
429
|
-
break;
|
|
430
|
-
}
|
|
431
|
-
objectAttributes.push({ ...optionValue, key, attribute: optionFinal });
|
|
432
|
-
};
|
|
433
|
-
function buildTrace(stack, indent = 4) {
|
|
434
|
-
try {
|
|
435
|
-
if (!stack || stack.length === 0) return "[]";
|
|
436
|
-
let traceObj = stack[stack.length - 1] && typeof stack[stack.length - 1] === "object" ? JSON.parse(JSON.stringify(stack[stack.length - 1])) : { value: stack[stack.length - 1] };
|
|
437
|
-
traceObj._error = "This is the element where the error occurred";
|
|
438
|
-
for (let i = stack.length - 2; i >= 0; i--) {
|
|
439
|
-
const parent = stack[i];
|
|
440
|
-
const child = stack[i + 1];
|
|
441
|
-
if (!parent || typeof parent !== "object") {
|
|
442
|
-
traceObj = { value: parent, _errorChild: traceObj };
|
|
443
|
-
continue;
|
|
444
|
-
}
|
|
445
|
-
let parentClone;
|
|
446
|
-
try {
|
|
447
|
-
parentClone = JSON.parse(JSON.stringify(parent));
|
|
448
|
-
} catch {
|
|
449
|
-
parentClone = { value: parent };
|
|
450
|
-
}
|
|
451
|
-
let index = -1;
|
|
452
|
-
if (Array.isArray(parentClone.children)) {
|
|
453
|
-
index = parentClone.children.findIndex((c) => c === child);
|
|
454
|
-
}
|
|
455
|
-
if (index !== -1 && parentClone.children) {
|
|
456
|
-
parentClone.children = parentClone.children.slice(0, index + 1);
|
|
457
|
-
parentClone.children[index] = traceObj;
|
|
458
|
-
} else {
|
|
459
|
-
parentClone._errorChild = traceObj;
|
|
460
|
-
}
|
|
461
|
-
traceObj = parentClone;
|
|
462
|
-
}
|
|
463
|
-
return JSON.stringify(traceObj, null, indent).replace(/^/gm, " ".repeat(indent));
|
|
464
|
-
} catch {
|
|
465
|
-
return "Could not build stack-trace.";
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
var processPageElements = (element, objectAttributes, recursionLevel, stack = []) => {
|
|
469
|
-
stack.push(element);
|
|
470
|
-
try {
|
|
471
|
-
if (typeof element === "boolean" || typeof element === "number" || Array.isArray(element)) {
|
|
472
|
-
stack.pop();
|
|
473
|
-
return element;
|
|
474
|
-
}
|
|
475
|
-
if (typeof element === "string") {
|
|
476
|
-
stack.pop();
|
|
477
|
-
return element;
|
|
478
|
-
}
|
|
479
|
-
const processElementOptionsAsChildAndReturn = () => {
|
|
480
|
-
try {
|
|
481
|
-
const children = element.children;
|
|
482
|
-
element.children = [
|
|
483
|
-
element.options,
|
|
484
|
-
...children
|
|
485
|
-
];
|
|
486
|
-
element.options = {};
|
|
487
|
-
for (let i = 0; i < children.length + 1; i++) {
|
|
488
|
-
const child = element.children[i];
|
|
489
|
-
const processedChild = processPageElements(child, objectAttributes, recursionLevel + 1, stack);
|
|
490
|
-
element.children[i] = processedChild;
|
|
491
|
-
}
|
|
492
|
-
return {
|
|
493
|
-
...element,
|
|
494
|
-
options: {}
|
|
495
|
-
};
|
|
496
|
-
} catch (e) {
|
|
497
|
-
const errorString = `Could not process element options as a child. ${e}.`;
|
|
498
|
-
throw new Error(errorString);
|
|
499
|
-
}
|
|
500
|
-
};
|
|
501
|
-
if (typeof element.options !== "object") {
|
|
502
|
-
const result = processElementOptionsAsChildAndReturn();
|
|
503
|
-
stack.pop();
|
|
504
|
-
return result;
|
|
505
|
-
}
|
|
506
|
-
const {
|
|
507
|
-
tag: elementTag,
|
|
508
|
-
options: elementOptions,
|
|
509
|
-
children: elementChildren
|
|
510
|
-
} = element.options;
|
|
511
|
-
if (elementTag && elementOptions && elementChildren) {
|
|
512
|
-
const result = processElementOptionsAsChildAndReturn();
|
|
513
|
-
stack.pop();
|
|
514
|
-
return result;
|
|
515
|
-
}
|
|
516
|
-
const options2 = element.options;
|
|
517
|
-
for (const [optionName, optionValue] of Object.entries(options2)) {
|
|
518
|
-
const lcOptionName = optionName.toLowerCase();
|
|
519
|
-
if (typeof optionValue !== "object") {
|
|
520
|
-
if (lcOptionName === "innertext") {
|
|
521
|
-
delete options2[optionName];
|
|
522
|
-
if (element.children === null) {
|
|
523
|
-
throw `Cannot use innerText or innerHTML on childrenless elements.`;
|
|
524
|
-
}
|
|
525
|
-
element.children = [optionValue, ...element.children];
|
|
526
|
-
continue;
|
|
527
|
-
} else if (lcOptionName === "innerhtml") {
|
|
528
|
-
if (element.children === null) {
|
|
529
|
-
throw `Cannot use innerText or innerHTML on childrenless elements.`;
|
|
530
|
-
}
|
|
531
|
-
delete options2[optionName];
|
|
532
|
-
element.children = [optionValue];
|
|
533
|
-
continue;
|
|
534
|
-
}
|
|
535
|
-
continue;
|
|
536
|
-
}
|
|
537
|
-
;
|
|
538
|
-
processOptionAsObjectAttribute(element, optionName, optionValue, objectAttributes);
|
|
539
|
-
}
|
|
540
|
-
if (element.children) {
|
|
541
|
-
for (let i = 0; i < element.children.length; i++) {
|
|
542
|
-
const child = element.children[i];
|
|
543
|
-
const processedChild = processPageElements(child, objectAttributes, recursionLevel + 1, stack);
|
|
544
|
-
element.children[i] = processedChild;
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
stack.pop();
|
|
548
|
-
return element;
|
|
549
|
-
} catch (e) {
|
|
550
|
-
const trace = buildTrace(stack);
|
|
551
|
-
if (recursionLevel === 0) {
|
|
552
|
-
throw new Error(`${e}
|
|
553
|
-
|
|
554
|
-
Trace:
|
|
555
|
-
${trace}`);
|
|
556
|
-
} else {
|
|
557
|
-
throw e;
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
};
|
|
561
|
-
var pageToHTML = async (pageLocation, pageElements, metadata, DIST_DIR2, pageName, doWrite = true, requiredClientModules = {}, layout, pathname = "") => {
|
|
562
|
-
if (typeof pageElements === "string" || typeof pageElements === "boolean" || typeof pageElements === "number" || Array.isArray(pageElements)) {
|
|
563
|
-
throw new Error(`The root element of a page / layout must be a built element, not just a Child. Received: ${typeof pageElements}.`);
|
|
564
|
-
}
|
|
565
|
-
const objectAttributes = [];
|
|
566
|
-
const stack = [];
|
|
567
|
-
const processedPageElements = processPageElements(pageElements, objectAttributes, 0, stack);
|
|
568
|
-
const renderedPage = await serverSideRenderPage(
|
|
569
|
-
processedPageElements,
|
|
570
|
-
pageLocation
|
|
571
|
-
);
|
|
572
|
-
const { internals, builtMetadata } = await generateHTMLTemplate({
|
|
573
|
-
pageURL: pathname,
|
|
574
|
-
head: metadata,
|
|
575
|
-
addPageScriptTag: doWrite,
|
|
576
|
-
name: pageName,
|
|
577
|
-
requiredClientModules,
|
|
578
|
-
environment: options.environment
|
|
579
|
-
});
|
|
580
|
-
let extraBodyHTML = "";
|
|
581
|
-
if (doWrite === false) {
|
|
582
|
-
const state = getState();
|
|
583
|
-
const pageLoadHooks = getLoadHooks();
|
|
584
|
-
const userObjectAttributes = getObjectAttributes();
|
|
585
|
-
const {
|
|
586
|
-
result
|
|
587
|
-
} = await generateClientPageData(
|
|
588
|
-
pathname,
|
|
589
|
-
state || {},
|
|
590
|
-
[...objectAttributes, ...userObjectAttributes],
|
|
591
|
-
pageLoadHooks || [],
|
|
592
|
-
DIST_DIR2,
|
|
593
|
-
"page",
|
|
594
|
-
"pd",
|
|
595
|
-
false
|
|
596
|
-
);
|
|
597
|
-
const sanitized = pathname === "" ? "/" : `/${pathname}`;
|
|
598
|
-
extraBodyHTML = `<script data-hook="true" data-pathname="${sanitized}" type="text/plain">${result}</script>`;
|
|
599
|
-
extraBodyHTML += `<script>
|
|
600
|
-
const text = document.querySelector('[data-hook="true"][data-pathname="${sanitized}"][type="text/plain"').textContent;
|
|
601
|
-
const blob = new Blob([text], { type: 'text/javascript' });
|
|
602
|
-
const url = URL.createObjectURL(blob);
|
|
603
|
-
|
|
604
|
-
const script = document.createElement("script");
|
|
605
|
-
script.src = url;
|
|
606
|
-
script.type = "module";
|
|
607
|
-
script.setAttribute("data-page", "true");
|
|
608
|
-
script.setAttribute("data-pathname", "${sanitized}");
|
|
609
|
-
|
|
610
|
-
document.head.appendChild(script);
|
|
611
|
-
|
|
612
|
-
document.currentScript.remove();
|
|
613
|
-
</script>`;
|
|
614
|
-
extraBodyHTML = extraBodyHTML.replace(/\s+/g, " ").replace(/\s*([{}();,:])\s*/g, "$1").trim();
|
|
615
|
-
}
|
|
616
|
-
const headHTML = `<!DOCTYPE html>${layout.metadata.startHTML}${layout.scriptTag}${internals}${builtMetadata}${layout.metadata.endHTML}`;
|
|
617
|
-
const bodyHTML = `${layout.pageContent.startHTML}${renderedPage.bodyHTML}${extraBodyHTML}${layout.pageContent.endHTML}`;
|
|
618
|
-
const resultHTML = `${headHTML}${bodyHTML}`;
|
|
619
|
-
const htmlLocation = path.join(pageLocation, (pageName === "page" ? "index" : pageName) + ".html");
|
|
620
|
-
if (doWrite) {
|
|
621
|
-
const dirname2 = path.dirname(htmlLocation);
|
|
622
|
-
if (fs.existsSync(dirname2) === false) {
|
|
623
|
-
fs.mkdirSync(dirname2, { recursive: true });
|
|
624
|
-
}
|
|
625
|
-
fs.writeFileSync(
|
|
626
|
-
htmlLocation,
|
|
627
|
-
resultHTML,
|
|
628
|
-
{
|
|
629
|
-
encoding: "utf-8",
|
|
630
|
-
flag: "w"
|
|
631
|
-
}
|
|
632
|
-
);
|
|
633
|
-
return objectAttributes;
|
|
634
|
-
}
|
|
635
|
-
return resultHTML;
|
|
636
|
-
};
|
|
637
|
-
var generateClientPageData = async (pageLocation, state, objectAttributes, pageLoadHooks, DIST_DIR2, pageName, globalVariableName = "pd", write = true) => {
|
|
638
|
-
let clientPageJSText = "";
|
|
639
|
-
{
|
|
640
|
-
clientPageJSText += `${globalThis.__SERVER_PAGE_DATA_BANNER__}`;
|
|
641
|
-
}
|
|
642
|
-
{
|
|
643
|
-
clientPageJSText += `export const data = {`;
|
|
644
|
-
if (state) {
|
|
645
|
-
clientPageJSText += `state:[`;
|
|
646
|
-
for (const subject of state) {
|
|
647
|
-
if (typeof subject.value === "string") {
|
|
648
|
-
const stringified = JSON.stringify(subject.value);
|
|
649
|
-
clientPageJSText += `{id:${subject.id},value:${stringified}},`;
|
|
650
|
-
} else if (typeof subject.value === "function") {
|
|
651
|
-
clientPageJSText += `{id:${subject.id},value:${subject.value.toString()}},`;
|
|
652
|
-
} else {
|
|
653
|
-
clientPageJSText += `{id:${subject.id},value:${JSON.stringify(subject.value)}},`;
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
clientPageJSText += `],`;
|
|
657
|
-
}
|
|
658
|
-
const stateObjectAttributes = objectAttributes.filter((oa) => oa.type === 1 /* STATE */);
|
|
659
|
-
if (stateObjectAttributes.length > 0) {
|
|
660
|
-
const processed = [...stateObjectAttributes].map((soa) => {
|
|
661
|
-
delete soa.type;
|
|
662
|
-
return soa;
|
|
663
|
-
});
|
|
664
|
-
clientPageJSText += `soa:${JSON.stringify(processed)},`;
|
|
665
|
-
}
|
|
666
|
-
const observerObjectAttributes = objectAttributes.filter((oa) => oa.type === 2 /* OBSERVER */);
|
|
667
|
-
if (observerObjectAttributes.length > 0) {
|
|
668
|
-
let observerObjectAttributeString = "ooa:[";
|
|
669
|
-
for (const observerObjectAttribute of observerObjectAttributes) {
|
|
670
|
-
const ooa = observerObjectAttribute;
|
|
671
|
-
observerObjectAttributeString += `{key:${ooa.key},attribute:"${ooa.attribute}",update:${ooa.update.toString()},`;
|
|
672
|
-
observerObjectAttributeString += `refs:[`;
|
|
673
|
-
for (const ref of ooa.refs) {
|
|
674
|
-
observerObjectAttributeString += `{id:${ref.id}},`;
|
|
675
|
-
}
|
|
676
|
-
observerObjectAttributeString += "]},";
|
|
677
|
-
}
|
|
678
|
-
observerObjectAttributeString += "],";
|
|
679
|
-
clientPageJSText += observerObjectAttributeString;
|
|
680
|
-
}
|
|
681
|
-
if (pageLoadHooks.length > 0) {
|
|
682
|
-
clientPageJSText += "lh:[";
|
|
683
|
-
for (const loadHook2 of pageLoadHooks) {
|
|
684
|
-
clientPageJSText += `{fn:${loadHook2.fn}},`;
|
|
685
|
-
}
|
|
686
|
-
clientPageJSText += "],";
|
|
687
|
-
}
|
|
688
|
-
clientPageJSText += `};`;
|
|
689
|
-
}
|
|
690
|
-
const pageDataPath = path.join(DIST_DIR2, pageLocation, `${pageName}_data.js`);
|
|
691
|
-
let sendHardReloadInstruction = false;
|
|
692
|
-
const transformedResult = await esbuild.transform(clientPageJSText, { minify: options.environment === "production" }).catch((error) => {
|
|
693
|
-
console.error("Failed to transform client page js!", error);
|
|
694
|
-
});
|
|
695
|
-
if (!transformedResult) return { sendHardReloadInstruction };
|
|
696
|
-
if (fs.existsSync(pageDataPath)) {
|
|
697
|
-
const content = fs.readFileSync(pageDataPath).toString();
|
|
698
|
-
if (content !== transformedResult.code) {
|
|
699
|
-
sendHardReloadInstruction = true;
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
if (write) fs.writeFileSync(pageDataPath, transformedResult.code, "utf-8");
|
|
703
|
-
return { sendHardReloadInstruction, result: transformedResult.code };
|
|
704
|
-
};
|
|
705
|
-
var generateLayout = async (DIST_DIR2, filePath, directory, childIndicator, generateDynamic = false) => {
|
|
706
|
-
initializeState();
|
|
707
|
-
initializeObjectAttributes();
|
|
708
|
-
resetLoadHooks();
|
|
709
|
-
globalThis.__SERVER_PAGE_DATA_BANNER__ = "";
|
|
710
|
-
let layoutElements;
|
|
711
|
-
let metadataElements;
|
|
712
|
-
let modules = [];
|
|
713
|
-
let isDynamicLayout = false;
|
|
714
|
-
try {
|
|
715
|
-
const {
|
|
716
|
-
layout,
|
|
717
|
-
metadata,
|
|
718
|
-
isDynamic,
|
|
719
|
-
shippedModules: shippedModules2
|
|
720
|
-
} = await import("file://" + filePath);
|
|
721
|
-
if (shippedModules2 !== void 0) {
|
|
722
|
-
modules = shippedModules2;
|
|
723
|
-
}
|
|
724
|
-
layoutElements = layout;
|
|
725
|
-
metadataElements = metadata;
|
|
726
|
-
if (isDynamic === true) {
|
|
727
|
-
isDynamicLayout = isDynamic;
|
|
728
|
-
}
|
|
729
|
-
} catch (e) {
|
|
730
|
-
throw new Error(`Error in Page: ${directory === "" ? "/" : directory}layout.ts - ${e}`);
|
|
731
|
-
}
|
|
732
|
-
LAYOUT_MAP.set(directory === "" ? "/" : `/${directory}`, {
|
|
733
|
-
isDynamic: isDynamicLayout,
|
|
734
|
-
filePath
|
|
735
|
-
});
|
|
736
|
-
if (isDynamicLayout === true && generateDynamic === false) return false;
|
|
737
|
-
{
|
|
738
|
-
if (!layoutElements) {
|
|
739
|
-
throw new Error(`WARNING: ${filePath} should export a const layout, which is of type Layout: (child: Child) => AnyBuiltElement.`);
|
|
740
|
-
}
|
|
741
|
-
if (typeof layoutElements === "function") {
|
|
742
|
-
if (layoutElements.constructor.name === "AsyncFunction") {
|
|
743
|
-
layoutElements = await layoutElements(childIndicator);
|
|
744
|
-
} else {
|
|
745
|
-
layoutElements = layoutElements(childIndicator);
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
{
|
|
750
|
-
if (!metadataElements) {
|
|
751
|
-
throw new Error(`WARNING: ${filePath} should export a const metadata, which is of type LayoutMetadata: (child: Child) => AnyBuiltElement.`);
|
|
752
|
-
}
|
|
753
|
-
if (typeof metadataElements === "function") {
|
|
754
|
-
if (metadataElements.constructor.name === "AsyncFunction") {
|
|
755
|
-
metadataElements = await metadataElements(childIndicator);
|
|
756
|
-
} else {
|
|
757
|
-
metadataElements = metadataElements(childIndicator);
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
const state = getState();
|
|
762
|
-
const pageLoadHooks = getLoadHooks();
|
|
763
|
-
const objectAttributes = getObjectAttributes();
|
|
764
|
-
if (typeof layoutElements === "string" || typeof layoutElements === "boolean" || typeof layoutElements === "number" || Array.isArray(layoutElements)) {
|
|
765
|
-
throw new Error(`The root element of a page / layout must be a built element, not just a Child. Received: ${typeof layoutElements}.`);
|
|
766
|
-
}
|
|
767
|
-
const foundObjectAttributes = [];
|
|
768
|
-
const stack = [];
|
|
769
|
-
const processedPageElements = processPageElements(layoutElements, foundObjectAttributes, 0, stack);
|
|
770
|
-
const renderedPage = await serverSideRenderPage(
|
|
771
|
-
processedPageElements,
|
|
772
|
-
directory
|
|
773
|
-
);
|
|
774
|
-
const metadataHTML = metadataElements ? renderRecursively(metadataElements) : "";
|
|
775
|
-
await generateClientPageData(
|
|
776
|
-
directory,
|
|
777
|
-
state || {},
|
|
778
|
-
[...objectAttributes, ...foundObjectAttributes],
|
|
779
|
-
pageLoadHooks || [],
|
|
780
|
-
DIST_DIR2,
|
|
781
|
-
"layout",
|
|
782
|
-
"ld"
|
|
783
|
-
);
|
|
784
|
-
return { pageContentHTML: renderedPage.bodyHTML, metadataHTML };
|
|
785
|
-
};
|
|
786
|
-
var builtLayouts = /* @__PURE__ */ new Map();
|
|
787
|
-
var buildLayouts = async () => {
|
|
788
|
-
const pagesDirectory = path.resolve(options.pagesDirectory);
|
|
789
|
-
const subdirectories = [...getAllSubdirectories(pagesDirectory), ""];
|
|
790
|
-
let shouldClientHardReload = false;
|
|
791
|
-
for (const directory of subdirectories) {
|
|
792
|
-
const abs = path.resolve(path.join(pagesDirectory, directory));
|
|
793
|
-
const files = fs.readdirSync(abs, { withFileTypes: true }).filter((f) => f.name.endsWith(".ts"));
|
|
794
|
-
for (const file of files) {
|
|
795
|
-
const filePath = path.join(file.parentPath, file.name);
|
|
796
|
-
const name = file.name.slice(0, file.name.length - 3);
|
|
797
|
-
const isLayout = name === "layout";
|
|
798
|
-
if (isLayout == false) {
|
|
799
|
-
continue;
|
|
800
|
-
}
|
|
801
|
-
try {
|
|
802
|
-
const builtLayout = await buildLayout(filePath, directory);
|
|
803
|
-
if (!builtLayout) return { shouldClientHardReload: false };
|
|
804
|
-
builtLayouts.set(filePath, builtLayout);
|
|
805
|
-
} catch (e) {
|
|
806
|
-
console.error(e);
|
|
807
|
-
continue;
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
return { shouldClientHardReload };
|
|
812
|
-
};
|
|
813
|
-
var buildLayout = async (filePath, directory, generateDynamic = false) => {
|
|
814
|
-
const id = globalThis.__SERVER_CURRENT_STATE_ID__ += 1;
|
|
815
|
-
const childIndicator = `<template layout-id="${id}"></template>`;
|
|
816
|
-
const result = await generateLayout(
|
|
817
|
-
DIST_DIR,
|
|
818
|
-
filePath,
|
|
819
|
-
directory,
|
|
820
|
-
childIndicator,
|
|
821
|
-
generateDynamic
|
|
822
|
-
);
|
|
823
|
-
if (result === false) return false;
|
|
824
|
-
const { pageContentHTML, metadataHTML } = result;
|
|
825
|
-
const splitAround = (str, sub) => {
|
|
826
|
-
const i = str.indexOf(sub);
|
|
827
|
-
if (i === -1) throw new Error("substring does not exist in parent string");
|
|
828
|
-
return {
|
|
829
|
-
startHTML: str.substring(0, i),
|
|
830
|
-
endHTML: str.substring(i + sub.length)
|
|
831
|
-
};
|
|
832
|
-
};
|
|
833
|
-
const splitAt = (str, sub) => {
|
|
834
|
-
const i = str.indexOf(sub) + sub.length;
|
|
835
|
-
if (i === -1) throw new Error("substring does not exist in parent string");
|
|
836
|
-
return {
|
|
837
|
-
startHTML: str.substring(0, i),
|
|
838
|
-
endHTML: str.substring(i)
|
|
839
|
-
};
|
|
840
|
-
};
|
|
841
|
-
const pathname = directory === "" ? "/" : directory;
|
|
842
|
-
return {
|
|
843
|
-
pageContent: splitAt(pageContentHTML, childIndicator),
|
|
844
|
-
metadata: splitAround(metadataHTML, childIndicator),
|
|
845
|
-
scriptTag: `<script data-layout="true" type="module" src="${pathname}layout_data.js" data-pathname="${pathname}" defer="true"></script>`
|
|
846
|
-
};
|
|
847
|
-
};
|
|
848
|
-
var fetchPageLayoutHTML = async (dirname2) => {
|
|
849
|
-
const relative2 = path.relative(options.pagesDirectory, dirname2);
|
|
850
|
-
let split = relative2.split(path.sep).filter(Boolean);
|
|
851
|
-
split.push("/");
|
|
852
|
-
split.reverse();
|
|
853
|
-
let layouts = [];
|
|
854
|
-
for (const dir of split) {
|
|
855
|
-
if (LAYOUT_MAP.has(dir)) {
|
|
856
|
-
const filePath = path.join(path.resolve(options.pagesDirectory), dir, "layout.ts");
|
|
857
|
-
const layout = LAYOUT_MAP.get(dir);
|
|
858
|
-
if (layout.isDynamic) {
|
|
859
|
-
const builtLayout = await buildLayout(layout.filePath, dir, true);
|
|
860
|
-
if (!builtLayout) continue;
|
|
861
|
-
layouts.push(builtLayout);
|
|
862
|
-
} else {
|
|
863
|
-
layouts.push(builtLayouts.get(filePath));
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
const pageContent = {
|
|
868
|
-
startHTML: "",
|
|
869
|
-
endHTML: ""
|
|
870
|
-
};
|
|
871
|
-
const metadata = {
|
|
872
|
-
startHTML: "",
|
|
873
|
-
endHTML: ""
|
|
874
|
-
};
|
|
875
|
-
let scriptTags = "";
|
|
876
|
-
for (const layout of layouts) {
|
|
877
|
-
pageContent.startHTML += layout.pageContent.startHTML;
|
|
878
|
-
metadata.startHTML += layout.metadata.startHTML;
|
|
879
|
-
scriptTags += layout.scriptTag;
|
|
880
|
-
pageContent.endHTML += layout.pageContent.endHTML;
|
|
881
|
-
metadata.endHTML += layout.metadata.endHTML;
|
|
882
|
-
}
|
|
883
|
-
return { pageContent, metadata, scriptTag: scriptTags };
|
|
884
|
-
};
|
|
885
|
-
var buildPages = async (DIST_DIR2) => {
|
|
886
|
-
resetLayouts();
|
|
887
|
-
const pagesDirectory = path.resolve(options.pagesDirectory);
|
|
888
|
-
const subdirectories = [...getAllSubdirectories(pagesDirectory), ""];
|
|
889
|
-
let shouldClientHardReload = false;
|
|
890
|
-
for (const directory of subdirectories) {
|
|
891
|
-
const abs = path.resolve(path.join(pagesDirectory, directory));
|
|
892
|
-
const files = fs.readdirSync(abs, { withFileTypes: true }).filter((f) => f.name.endsWith(".ts"));
|
|
893
|
-
for (const file of files) {
|
|
894
|
-
const filePath = path.join(file.parentPath, file.name);
|
|
895
|
-
const name = file.name.slice(0, file.name.length - 3);
|
|
896
|
-
const isPage = name === "page";
|
|
897
|
-
if (isPage == false) {
|
|
898
|
-
continue;
|
|
899
|
-
}
|
|
900
|
-
try {
|
|
901
|
-
const hardReloadForPage = await buildPage(DIST_DIR2, directory, filePath, name);
|
|
902
|
-
if (hardReloadForPage) {
|
|
903
|
-
shouldClientHardReload = true;
|
|
904
|
-
}
|
|
905
|
-
} catch (e) {
|
|
906
|
-
console.error(e);
|
|
907
|
-
continue;
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
return {
|
|
912
|
-
shouldClientHardReload
|
|
913
|
-
};
|
|
914
|
-
};
|
|
915
|
-
var buildPage = async (DIST_DIR2, directory, filePath, name) => {
|
|
916
|
-
initializeState();
|
|
917
|
-
initializeObjectAttributes();
|
|
918
|
-
resetLoadHooks();
|
|
919
|
-
globalThis.__SERVER_PAGE_DATA_BANNER__ = "";
|
|
920
|
-
let pageElements;
|
|
921
|
-
let metadata;
|
|
922
|
-
let modules = {};
|
|
923
|
-
let pageIgnoresLayout = false;
|
|
924
|
-
let isDynamicPage = false;
|
|
925
|
-
try {
|
|
926
|
-
const {
|
|
927
|
-
page,
|
|
928
|
-
metadata: pageMetadata,
|
|
929
|
-
isDynamic,
|
|
930
|
-
shippedModules: shippedModules2,
|
|
931
|
-
ignoreLayout
|
|
932
|
-
} = await import("file://" + filePath);
|
|
933
|
-
if (shippedModules2 !== void 0) {
|
|
934
|
-
modules = shippedModules2;
|
|
935
|
-
}
|
|
936
|
-
if (ignoreLayout) {
|
|
937
|
-
pageIgnoresLayout = true;
|
|
938
|
-
}
|
|
939
|
-
pageElements = page;
|
|
940
|
-
metadata = pageMetadata;
|
|
941
|
-
if (isDynamic === true) {
|
|
942
|
-
isDynamicPage = isDynamic;
|
|
943
|
-
}
|
|
944
|
-
} catch (e) {
|
|
945
|
-
throw new Error(`Error in Page: ${directory}/${name}.ts - ${e}`);
|
|
946
|
-
}
|
|
947
|
-
PAGE_MAP.set(directory === "" ? "/" : `/${directory}`, {
|
|
948
|
-
isDynamic: isDynamicPage,
|
|
949
|
-
filePath
|
|
950
|
-
});
|
|
951
|
-
if (isDynamicPage) return false;
|
|
952
|
-
if (modules !== void 0) {
|
|
953
|
-
for (const [globalName, path2] of Object.entries(modules)) {
|
|
954
|
-
modulesToShip.push({ globalName, path: path2 });
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
if (!metadata || metadata && typeof metadata !== "function") {
|
|
958
|
-
console.warn(`WARNING: ${filePath} does not export a metadata function.`);
|
|
959
|
-
}
|
|
960
|
-
if (!pageElements) {
|
|
961
|
-
console.warn(`WARNING: ${filePath} should export a const page, which is of type () => BuiltElement<"body">.`);
|
|
962
|
-
}
|
|
963
|
-
const pageProps = {
|
|
964
|
-
pageName: directory
|
|
965
|
-
};
|
|
966
|
-
if (typeof pageElements === "function") {
|
|
967
|
-
if (pageElements.constructor.name === "AsyncFunction") {
|
|
968
|
-
pageElements = await pageElements(pageProps);
|
|
969
|
-
} else {
|
|
970
|
-
pageElements = pageElements(pageProps);
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
const state = getState();
|
|
974
|
-
const pageLoadHooks = getLoadHooks();
|
|
975
|
-
const objectAttributes = getObjectAttributes();
|
|
976
|
-
const layout = await fetchPageLayoutHTML(path.dirname(filePath));
|
|
977
|
-
const foundObjectAttributes = await pageToHTML(
|
|
978
|
-
path.join(DIST_DIR2, directory),
|
|
979
|
-
pageElements || body(),
|
|
980
|
-
metadata ?? (() => head()),
|
|
981
|
-
DIST_DIR2,
|
|
982
|
-
name,
|
|
983
|
-
true,
|
|
984
|
-
modules,
|
|
985
|
-
layout,
|
|
986
|
-
directory
|
|
987
|
-
);
|
|
988
|
-
const {
|
|
989
|
-
sendHardReloadInstruction
|
|
990
|
-
} = await generateClientPageData(
|
|
991
|
-
directory,
|
|
992
|
-
state || {},
|
|
993
|
-
[...objectAttributes, ...foundObjectAttributes],
|
|
994
|
-
pageLoadHooks || [],
|
|
995
|
-
DIST_DIR2,
|
|
996
|
-
name
|
|
997
|
-
);
|
|
998
|
-
return sendHardReloadInstruction === true;
|
|
999
|
-
};
|
|
1000
|
-
var buildDynamicPage = async (DIST_DIR2, directory, pageInfo, req, res) => {
|
|
1001
|
-
directory = directory === "/" ? "" : directory;
|
|
1002
|
-
const filePath = pageInfo.filePath;
|
|
1003
|
-
initializeState();
|
|
1004
|
-
initializeObjectAttributes();
|
|
1005
|
-
resetLoadHooks();
|
|
1006
|
-
globalThis.__SERVER_PAGE_DATA_BANNER__ = "";
|
|
1007
|
-
let pageElements = async (props) => body();
|
|
1008
|
-
let metadata = async (props) => html();
|
|
1009
|
-
let modules = {};
|
|
1010
|
-
let pageIgnoresLayout = false;
|
|
1011
|
-
let isDynamicPage = false;
|
|
1012
|
-
try {
|
|
1013
|
-
const {
|
|
1014
|
-
page,
|
|
1015
|
-
metadata: pageMetadata,
|
|
1016
|
-
isDynamic,
|
|
1017
|
-
shippedModules: shippedModules2,
|
|
1018
|
-
ignoreLayout,
|
|
1019
|
-
requestHook
|
|
1020
|
-
} = await import("file://" + filePath);
|
|
1021
|
-
if (requestHook) {
|
|
1022
|
-
const hook = requestHook;
|
|
1023
|
-
const doContinue = await hook(req, res);
|
|
1024
|
-
if (!doContinue) {
|
|
1025
|
-
return false;
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
if (shippedModules2 !== void 0) {
|
|
1029
|
-
modules = shippedModules2;
|
|
1030
|
-
}
|
|
1031
|
-
if (ignoreLayout) {
|
|
1032
|
-
pageIgnoresLayout = true;
|
|
1033
|
-
}
|
|
1034
|
-
pageElements = page;
|
|
1035
|
-
metadata = pageMetadata;
|
|
1036
|
-
if (isDynamic === true) {
|
|
1037
|
-
isDynamicPage = isDynamic;
|
|
1038
|
-
}
|
|
1039
|
-
} catch (e) {
|
|
1040
|
-
throw new Error(`Error in Page: ${directory}/page.ts - ${e}`);
|
|
1041
|
-
}
|
|
1042
|
-
if (modules !== void 0) {
|
|
1043
|
-
for (const [globalName, path2] of Object.entries(modules)) {
|
|
1044
|
-
modulesToShip.push({ globalName, path: path2 });
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
if (!metadata || metadata && typeof metadata !== "function") {
|
|
1048
|
-
console.warn(`WARNING: ${filePath} does not export a metadata function.`);
|
|
1049
|
-
}
|
|
1050
|
-
if (!pageElements) {
|
|
1051
|
-
console.warn(`WARNING: ${filePath} should export a const page, which is of type () => BuiltElement<"body">.`);
|
|
1052
|
-
}
|
|
1053
|
-
const pageProps = {
|
|
1054
|
-
pageName: directory
|
|
1055
|
-
};
|
|
1056
|
-
if (typeof pageElements === "function") {
|
|
1057
|
-
if (pageElements.constructor.name === "AsyncFunction") {
|
|
1058
|
-
pageElements = await pageElements(pageProps);
|
|
1059
|
-
} else {
|
|
1060
|
-
pageElements = pageElements(pageProps);
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
const layout = await fetchPageLayoutHTML(path.dirname(filePath));
|
|
1064
|
-
const resultHTML = await pageToHTML(
|
|
1065
|
-
path.join(DIST_DIR2, directory),
|
|
1066
|
-
pageElements,
|
|
1067
|
-
metadata,
|
|
1068
|
-
DIST_DIR2,
|
|
1069
|
-
"page",
|
|
1070
|
-
false,
|
|
1071
|
-
modules,
|
|
1072
|
-
layout,
|
|
1073
|
-
directory
|
|
1074
|
-
);
|
|
1075
|
-
await shipModules();
|
|
1076
|
-
return { resultHTML };
|
|
1077
|
-
};
|
|
1078
|
-
var shipModules = async () => {
|
|
1079
|
-
for (const plugin of modulesToShip) {
|
|
1080
|
-
{
|
|
1081
|
-
if (shippedModules.has(plugin.globalName)) continue;
|
|
1082
|
-
shippedModules.set(plugin.globalName, true);
|
|
1083
|
-
}
|
|
1084
|
-
esbuild.build({
|
|
1085
|
-
entryPoints: [plugin.path],
|
|
1086
|
-
bundle: true,
|
|
1087
|
-
outfile: path.join(DIST_DIR, "shipped", plugin.globalName + ".js"),
|
|
1088
|
-
format: "iife",
|
|
1089
|
-
platform: "browser",
|
|
1090
|
-
globalName: plugin.globalName,
|
|
1091
|
-
minify: true,
|
|
1092
|
-
treeShaking: true
|
|
1093
|
-
});
|
|
1094
|
-
}
|
|
1095
|
-
modulesToShip = [];
|
|
1096
|
-
};
|
|
1097
|
-
var build = async () => {
|
|
1098
|
-
if (options.quiet === true) {
|
|
1099
|
-
console.log = function() {
|
|
1100
|
-
};
|
|
1101
|
-
console.error = function() {
|
|
1102
|
-
};
|
|
1103
|
-
console.warn = function() {
|
|
1104
|
-
};
|
|
1105
|
-
}
|
|
1106
|
-
try {
|
|
1107
|
-
{
|
|
1108
|
-
log2(bold(yellow(" -- Elegance.JS -- ")));
|
|
1109
|
-
if (options.environment === "production") {
|
|
1110
|
-
log2(
|
|
1111
|
-
" - ",
|
|
1112
|
-
bgYellow(bold(black(" NOTE "))),
|
|
1113
|
-
" : ",
|
|
1114
|
-
white("In production mode, no "),
|
|
1115
|
-
underline("console.log() "),
|
|
1116
|
-
white("statements will be shown on the client, and all code will be minified.")
|
|
1117
|
-
);
|
|
1118
|
-
log2("");
|
|
1119
|
-
}
|
|
1120
|
-
}
|
|
1121
|
-
if (options.preCompile) {
|
|
1122
|
-
options.preCompile();
|
|
1123
|
-
}
|
|
1124
|
-
const start = performance.now();
|
|
1125
|
-
let shouldClientHardReload;
|
|
1126
|
-
{
|
|
1127
|
-
const { shouldClientHardReload: doReload } = await buildLayouts();
|
|
1128
|
-
if (doReload) shouldClientHardReload = true;
|
|
1129
|
-
}
|
|
1130
|
-
{
|
|
1131
|
-
const { shouldClientHardReload: doReload } = await buildPages(path.resolve(DIST_DIR));
|
|
1132
|
-
if (doReload) shouldClientHardReload = true;
|
|
1133
|
-
}
|
|
1134
|
-
await shipModules();
|
|
1135
|
-
const pagesBuilt = performance.now();
|
|
1136
|
-
await buildClient(DIST_DIR);
|
|
1137
|
-
const end = performance.now();
|
|
1138
|
-
if (options.publicDirectory) {
|
|
1139
|
-
log2("Recursively copying public directory.. this may take a while.");
|
|
1140
|
-
const src = path.relative(process.cwd(), options.publicDirectory.path);
|
|
1141
|
-
if (fs.existsSync(src) === false) {
|
|
1142
|
-
console.warn("WARNING: Public directory not found, an attempt will be made create it..");
|
|
1143
|
-
fs.mkdirSync(src, { recursive: true });
|
|
1144
|
-
}
|
|
1145
|
-
await fs.promises.cp(src, path.join(DIST_DIR), { recursive: true });
|
|
1146
|
-
}
|
|
1147
|
-
{
|
|
1148
|
-
log2(`Took ${Math.round(pagesBuilt - start)}ms to Build Pages.`);
|
|
1149
|
-
log2(`Took ${Math.round(end - pagesBuilt)}ms to Build Client.`);
|
|
1150
|
-
}
|
|
1151
|
-
if (options.server != void 0 && options.server.runServer == true) {
|
|
1152
|
-
startServer({
|
|
1153
|
-
root: options.server.root ?? DIST_DIR,
|
|
1154
|
-
environment: options.environment,
|
|
1155
|
-
port: options.server.port ?? 3e3,
|
|
1156
|
-
host: options.server.host ?? "localhost",
|
|
1157
|
-
DIST_DIR,
|
|
1158
|
-
pagesDirectory: options.pagesDirectory
|
|
1159
|
-
});
|
|
1160
|
-
}
|
|
1161
|
-
process.send?.({ event: "message", data: "compile-finish" });
|
|
1162
|
-
if (shouldClientHardReload) {
|
|
1163
|
-
process.send({ event: "message", data: "hard-reload" });
|
|
1164
|
-
} else {
|
|
1165
|
-
process.send({ event: "message", data: "soft-reload" });
|
|
1166
|
-
}
|
|
1167
|
-
} catch (e) {
|
|
1168
|
-
console.error("Build Failed! Received Error:");
|
|
1169
|
-
console.error(e);
|
|
1170
|
-
return false;
|
|
1171
|
-
}
|
|
1172
|
-
return true;
|
|
1173
|
-
};
|
|
1174
|
-
(async () => {
|
|
1175
|
-
await build();
|
|
1176
|
-
})();
|
|
1177
|
-
|
|
1178
|
-
// src/server/server.ts
|
|
1179
|
-
var gzipAsync = promisify(gzip);
|
|
1180
|
-
var deflateAsync = promisify(deflate);
|
|
1181
|
-
var MIME_TYPES = {
|
|
1
|
+
import {
|
|
2
|
+
createServer as createHttpServer
|
|
3
|
+
} from "http";
|
|
4
|
+
import {
|
|
5
|
+
promises as fs
|
|
6
|
+
} from "fs";
|
|
7
|
+
import {
|
|
8
|
+
join,
|
|
9
|
+
normalize,
|
|
10
|
+
extname,
|
|
11
|
+
dirname
|
|
12
|
+
} from "path";
|
|
13
|
+
import {
|
|
14
|
+
pathToFileURL
|
|
15
|
+
} from "url";
|
|
16
|
+
import {
|
|
17
|
+
log
|
|
18
|
+
} from "../log";
|
|
19
|
+
import {
|
|
20
|
+
gzip,
|
|
21
|
+
deflate
|
|
22
|
+
} from "zlib";
|
|
23
|
+
import {
|
|
24
|
+
promisify
|
|
25
|
+
} from "util";
|
|
26
|
+
import {
|
|
27
|
+
PAGE_MAP
|
|
28
|
+
} from "../build";
|
|
29
|
+
const gzipAsync = promisify(gzip);
|
|
30
|
+
const deflateAsync = promisify(deflate);
|
|
31
|
+
const MIME_TYPES = {
|
|
1182
32
|
".html": "text/html; charset=utf-8",
|
|
1183
33
|
".css": "text/css; charset=utf-8",
|
|
1184
34
|
".js": "application/javascript; charset=utf-8",
|
|
@@ -1197,7 +47,7 @@ function startServer({
|
|
|
1197
47
|
port = 3e3,
|
|
1198
48
|
host = "localhost",
|
|
1199
49
|
environment = "production",
|
|
1200
|
-
DIST_DIR
|
|
50
|
+
DIST_DIR
|
|
1201
51
|
}) {
|
|
1202
52
|
if (!root) throw new Error("Root directory must be specified.");
|
|
1203
53
|
if (!pagesDirectory) throw new Error("Pages directory must be specified.");
|
|
@@ -1206,7 +56,9 @@ function startServer({
|
|
|
1206
56
|
const requestHandler = async (req, res) => {
|
|
1207
57
|
try {
|
|
1208
58
|
if (!req.url) {
|
|
1209
|
-
await sendResponse(req, res, 400, {
|
|
59
|
+
await sendResponse(req, res, 400, {
|
|
60
|
+
"Content-Type": "text/plain; charset=utf-8"
|
|
61
|
+
}, "Bad Request");
|
|
1210
62
|
return;
|
|
1211
63
|
}
|
|
1212
64
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
@@ -1224,16 +76,18 @@ function startServer({
|
|
|
1224
76
|
if (url.pathname.startsWith("/api/")) {
|
|
1225
77
|
await handleApiRequest(pagesDirectory, url.pathname, req, res);
|
|
1226
78
|
} else if (PAGE_MAP.has(url.pathname)) {
|
|
1227
|
-
await handlePageRequest(root, pagesDirectory, url.pathname, req, res,
|
|
79
|
+
await handlePageRequest(root, pagesDirectory, url.pathname, req, res, DIST_DIR, PAGE_MAP.get(url.pathname));
|
|
1228
80
|
} else {
|
|
1229
|
-
await handleStaticRequest(root, pagesDirectory, url.pathname, req, res,
|
|
81
|
+
await handleStaticRequest(root, pagesDirectory, url.pathname, req, res, DIST_DIR);
|
|
1230
82
|
}
|
|
1231
83
|
if (environment === "development") {
|
|
1232
84
|
log.info(req.method, "::", req.url, "-", res.statusCode);
|
|
1233
85
|
}
|
|
1234
86
|
} catch (err) {
|
|
1235
87
|
log.error(err);
|
|
1236
|
-
await sendResponse(req, res, 500, {
|
|
88
|
+
await sendResponse(req, res, 500, {
|
|
89
|
+
"Content-Type": "text/plain; charset=utf-8"
|
|
90
|
+
}, "Internal Server Error");
|
|
1237
91
|
}
|
|
1238
92
|
};
|
|
1239
93
|
function attemptListen(p) {
|
|
@@ -1260,7 +114,7 @@ async function getTargetInfo(root, pathname) {
|
|
|
1260
114
|
}
|
|
1261
115
|
let stats;
|
|
1262
116
|
try {
|
|
1263
|
-
stats = await
|
|
117
|
+
stats = await fs.stat(filePath);
|
|
1264
118
|
} catch {
|
|
1265
119
|
}
|
|
1266
120
|
let targetDir;
|
|
@@ -1269,7 +123,11 @@ async function getTargetInfo(root, pathname) {
|
|
|
1269
123
|
} else {
|
|
1270
124
|
targetDir = originalPathname.endsWith("/") ? filePath : dirname(filePath);
|
|
1271
125
|
}
|
|
1272
|
-
return {
|
|
126
|
+
return {
|
|
127
|
+
filePath,
|
|
128
|
+
targetDir,
|
|
129
|
+
stats
|
|
130
|
+
};
|
|
1273
131
|
}
|
|
1274
132
|
function getMiddlewareDirs(base, parts) {
|
|
1275
133
|
const middlewareDirs = [];
|
|
@@ -1287,7 +145,7 @@ async function collectMiddlewares(dirs) {
|
|
|
1287
145
|
const mwPath = join(dir, "middleware.ts");
|
|
1288
146
|
let mwModule;
|
|
1289
147
|
try {
|
|
1290
|
-
await
|
|
148
|
+
await fs.access(mwPath);
|
|
1291
149
|
const url = pathToFileURL(mwPath).href;
|
|
1292
150
|
mwModule = await import(url);
|
|
1293
151
|
} catch {
|
|
@@ -1303,18 +161,23 @@ async function collectMiddlewares(dirs) {
|
|
|
1303
161
|
}
|
|
1304
162
|
return middlewares;
|
|
1305
163
|
}
|
|
1306
|
-
async function handlePageRequest(root, pagesDirectory, pathname, req, res,
|
|
164
|
+
async function handlePageRequest(root, pagesDirectory, pathname, req, res, DIST_DIR, pageInfo) {
|
|
1307
165
|
try {
|
|
1308
|
-
const {
|
|
166
|
+
const {
|
|
167
|
+
filePath,
|
|
168
|
+
targetDir,
|
|
169
|
+
stats
|
|
170
|
+
} = await getTargetInfo(root, pathname);
|
|
1309
171
|
const relDir = targetDir.slice(root.length).replace(/^[\/\\]+/, "");
|
|
1310
172
|
const parts = relDir.split(/[\\/]/).filter(Boolean);
|
|
1311
173
|
const middlewareDirs = getMiddlewareDirs(pagesDirectory, parts);
|
|
1312
174
|
const middlewares = await collectMiddlewares(middlewareDirs);
|
|
1313
|
-
|
|
175
|
+
const data = {};
|
|
176
|
+
const isDynamic = pageInfo.isDynamic;
|
|
1314
177
|
const handlerPath = isDynamic ? pageInfo.filePath : join(filePath, "index.html");
|
|
1315
178
|
let hasHandler = false;
|
|
1316
179
|
try {
|
|
1317
|
-
await
|
|
180
|
+
await fs.access(handlerPath);
|
|
1318
181
|
hasHandler = true;
|
|
1319
182
|
} catch {
|
|
1320
183
|
}
|
|
@@ -1325,44 +188,65 @@ async function handlePageRequest(root, pagesDirectory, pathname, req, res, DIST_
|
|
|
1325
188
|
}
|
|
1326
189
|
if (isDynamic) {
|
|
1327
190
|
try {
|
|
191
|
+
const {
|
|
192
|
+
buildDynamicPage
|
|
193
|
+
} = await import("../page_compiler");
|
|
1328
194
|
const result = await buildDynamicPage(
|
|
1329
|
-
|
|
195
|
+
DIST_DIR,
|
|
1330
196
|
pathname,
|
|
1331
197
|
pageInfo,
|
|
1332
198
|
req2,
|
|
1333
|
-
res2
|
|
199
|
+
res2,
|
|
200
|
+
data
|
|
1334
201
|
);
|
|
1335
202
|
if (result === false) {
|
|
1336
203
|
return;
|
|
1337
204
|
}
|
|
1338
|
-
const {
|
|
205
|
+
const {
|
|
206
|
+
resultHTML
|
|
207
|
+
} = result;
|
|
1339
208
|
if (resultHTML === false) {
|
|
1340
209
|
return;
|
|
1341
210
|
}
|
|
1342
|
-
await sendResponse(req2, res2, 200, {
|
|
211
|
+
await sendResponse(req2, res2, 200, {
|
|
212
|
+
"Content-Type": MIME_TYPES[".html"]
|
|
213
|
+
}, resultHTML);
|
|
1343
214
|
} catch (err) {
|
|
1344
215
|
log.error("Error building dynamic page -", err);
|
|
1345
216
|
}
|
|
1346
217
|
} else {
|
|
1347
218
|
const ext = extname(handlerPath).toLowerCase();
|
|
1348
219
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
1349
|
-
const
|
|
1350
|
-
await sendResponse(req2, res2, 200, {
|
|
220
|
+
const fileData = await fs.readFile(handlerPath);
|
|
221
|
+
await sendResponse(req2, res2, 200, {
|
|
222
|
+
"Content-Type": contentType
|
|
223
|
+
}, fileData);
|
|
1351
224
|
}
|
|
1352
225
|
};
|
|
1353
|
-
const composed = composeMiddlewares(middlewares, finalHandler, {
|
|
226
|
+
const composed = composeMiddlewares(middlewares, finalHandler, {
|
|
227
|
+
isApi: false,
|
|
228
|
+
root,
|
|
229
|
+
pathname,
|
|
230
|
+
data
|
|
231
|
+
});
|
|
1354
232
|
await composed(req, res);
|
|
1355
233
|
} catch (err) {
|
|
1356
234
|
if (err.message === "Forbidden") {
|
|
1357
|
-
await sendResponse(req, res, 403, {
|
|
235
|
+
await sendResponse(req, res, 403, {
|
|
236
|
+
"Content-Type": "text/plain; charset=utf-8"
|
|
237
|
+
}, "Forbidden");
|
|
1358
238
|
} else {
|
|
1359
239
|
throw err;
|
|
1360
240
|
}
|
|
1361
241
|
}
|
|
1362
242
|
}
|
|
1363
|
-
async function handleStaticRequest(root, pagesDirectory, pathname, req, res,
|
|
243
|
+
async function handleStaticRequest(root, pagesDirectory, pathname, req, res, DIST_DIR) {
|
|
1364
244
|
try {
|
|
1365
|
-
const {
|
|
245
|
+
const {
|
|
246
|
+
filePath,
|
|
247
|
+
targetDir,
|
|
248
|
+
stats
|
|
249
|
+
} = await getTargetInfo(root, pathname);
|
|
1366
250
|
const relDir = targetDir.slice(root.length).replace(/^[\/\\]+/, "");
|
|
1367
251
|
const parts = relDir.split(/[\\/]/).filter(Boolean);
|
|
1368
252
|
const middlewareDirs = getMiddlewareDirs(pagesDirectory, parts);
|
|
@@ -1370,12 +254,10 @@ async function handleStaticRequest(root, pagesDirectory, pathname, req, res, DIS
|
|
|
1370
254
|
let handlerPath = filePath;
|
|
1371
255
|
if (stats && stats.isDirectory()) {
|
|
1372
256
|
handlerPath = join(filePath, "index.html");
|
|
1373
|
-
} else {
|
|
1374
|
-
handlerPath = filePath;
|
|
1375
257
|
}
|
|
1376
258
|
let hasHandler = false;
|
|
1377
259
|
try {
|
|
1378
|
-
await
|
|
260
|
+
await fs.access(handlerPath);
|
|
1379
261
|
hasHandler = true;
|
|
1380
262
|
} catch {
|
|
1381
263
|
}
|
|
@@ -1386,14 +268,22 @@ async function handleStaticRequest(root, pagesDirectory, pathname, req, res, DIS
|
|
|
1386
268
|
}
|
|
1387
269
|
const ext = extname(handlerPath).toLowerCase();
|
|
1388
270
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
1389
|
-
const
|
|
1390
|
-
await sendResponse(req2, res2, 200, {
|
|
271
|
+
const fileData = await fs.readFile(handlerPath);
|
|
272
|
+
await sendResponse(req2, res2, 200, {
|
|
273
|
+
"Content-Type": contentType
|
|
274
|
+
}, fileData);
|
|
1391
275
|
};
|
|
1392
|
-
const composed = composeMiddlewares(middlewares, finalHandler, {
|
|
276
|
+
const composed = composeMiddlewares(middlewares, finalHandler, {
|
|
277
|
+
isApi: false,
|
|
278
|
+
root,
|
|
279
|
+
pathname
|
|
280
|
+
});
|
|
1393
281
|
await composed(req, res);
|
|
1394
282
|
} catch (err) {
|
|
1395
283
|
if (err.message === "Forbidden") {
|
|
1396
|
-
await sendResponse(req, res, 403, {
|
|
284
|
+
await sendResponse(req, res, 403, {
|
|
285
|
+
"Content-Type": "text/plain; charset=utf-8"
|
|
286
|
+
}, "Forbidden");
|
|
1397
287
|
} else {
|
|
1398
288
|
throw err;
|
|
1399
289
|
}
|
|
@@ -1408,7 +298,7 @@ async function handleApiRequest(pagesDirectory, pathname, req, res) {
|
|
|
1408
298
|
const routePath = join(routeDir, "route.ts");
|
|
1409
299
|
let hasRoute = false;
|
|
1410
300
|
try {
|
|
1411
|
-
await
|
|
301
|
+
await fs.access(routePath);
|
|
1412
302
|
hasRoute = true;
|
|
1413
303
|
} catch {
|
|
1414
304
|
}
|
|
@@ -1433,18 +323,20 @@ async function handleApiRequest(pagesDirectory, pathname, req, res) {
|
|
|
1433
323
|
}
|
|
1434
324
|
await fn(req2, res2);
|
|
1435
325
|
};
|
|
1436
|
-
const composed = composeMiddlewares(middlewares, finalHandler, {
|
|
326
|
+
const composed = composeMiddlewares(middlewares, finalHandler, {
|
|
327
|
+
isApi: true
|
|
328
|
+
});
|
|
1437
329
|
await composed(req, res);
|
|
1438
330
|
}
|
|
1439
|
-
function composeMiddlewares(mws, final,
|
|
331
|
+
function composeMiddlewares(mws, final, options) {
|
|
1440
332
|
return async function(req, res) {
|
|
1441
333
|
let index = 0;
|
|
1442
334
|
async function dispatch(err) {
|
|
1443
335
|
if (err) {
|
|
1444
|
-
if (
|
|
336
|
+
if (options.isApi) {
|
|
1445
337
|
return respondWithJsonError(req, res, 500, err.message || "Internal Server Error");
|
|
1446
338
|
} else {
|
|
1447
|
-
return await respondWithErrorPage(
|
|
339
|
+
return await respondWithErrorPage(options.root, options.pathname, 500, req, res);
|
|
1448
340
|
}
|
|
1449
341
|
}
|
|
1450
342
|
if (index >= mws.length) {
|
|
@@ -1464,7 +356,7 @@ function composeMiddlewares(mws, final, options2) {
|
|
|
1464
356
|
};
|
|
1465
357
|
};
|
|
1466
358
|
try {
|
|
1467
|
-
await thisMw(req, res, onceNext(next));
|
|
359
|
+
await thisMw(req, res, onceNext(next), options.data || {});
|
|
1468
360
|
} catch (error) {
|
|
1469
361
|
await dispatch(error);
|
|
1470
362
|
}
|
|
@@ -1473,8 +365,12 @@ function composeMiddlewares(mws, final, options2) {
|
|
|
1473
365
|
};
|
|
1474
366
|
}
|
|
1475
367
|
async function respondWithJsonError(req, res, code, message) {
|
|
1476
|
-
const
|
|
1477
|
-
|
|
368
|
+
const body = JSON.stringify({
|
|
369
|
+
error: message
|
|
370
|
+
});
|
|
371
|
+
await sendResponse(req, res, code, {
|
|
372
|
+
"Content-Type": "application/json; charset=utf-8"
|
|
373
|
+
}, body);
|
|
1478
374
|
}
|
|
1479
375
|
async function respondWithErrorPage(root, pathname, code, req, res) {
|
|
1480
376
|
let currentPath = normalize(join(root, decodeURIComponent(pathname)));
|
|
@@ -1484,7 +380,7 @@ async function respondWithErrorPage(root, pathname, code, req, res) {
|
|
|
1484
380
|
const candidate = join(currentPath, `${code}.html`);
|
|
1485
381
|
if (!tried.has(candidate)) {
|
|
1486
382
|
try {
|
|
1487
|
-
await
|
|
383
|
+
await fs.access(candidate);
|
|
1488
384
|
errorFilePath = candidate;
|
|
1489
385
|
break;
|
|
1490
386
|
} catch {
|
|
@@ -1498,29 +394,31 @@ async function respondWithErrorPage(root, pathname, code, req, res) {
|
|
|
1498
394
|
if (!errorFilePath) {
|
|
1499
395
|
const fallback = join(root, `${code}.html`);
|
|
1500
396
|
try {
|
|
1501
|
-
await
|
|
397
|
+
await fs.access(fallback);
|
|
1502
398
|
errorFilePath = fallback;
|
|
1503
399
|
} catch {
|
|
1504
400
|
}
|
|
1505
401
|
}
|
|
1506
402
|
if (errorFilePath) {
|
|
1507
403
|
try {
|
|
1508
|
-
const
|
|
1509
|
-
await sendResponse(req, res, code, {
|
|
404
|
+
const html = await fs.readFile(errorFilePath, "utf8");
|
|
405
|
+
await sendResponse(req, res, code, {
|
|
406
|
+
"Content-Type": "text/html; charset=utf-8"
|
|
407
|
+
}, html);
|
|
1510
408
|
return;
|
|
1511
409
|
} catch {
|
|
1512
410
|
}
|
|
1513
411
|
}
|
|
1514
|
-
await sendResponse(req, res, code, {
|
|
412
|
+
await sendResponse(req, res, code, {
|
|
413
|
+
"Content-Type": "text/plain; charset=utf-8"
|
|
414
|
+
}, `${code} Error`);
|
|
1515
415
|
}
|
|
1516
416
|
function isCompressible(contentType) {
|
|
1517
417
|
if (!contentType) return false;
|
|
1518
418
|
return /text\/|javascript|json|xml|svg/.test(contentType);
|
|
1519
419
|
}
|
|
1520
|
-
async function sendResponse(req, res, status, headers,
|
|
1521
|
-
|
|
1522
|
-
body2 = Buffer.from(body2);
|
|
1523
|
-
}
|
|
420
|
+
async function sendResponse(req, res, status, headers, body) {
|
|
421
|
+
let bufferBody = typeof body === "string" ? Buffer.from(body) : body;
|
|
1524
422
|
const accept = req.headers["accept-encoding"] || "";
|
|
1525
423
|
let encoding = null;
|
|
1526
424
|
if (accept.match(/\bgzip\b/)) {
|
|
@@ -1530,12 +428,12 @@ async function sendResponse(req, res, status, headers, body2) {
|
|
|
1530
428
|
}
|
|
1531
429
|
if (!encoding || !isCompressible(headers["Content-Type"] || "")) {
|
|
1532
430
|
res.writeHead(status, headers);
|
|
1533
|
-
res.end(
|
|
431
|
+
res.end(bufferBody);
|
|
1534
432
|
return;
|
|
1535
433
|
}
|
|
1536
434
|
const compressor = encoding === "gzip" ? gzipAsync : deflateAsync;
|
|
1537
435
|
try {
|
|
1538
|
-
const compressed = await compressor(
|
|
436
|
+
const compressed = await compressor(bufferBody);
|
|
1539
437
|
headers["Content-Encoding"] = encoding;
|
|
1540
438
|
headers["Vary"] = "Accept-Encoding";
|
|
1541
439
|
res.writeHead(status, headers);
|
|
@@ -1543,7 +441,7 @@ async function sendResponse(req, res, status, headers, body2) {
|
|
|
1543
441
|
} catch (err) {
|
|
1544
442
|
log.error("Compression error:", err);
|
|
1545
443
|
res.writeHead(status, headers);
|
|
1546
|
-
res.end(
|
|
444
|
+
res.end(bufferBody);
|
|
1547
445
|
}
|
|
1548
446
|
}
|
|
1549
447
|
export {
|