elegance-js 2.0.19 → 2.1.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.
@@ -1,9 +1,9 @@
1
1
  // src/page_compiler.ts
2
- import fs from "fs";
2
+ import fs2 from "fs";
3
3
  import path from "path";
4
4
  import { registerLoader, setArcTsConfig } from "ts-arc";
5
5
  import esbuild from "esbuild";
6
- import { fileURLToPath } from "url";
6
+ import { fileURLToPath as fileURLToPath2 } from "url";
7
7
 
8
8
  // src/shared/serverElements.ts
9
9
  var createBuildableElement = (tag) => {
@@ -216,7 +216,7 @@ var generateHTMLTemplate = async ({
216
216
  serverData = null,
217
217
  addPageScriptTag = true,
218
218
  name,
219
- requiredClientModules = [],
219
+ requiredClientModules = {},
220
220
  environment
221
221
  }) => {
222
222
  let StartTemplate = `<meta name="viewport" content="width=device-width, initial-scale=1.0">`;
@@ -224,11 +224,12 @@ var generateHTMLTemplate = async ({
224
224
  StartTemplate += `<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">`;
225
225
  }
226
226
  StartTemplate += '<meta charset="UTF-8">';
227
- for (const module of requiredClientModules) {
228
- StartTemplate += `<script data-module="true" src="/shipped/${module}.js" defer="true"></script>`;
227
+ for (const [globalName] of Object.entries(requiredClientModules)) {
228
+ StartTemplate += `<script data-module="true" src="/shipped/${globalName}.js" defer="true"></script>`;
229
229
  }
230
230
  if (addPageScriptTag === true) {
231
- StartTemplate += `<script data-page="true" type="module" src="${pageURL === "" ? "" : "/"}${pageURL}/${name}_data.js" defer="true"></script>`;
231
+ const sanitized = pageURL === "" ? "/" : pageURL;
232
+ StartTemplate += `<script data-page="true" type="module" data-pathname="${sanitized}" src="${sanitized}${name}_data.js" defer="true"></script>`;
232
233
  }
233
234
  StartTemplate += `<script type="module" src="/client.js" defer="true"></script>`;
234
235
  let builtHead;
@@ -247,13 +248,419 @@ var generateHTMLTemplate = async ({
247
248
  };
248
249
  };
249
250
 
250
- // src/server/createState.ts
251
+ // src/server/server.ts
252
+ import { createServer as createHttpServer } from "http";
253
+ import { promises as fs } from "fs";
254
+ import { join, normalize, extname, dirname } from "path";
255
+ import { pathToFileURL } from "url";
256
+
257
+ // src/log.ts
258
+ var quiet = false;
259
+ function getTimestamp() {
260
+ const now = /* @__PURE__ */ new Date();
261
+ return now.toLocaleString(void 0, {
262
+ year: "2-digit",
263
+ month: "2-digit",
264
+ day: "2-digit",
265
+ hour: "2-digit",
266
+ minute: "2-digit",
267
+ second: "2-digit"
268
+ });
269
+ }
270
+ function color(text, code) {
271
+ return `\x1B[${code}m${text}\x1B[0m`;
272
+ }
273
+ function logInfo(...args) {
274
+ if (quiet) return;
275
+ console.info(`Elegance.JS: ${getTimestamp()} ${color("[INFO]:", 34)}`, ...args);
276
+ }
277
+ function logWarn(...args) {
278
+ if (quiet) return;
279
+ console.warn(`Elegance.JS: ${getTimestamp()} ${color("[WARN]:", 33)}`, ...args);
280
+ }
281
+ function logError(...args) {
282
+ console.error(`Elegance.JS: ${getTimestamp()} ${color("[ERROR]:", 31)}`, ...args);
283
+ }
284
+ var log = {
285
+ info: logInfo,
286
+ warn: logWarn,
287
+ error: logError
288
+ };
289
+
290
+ // src/server/server.ts
291
+ import { gzip, deflate } from "zlib";
292
+ import { promisify } from "util";
293
+ var gzipAsync = promisify(gzip);
294
+ var deflateAsync = promisify(deflate);
295
+ var MIME_TYPES = {
296
+ ".html": "text/html; charset=utf-8",
297
+ ".css": "text/css; charset=utf-8",
298
+ ".js": "application/javascript; charset=utf-8",
299
+ ".json": "application/json; charset=utf-8",
300
+ ".png": "image/png",
301
+ ".jpg": "image/jpeg",
302
+ ".jpeg": "image/jpeg",
303
+ ".gif": "image/gif",
304
+ ".svg": "image/svg+xml",
305
+ ".ico": "image/x-icon",
306
+ ".txt": "text/plain; charset=utf-8"
307
+ };
308
+ function startServer({
309
+ root,
310
+ port = 3e3,
311
+ host = "localhost",
312
+ environment = "production",
313
+ DIST_DIR: DIST_DIR2
314
+ }) {
315
+ if (!root) throw new Error("Root directory must be specified.");
316
+ root = normalize(root).replace(/[\\/]+$/, "");
317
+ const requestHandler = async (req, res) => {
318
+ try {
319
+ if (!req.url) {
320
+ await sendResponse(req, res, 400, { "Content-Type": "text/plain; charset=utf-8" }, "Bad Request");
321
+ return;
322
+ }
323
+ res.setHeader("Access-Control-Allow-Origin", "*");
324
+ res.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
325
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
326
+ if (req.method === "OPTIONS") {
327
+ res.writeHead(204);
328
+ res.end();
329
+ if (environment === "development") {
330
+ log.info(req.method, "::", req.url, "-", res.statusCode);
331
+ }
332
+ return;
333
+ }
334
+ const url = new URL(req.url, `http://${req.headers.host}`);
335
+ if (url.pathname.startsWith("/api/")) {
336
+ await handleApiRequest(root, url.pathname, req, res);
337
+ } else if (PAGE_MAP.has(url.pathname)) {
338
+ await handlePageRequest(root, url.pathname, req, res, DIST_DIR2, PAGE_MAP.get(url.pathname));
339
+ } else {
340
+ await handleStaticRequest(root, url.pathname, req, res, DIST_DIR2);
341
+ }
342
+ if (environment === "development") {
343
+ log.info(req.method, "::", req.url, "-", res.statusCode);
344
+ }
345
+ } catch (err) {
346
+ log.error(err);
347
+ await sendResponse(req, res, 500, { "Content-Type": "text/plain; charset=utf-8" }, "Internal Server Error");
348
+ }
349
+ };
350
+ function attemptListen(p) {
351
+ const server = createHttpServer(requestHandler);
352
+ server.on("error", (err) => {
353
+ if (err.code === "EADDRINUSE") {
354
+ attemptListen(p + 1);
355
+ } else {
356
+ console.error(err);
357
+ }
358
+ });
359
+ server.listen(p, host, () => {
360
+ log.info(`Server running at http://${host}:${p}/`);
361
+ });
362
+ return server;
363
+ }
364
+ return attemptListen(port);
365
+ }
366
+ async function getTargetInfo(root, pathname) {
367
+ const originalPathname = pathname;
368
+ const filePath = normalize(join(root, decodeURIComponent(pathname))).replace(/[\\/]+$/, "");
369
+ if (!filePath.startsWith(root)) {
370
+ throw new Error("Forbidden");
371
+ }
372
+ let stats;
373
+ try {
374
+ stats = await fs.stat(filePath);
375
+ } catch {
376
+ }
377
+ let targetDir;
378
+ if (stats) {
379
+ targetDir = stats.isDirectory() ? filePath : dirname(filePath);
380
+ } else {
381
+ targetDir = originalPathname.endsWith("/") ? filePath : dirname(filePath);
382
+ }
383
+ return { filePath, targetDir, stats };
384
+ }
385
+ function getMiddlewareDirs(base, parts) {
386
+ const middlewareDirs = [];
387
+ let current = base;
388
+ middlewareDirs.push(current);
389
+ for (const part of parts) {
390
+ current = join(current, part);
391
+ middlewareDirs.push(current);
392
+ }
393
+ return middlewareDirs;
394
+ }
395
+ async function collectMiddlewares(dirs) {
396
+ const middlewares = [];
397
+ for (const dir of dirs) {
398
+ const mwPath = join(dir, "middleware.mjs");
399
+ let mwModule;
400
+ try {
401
+ await fs.access(mwPath);
402
+ const url = pathToFileURL(mwPath).href;
403
+ mwModule = await import(url);
404
+ } catch {
405
+ continue;
406
+ }
407
+ const mwKeys = Object.keys(mwModule).sort();
408
+ for (const key of mwKeys) {
409
+ const f = mwModule[key];
410
+ if (typeof f === "function" && !middlewares.some((existing) => existing === f)) {
411
+ middlewares.push(f);
412
+ }
413
+ }
414
+ }
415
+ return middlewares;
416
+ }
417
+ async function handlePageRequest(root, pathname, req, res, DIST_DIR2, pageInfo) {
418
+ try {
419
+ const { filePath, targetDir, stats } = await getTargetInfo(root, pathname);
420
+ const relDir = targetDir.slice(root.length).replace(/^[\/\\]+/, "");
421
+ const parts = relDir.split(/[\\/]/).filter(Boolean);
422
+ const middlewareDirs = getMiddlewareDirs(root, parts);
423
+ const middlewares = await collectMiddlewares(middlewareDirs);
424
+ let isDynamic = pageInfo.isDynamic;
425
+ const handlerPath = isDynamic ? pageInfo.filePath : join(filePath, "index.html");
426
+ let hasHandler = false;
427
+ try {
428
+ await fs.access(handlerPath);
429
+ hasHandler = true;
430
+ } catch {
431
+ }
432
+ const finalHandler = async (req2, res2) => {
433
+ if (!hasHandler) {
434
+ await respondWithErrorPage(root, pathname, 404, req2, res2);
435
+ return;
436
+ }
437
+ if (isDynamic) {
438
+ try {
439
+ const { resultHTML } = await buildDynamicPage(
440
+ DIST_DIR2,
441
+ pathname,
442
+ pageInfo
443
+ );
444
+ if (resultHTML === false) {
445
+ return;
446
+ }
447
+ await sendResponse(req2, res2, 200, { "Content-Type": MIME_TYPES[".html"] }, resultHTML);
448
+ } catch (err) {
449
+ log.error("Error building dynamic page -", err);
450
+ }
451
+ } else {
452
+ const ext = extname(handlerPath).toLowerCase();
453
+ const contentType = MIME_TYPES[ext] || "application/octet-stream";
454
+ const data = await fs.readFile(handlerPath);
455
+ await sendResponse(req2, res2, 200, { "Content-Type": contentType }, data);
456
+ }
457
+ };
458
+ const composed = composeMiddlewares(middlewares, finalHandler, { isApi: false, root, pathname });
459
+ await composed(req, res);
460
+ } catch (err) {
461
+ if (err.message === "Forbidden") {
462
+ await sendResponse(req, res, 403, { "Content-Type": "text/plain; charset=utf-8" }, "Forbidden");
463
+ } else {
464
+ throw err;
465
+ }
466
+ }
467
+ }
468
+ async function handleStaticRequest(root, pathname, req, res, DIST_DIR2) {
469
+ try {
470
+ const { filePath, targetDir, stats } = await getTargetInfo(root, pathname);
471
+ const relDir = targetDir.slice(root.length).replace(/^[\/\\]+/, "");
472
+ const parts = relDir.split(/[\\/]/).filter(Boolean);
473
+ const middlewareDirs = getMiddlewareDirs(root, parts);
474
+ const middlewares = await collectMiddlewares(middlewareDirs);
475
+ let handlerPath = filePath;
476
+ if (stats && stats.isDirectory()) {
477
+ handlerPath = join(filePath, "index.html");
478
+ } else {
479
+ handlerPath = filePath;
480
+ }
481
+ let hasHandler = false;
482
+ try {
483
+ await fs.access(handlerPath);
484
+ hasHandler = true;
485
+ } catch {
486
+ }
487
+ const finalHandler = async (req2, res2) => {
488
+ if (!hasHandler) {
489
+ await respondWithErrorPage(root, pathname, 404, req2, res2);
490
+ return;
491
+ }
492
+ const ext = extname(handlerPath).toLowerCase();
493
+ const contentType = MIME_TYPES[ext] || "application/octet-stream";
494
+ const data = await fs.readFile(handlerPath);
495
+ await sendResponse(req2, res2, 200, { "Content-Type": contentType }, data);
496
+ };
497
+ const composed = composeMiddlewares(middlewares, finalHandler, { isApi: false, root, pathname });
498
+ await composed(req, res);
499
+ } catch (err) {
500
+ if (err.message === "Forbidden") {
501
+ await sendResponse(req, res, 403, { "Content-Type": "text/plain; charset=utf-8" }, "Forbidden");
502
+ } else {
503
+ throw err;
504
+ }
505
+ }
506
+ }
507
+ async function handleApiRequest(root, pathname, req, res) {
508
+ const apiSubPath = pathname.slice("/api/".length);
509
+ const parts = apiSubPath.split("/").filter(Boolean);
510
+ const middlewareDirs = getMiddlewareDirs(join(root, "api"), parts);
511
+ const middlewares = await collectMiddlewares(middlewareDirs);
512
+ const routeDir = middlewareDirs[middlewareDirs.length - 1];
513
+ const routePath = join(routeDir, "route.mjs");
514
+ let hasRoute = false;
515
+ try {
516
+ await fs.access(routePath);
517
+ hasRoute = true;
518
+ } catch {
519
+ }
520
+ let fn = null;
521
+ let module = null;
522
+ if (hasRoute) {
523
+ try {
524
+ const moduleUrl = pathToFileURL(routePath).href;
525
+ module = await import(moduleUrl);
526
+ fn = module[req.method];
527
+ } catch (err) {
528
+ console.error(err);
529
+ return respondWithJsonError(req, res, 500, "Internal Server Error");
530
+ }
531
+ }
532
+ const finalHandler = async (req2, res2) => {
533
+ if (!hasRoute) {
534
+ return respondWithJsonError(req2, res2, 404, "Not Found");
535
+ }
536
+ if (typeof fn !== "function") {
537
+ return respondWithJsonError(req2, res2, 405, "Method Not Allowed");
538
+ }
539
+ await fn(req2, res2);
540
+ };
541
+ const composed = composeMiddlewares(middlewares, finalHandler, { isApi: true });
542
+ await composed(req, res);
543
+ }
544
+ function composeMiddlewares(mws, final, options2) {
545
+ return async function(req, res) {
546
+ let index = 0;
547
+ async function dispatch(err) {
548
+ if (err) {
549
+ if (options2.isApi) {
550
+ return respondWithJsonError(req, res, 500, err.message || "Internal Server Error");
551
+ } else {
552
+ return await respondWithErrorPage(options2.root, options2.pathname, 500, req, res);
553
+ }
554
+ }
555
+ if (index >= mws.length) {
556
+ return await final(req, res);
557
+ }
558
+ const thisMw = mws[index++];
559
+ const next = (e) => dispatch(e);
560
+ const onceNext = (nextFn) => {
561
+ let called = false;
562
+ return async (e) => {
563
+ if (called) {
564
+ log.warn("next() was called in a middleware more than once.");
565
+ return;
566
+ }
567
+ called = true;
568
+ await nextFn(e);
569
+ };
570
+ };
571
+ try {
572
+ await thisMw(req, res, onceNext(next));
573
+ } catch (error) {
574
+ await dispatch(error);
575
+ }
576
+ }
577
+ await dispatch();
578
+ };
579
+ }
580
+ async function respondWithJsonError(req, res, code, message) {
581
+ const body2 = JSON.stringify({ error: message });
582
+ await sendResponse(req, res, code, { "Content-Type": "application/json; charset=utf-8" }, body2);
583
+ }
584
+ async function respondWithErrorPage(root, pathname, code, req, res) {
585
+ let currentPath = normalize(join(root, decodeURIComponent(pathname)));
586
+ let tried = /* @__PURE__ */ new Set();
587
+ let errorFilePath = null;
588
+ while (currentPath.startsWith(root)) {
589
+ const candidate = join(currentPath, `${code}.html`);
590
+ if (!tried.has(candidate)) {
591
+ try {
592
+ await fs.access(candidate);
593
+ errorFilePath = candidate;
594
+ break;
595
+ } catch {
596
+ }
597
+ tried.add(candidate);
598
+ }
599
+ const parent = dirname(currentPath);
600
+ if (parent === currentPath) break;
601
+ currentPath = parent;
602
+ }
603
+ if (!errorFilePath) {
604
+ const fallback = join(root, `${code}.html`);
605
+ try {
606
+ await fs.access(fallback);
607
+ errorFilePath = fallback;
608
+ } catch {
609
+ }
610
+ }
611
+ if (errorFilePath) {
612
+ try {
613
+ const html2 = await fs.readFile(errorFilePath, "utf8");
614
+ await sendResponse(req, res, code, { "Content-Type": "text/html; charset=utf-8" }, html2);
615
+ return;
616
+ } catch {
617
+ }
618
+ }
619
+ await sendResponse(req, res, code, { "Content-Type": "text/plain; charset=utf-8" }, `${code} Error`);
620
+ }
621
+ function isCompressible(contentType) {
622
+ if (!contentType) return false;
623
+ return /text\/|javascript|json|xml|svg/.test(contentType);
624
+ }
625
+ async function sendResponse(req, res, status, headers, body2) {
626
+ if (typeof body2 === "string") {
627
+ body2 = Buffer.from(body2);
628
+ }
629
+ const accept = req.headers["accept-encoding"] || "";
630
+ let encoding = null;
631
+ if (accept.match(/\bgzip\b/)) {
632
+ encoding = "gzip";
633
+ } else if (accept.match(/\bdeflate\b/)) {
634
+ encoding = "deflate";
635
+ }
636
+ if (!encoding || !isCompressible(headers["Content-Type"] || "")) {
637
+ res.writeHead(status, headers);
638
+ res.end(body2);
639
+ return;
640
+ }
641
+ const compressor = encoding === "gzip" ? gzipAsync : deflateAsync;
642
+ try {
643
+ const compressed = await compressor(body2);
644
+ headers["Content-Encoding"] = encoding;
645
+ headers["Vary"] = "Accept-Encoding";
646
+ res.writeHead(status, headers);
647
+ res.end(compressed);
648
+ } catch (err) {
649
+ log.error("Compression error:", err);
650
+ res.writeHead(status, headers);
651
+ res.end(body2);
652
+ }
653
+ }
654
+
655
+ // src/server/loadHook.ts
656
+ var resetLoadHooks = () => globalThis.__SERVER_CURRENT_LOADHOOKS__ = [];
657
+ var getLoadHooks = () => globalThis.__SERVER_CURRENT_LOADHOOKS__;
658
+
659
+ // src/server/state.ts
251
660
  if (!globalThis.__SERVER_CURRENT_STATE_ID__) {
252
661
  globalThis.__SERVER_CURRENT_STATE_ID__ = 1;
253
662
  }
254
- var initializeState = () => {
255
- globalThis.__SERVER_CURRENT_STATE__ = [];
256
- };
663
+ var initializeState = () => globalThis.__SERVER_CURRENT_STATE__ = [];
257
664
  var getState = () => {
258
665
  return globalThis.__SERVER_CURRENT_STATE__;
259
666
  };
@@ -262,16 +669,12 @@ var getObjectAttributes = () => {
262
669
  return globalThis.__SERVER_CURRENT_OBJECT_ATTRIBUTES__;
263
670
  };
264
671
 
265
- // src/server/loadHook.ts
266
- var resetLoadHooks = () => globalThis.__SERVER_CURRENT_LOADHOOKS__ = [];
267
- var getLoadHooks = () => globalThis.__SERVER_CURRENT_LOADHOOKS__;
268
-
269
672
  // src/server/layout.ts
270
673
  var resetLayouts = () => globalThis.__SERVER_CURRENT_LAYOUTS__ = /* @__PURE__ */ new Map();
271
674
  if (!globalThis.__SERVER_CURRENT_LAYOUT_ID__) globalThis.__SERVER_CURRENT_LAYOUT_ID__ = 1;
272
675
 
273
676
  // src/page_compiler.ts
274
- var __filename = fileURLToPath(import.meta.url);
677
+ var __filename = fileURLToPath2(import.meta.url);
275
678
  var __dirname = path.dirname(__filename);
276
679
  setArcTsConfig(__dirname);
277
680
  registerLoader();
@@ -281,6 +684,8 @@ if (packageDir === void 0) {
281
684
  }
282
685
  var clientPath = path.resolve(packageDir, "./dist/client/client.mjs");
283
686
  var watcherPath = path.resolve(packageDir, "./dist/client/watcher.mjs");
687
+ var shippedModules = /* @__PURE__ */ new Map();
688
+ var modulesToShip = [];
284
689
  var yellow = (text) => {
285
690
  return `\x1B[38;2;238;184;68m${text}`;
286
691
  };
@@ -299,15 +704,17 @@ var underline = (text) => {
299
704
  var white = (text) => {
300
705
  return `\x1B[38;2;255;247;229m${text}`;
301
706
  };
302
- var log = (...text) => {
707
+ var log2 = (...text) => {
303
708
  if (options.quiet) return;
304
709
  return console.log(text.map((text2) => `${text2}\x1B[0m`).join(""));
305
710
  };
306
711
  var options = JSON.parse(process.env.OPTIONS);
307
712
  var DIST_DIR = process.env.DIST_DIR;
713
+ var PAGE_MAP = /* @__PURE__ */ new Map();
714
+ var LAYOUT_MAP2 = /* @__PURE__ */ new Map();
308
715
  var getAllSubdirectories = (dir, baseDir = dir) => {
309
716
  let directories = [];
310
- const items = fs.readdirSync(dir, { withFileTypes: true });
717
+ const items = fs2.readdirSync(dir, { withFileTypes: true });
311
718
  for (const item of items) {
312
719
  if (item.isDirectory()) {
313
720
  const fullPath = path.join(dir, item.name);
@@ -320,10 +727,10 @@ var getAllSubdirectories = (dir, baseDir = dir) => {
320
727
  };
321
728
  var buildClient = async (DIST_DIR2) => {
322
729
  let clientString = "window.__name = (func) => func; ";
323
- clientString += fs.readFileSync(clientPath, "utf-8");
730
+ clientString += fs2.readFileSync(clientPath, "utf-8");
324
731
  if (options.hotReload !== void 0) {
325
732
  clientString += `const watchServerPort = ${options.hotReload.port}`;
326
- clientString += fs.readFileSync(watcherPath, "utf-8");
733
+ clientString += fs2.readFileSync(watcherPath, "utf-8");
327
734
  }
328
735
  const transformedClient = await esbuild.transform(clientString, {
329
736
  minify: options.environment === "production",
@@ -333,7 +740,7 @@ var buildClient = async (DIST_DIR2) => {
333
740
  platform: "node",
334
741
  loader: "ts"
335
742
  });
336
- fs.writeFileSync(
743
+ fs2.writeFileSync(
337
744
  path.join(DIST_DIR2, "/client.js"),
338
745
  transformedClient.code
339
746
  );
@@ -512,7 +919,7 @@ ${trace}`);
512
919
  }
513
920
  }
514
921
  };
515
- var pageToHTML = async (pageLocation, pageElements, metadata, DIST_DIR2, pageName, doWrite = true, requiredClientModules = [], layout) => {
922
+ var pageToHTML = async (pageLocation, pageElements, metadata, DIST_DIR2, pageName, doWrite = true, requiredClientModules = {}, layout, pathname = "") => {
516
923
  if (typeof pageElements === "string" || typeof pageElements === "boolean" || typeof pageElements === "number" || Array.isArray(pageElements)) {
517
924
  throw new Error(`The root element of a page / layout must be a built element, not just a Child. Received: ${typeof pageElements}.`);
518
925
  }
@@ -524,23 +931,59 @@ var pageToHTML = async (pageLocation, pageElements, metadata, DIST_DIR2, pageNam
524
931
  pageLocation
525
932
  );
526
933
  const { internals, builtMetadata } = await generateHTMLTemplate({
527
- pageURL: path.relative(DIST_DIR2, pageLocation),
934
+ pageURL: pathname,
528
935
  head: metadata,
529
- addPageScriptTag: true,
936
+ addPageScriptTag: doWrite,
530
937
  name: pageName,
531
938
  requiredClientModules,
532
939
  environment: options.environment
533
940
  });
941
+ let extraBodyHTML = "";
942
+ if (doWrite === false) {
943
+ const state = getState();
944
+ const pageLoadHooks = getLoadHooks();
945
+ const userObjectAttributes = getObjectAttributes();
946
+ const {
947
+ result
948
+ } = await generateClientPageData(
949
+ pathname,
950
+ state || {},
951
+ [...objectAttributes, ...userObjectAttributes],
952
+ pageLoadHooks || [],
953
+ DIST_DIR2,
954
+ "page",
955
+ "pd",
956
+ false
957
+ );
958
+ const sanitized = pathname === "" ? "/" : pathname;
959
+ extraBodyHTML = `<script data-hook="true" data-pathname="${sanitized}" type="text/plain">${result}</script>`;
960
+ extraBodyHTML += `<script>
961
+ const text = document.querySelector('[data-hook="true"][data-pathname="${sanitized}"][type="text/plain"').textContent;
962
+ const blob = new Blob([text], { type: 'text/javascript' });
963
+ const url = URL.createObjectURL(blob);
964
+
965
+ const script = document.createElement("script");
966
+ script.src = url;
967
+ script.type = "module";
968
+ script.setAttribute("data-page", "true");
969
+ script.setAttribute("data-pathname", "${sanitized}");
970
+
971
+ document.head.appendChild(script);
972
+
973
+ document.currentScript.remove();
974
+ </script>`;
975
+ extraBodyHTML = extraBodyHTML.replace(/\s+/g, " ").replace(/\s*([{}();,:])\s*/g, "$1").trim();
976
+ }
534
977
  const headHTML = `<!DOCTYPE html>${layout.metadata.startHTML}${layout.scriptTag}${internals}${builtMetadata}${layout.metadata.endHTML}`;
535
- const bodyHTML = `${layout.pageContent.startHTML}${renderedPage.bodyHTML}${layout.pageContent.endHTML}`;
978
+ const bodyHTML = `${layout.pageContent.startHTML}${renderedPage.bodyHTML}${extraBodyHTML}${layout.pageContent.endHTML}`;
536
979
  const resultHTML = `${headHTML}${bodyHTML}`;
537
980
  const htmlLocation = path.join(pageLocation, (pageName === "page" ? "index" : pageName) + ".html");
538
981
  if (doWrite) {
539
- const dirname = path.dirname(htmlLocation);
540
- if (fs.existsSync(dirname) === false) {
541
- fs.mkdirSync(dirname, { recursive: true });
982
+ const dirname2 = path.dirname(htmlLocation);
983
+ if (fs2.existsSync(dirname2) === false) {
984
+ fs2.mkdirSync(dirname2, { recursive: true });
542
985
  }
543
- fs.writeFileSync(
986
+ fs2.writeFileSync(
544
987
  htmlLocation,
545
988
  resultHTML,
546
989
  {
@@ -549,16 +992,14 @@ var pageToHTML = async (pageLocation, pageElements, metadata, DIST_DIR2, pageNam
549
992
  }
550
993
  );
551
994
  return objectAttributes;
552
- } else {
553
- return {
554
- objectAttributes,
555
- resultHTML
556
- };
557
995
  }
996
+ return resultHTML;
558
997
  };
559
- var generateClientPageData = async (pageLocation, state, objectAttributes, pageLoadHooks, DIST_DIR2, pageName, globalVariableName = "pd") => {
560
- const pageDiff = path.relative(DIST_DIR2, pageLocation);
561
- let clientPageJSText = `${globalThis.__SERVER_PAGE_DATA_BANNER__}let url="${pageDiff === "" ? "/" : `/${pageDiff}`}";`;
998
+ var generateClientPageData = async (pageLocation, state, objectAttributes, pageLoadHooks, DIST_DIR2, pageName, globalVariableName = "pd", write = true) => {
999
+ let clientPageJSText = "";
1000
+ {
1001
+ clientPageJSText += `${globalThis.__SERVER_PAGE_DATA_BANNER__}`;
1002
+ }
562
1003
  {
563
1004
  clientPageJSText += `export const data = {`;
564
1005
  if (state) {
@@ -600,30 +1041,29 @@ var generateClientPageData = async (pageLocation, state, objectAttributes, pageL
600
1041
  }
601
1042
  if (pageLoadHooks.length > 0) {
602
1043
  clientPageJSText += "lh:[";
603
- for (const loadHook of pageLoadHooks) {
604
- clientPageJSText += `{fn:${loadHook.fn}},`;
1044
+ for (const loadHook2 of pageLoadHooks) {
1045
+ clientPageJSText += `{fn:${loadHook2.fn}},`;
605
1046
  }
606
1047
  clientPageJSText += "],";
607
1048
  }
608
1049
  clientPageJSText += `};`;
609
1050
  }
610
- clientPageJSText += `if(!globalThis.${globalVariableName}) { globalThis.${globalVariableName} = {}; }; globalThis.${globalVariableName}[url] = data;`;
611
- const pageDataPath = path.join(pageLocation, `${pageName}_data.js`);
1051
+ const pageDataPath = path.join(DIST_DIR2, pageLocation, `${pageName}_data.js`);
612
1052
  let sendHardReloadInstruction = false;
613
1053
  const transformedResult = await esbuild.transform(clientPageJSText, { minify: options.environment === "production" }).catch((error) => {
614
1054
  console.error("Failed to transform client page js!", error);
615
1055
  });
616
1056
  if (!transformedResult) return { sendHardReloadInstruction };
617
- if (fs.existsSync(pageDataPath)) {
618
- const content = fs.readFileSync(pageDataPath).toString();
1057
+ if (fs2.existsSync(pageDataPath)) {
1058
+ const content = fs2.readFileSync(pageDataPath).toString();
619
1059
  if (content !== transformedResult.code) {
620
1060
  sendHardReloadInstruction = true;
621
1061
  }
622
1062
  }
623
- fs.writeFileSync(pageDataPath, transformedResult.code, "utf-8");
624
- return { sendHardReloadInstruction };
1063
+ if (write) fs2.writeFileSync(pageDataPath, transformedResult.code, "utf-8");
1064
+ return { sendHardReloadInstruction, result: transformedResult.code };
625
1065
  };
626
- var generateLayout = async (DIST_DIR2, filePath, directory, childIndicator) => {
1066
+ var generateLayout = async (DIST_DIR2, filePath, directory, childIndicator, generateDynamic = false) => {
627
1067
  initializeState();
628
1068
  initializeObjectAttributes();
629
1069
  resetLoadHooks();
@@ -631,24 +1071,30 @@ var generateLayout = async (DIST_DIR2, filePath, directory, childIndicator) => {
631
1071
  let layoutElements;
632
1072
  let metadataElements;
633
1073
  let modules = [];
1074
+ let isDynamicLayout = false;
634
1075
  try {
635
1076
  const {
636
1077
  layout,
637
1078
  metadata,
638
1079
  isDynamic,
639
- requiredClientModules
1080
+ shippedModules: shippedModules2
640
1081
  } = await import("file://" + filePath);
641
- if (requiredClientModules !== void 0) {
642
- modules = requiredClientModules;
1082
+ if (shippedModules2 !== void 0) {
1083
+ modules = shippedModules2;
643
1084
  }
644
1085
  layoutElements = layout;
645
1086
  metadataElements = metadata;
646
1087
  if (isDynamic === true) {
647
- throw new Error("ts-arc in Elegance does not support dynamic pages yet.");
1088
+ isDynamicLayout = isDynamic;
648
1089
  }
649
1090
  } catch (e) {
650
- throw new Error(`Error in Page: ${directory === "" ? "/" : directory}layout.mjs - ${e}`);
1091
+ throw new Error(`Error in Page: ${directory === "" ? "/" : directory}layout.ts - ${e}`);
651
1092
  }
1093
+ LAYOUT_MAP2.set(directory === "" ? "/" : directory, {
1094
+ isDynamic: isDynamicLayout,
1095
+ filePath
1096
+ });
1097
+ if (isDynamicLayout === true && generateDynamic === false) return false;
652
1098
  {
653
1099
  if (!layoutElements) {
654
1100
  throw new Error(`WARNING: ${filePath} should export a const layout, which is of type Layout: (child: Child) => AnyBuiltElement.`);
@@ -683,11 +1129,12 @@ var generateLayout = async (DIST_DIR2, filePath, directory, childIndicator) => {
683
1129
  const stack = [];
684
1130
  const processedPageElements = processPageElements(layoutElements, foundObjectAttributes, 0, stack);
685
1131
  const renderedPage = await serverSideRenderPage(
686
- processedPageElements
1132
+ processedPageElements,
1133
+ directory
687
1134
  );
688
1135
  const metadataHTML = metadataElements ? renderRecursively(metadataElements) : "";
689
1136
  await generateClientPageData(
690
- path.dirname(path.join(DIST_DIR2, "dist", directory)),
1137
+ directory,
691
1138
  state || {},
692
1139
  [...objectAttributes, ...foundObjectAttributes],
693
1140
  pageLoadHooks || [],
@@ -704,7 +1151,7 @@ var buildLayouts = async () => {
704
1151
  let shouldClientHardReload = false;
705
1152
  for (const directory of subdirectories) {
706
1153
  const abs = path.resolve(path.join(pagesDirectory, directory));
707
- const files = fs.readdirSync(abs, { withFileTypes: true }).filter((f) => f.name.endsWith(".ts"));
1154
+ const files = fs2.readdirSync(abs, { withFileTypes: true }).filter((f) => f.name.endsWith(".ts"));
708
1155
  for (const file of files) {
709
1156
  const filePath = path.join(file.parentPath, file.name);
710
1157
  const name = file.name.slice(0, file.name.length - 3);
@@ -714,6 +1161,7 @@ var buildLayouts = async () => {
714
1161
  }
715
1162
  try {
716
1163
  const builtLayout = await buildLayout(filePath, directory);
1164
+ if (!builtLayout) return { shouldClientHardReload: false };
717
1165
  builtLayouts.set(filePath, builtLayout);
718
1166
  } catch (e) {
719
1167
  console.error(e);
@@ -723,15 +1171,18 @@ var buildLayouts = async () => {
723
1171
  }
724
1172
  return { shouldClientHardReload };
725
1173
  };
726
- var buildLayout = async (filePath, directory) => {
1174
+ var buildLayout = async (filePath, directory, generateDynamic = false) => {
727
1175
  const id = globalThis.__SERVER_CURRENT_STATE_ID__ += 1;
728
1176
  const childIndicator = `<template layout-id="${id}"></template>`;
729
- const { pageContentHTML, metadataHTML } = await generateLayout(
1177
+ const result = await generateLayout(
730
1178
  DIST_DIR,
731
1179
  filePath,
732
1180
  directory,
733
- childIndicator
1181
+ childIndicator,
1182
+ generateDynamic
734
1183
  );
1184
+ if (result === false) return false;
1185
+ const { pageContentHTML, metadataHTML } = result;
735
1186
  const splitAround = (str, sub) => {
736
1187
  const i = str.indexOf(sub);
737
1188
  if (i === -1) throw new Error("substring does not exist in parent string");
@@ -748,23 +1199,30 @@ var buildLayout = async (filePath, directory) => {
748
1199
  endHTML: str.substring(i)
749
1200
  };
750
1201
  };
751
- const pageURL = directory;
1202
+ const pathname = directory === "" ? "/" : directory;
752
1203
  return {
753
1204
  pageContent: splitAt(pageContentHTML, childIndicator),
754
1205
  metadata: splitAround(metadataHTML, childIndicator),
755
- scriptTag: `<script data-layout="true" type="module" src="${pageURL}${pageURL === "/" ? "" : "/"}layout_data.js" defer="true"></script>`
1206
+ scriptTag: `<script data-layout="true" type="module" src="${pathname}layout_data.js" data-pathname="${pathname}" defer="true"></script>`
756
1207
  };
757
1208
  };
758
- var fetchPageLayoutHTML = async (dirname) => {
759
- const relative = path.relative(options.pagesDirectory, dirname);
760
- let split = relative.split(path.sep).filter(Boolean);
1209
+ var fetchPageLayoutHTML = async (dirname2) => {
1210
+ const relative2 = path.relative(options.pagesDirectory, dirname2);
1211
+ let split = relative2.split(path.sep).filter(Boolean);
761
1212
  split.push("/");
762
1213
  split.reverse();
763
1214
  let layouts = [];
764
1215
  for (const dir of split) {
765
- const filePath = path.resolve(path.join(options.pagesDirectory, dir, "layout.ts"));
766
- if (builtLayouts.has(filePath)) {
767
- layouts.push(builtLayouts.get(filePath));
1216
+ if (LAYOUT_MAP2.has(dir)) {
1217
+ const filePath = path.join(path.resolve(options.pagesDirectory), dir, "layout.ts");
1218
+ const layout = LAYOUT_MAP2.get(dir);
1219
+ if (layout.isDynamic) {
1220
+ const builtLayout = await buildLayout(layout.filePath, dir, true);
1221
+ if (!builtLayout) continue;
1222
+ layouts.push(builtLayout);
1223
+ } else {
1224
+ layouts.push(builtLayouts.get(filePath));
1225
+ }
768
1226
  }
769
1227
  }
770
1228
  const pageContent = {
@@ -792,7 +1250,7 @@ var buildPages = async (DIST_DIR2) => {
792
1250
  let shouldClientHardReload = false;
793
1251
  for (const directory of subdirectories) {
794
1252
  const abs = path.resolve(path.join(pagesDirectory, directory));
795
- const files = fs.readdirSync(abs, { withFileTypes: true }).filter((f) => f.name.endsWith(".ts"));
1253
+ const files = fs2.readdirSync(abs, { withFileTypes: true }).filter((f) => f.name.endsWith(".ts"));
796
1254
  for (const file of files) {
797
1255
  const filePath = path.join(file.parentPath, file.name);
798
1256
  const name = file.name.slice(0, file.name.length - 3);
@@ -822,32 +1280,43 @@ var buildPage = async (DIST_DIR2, directory, filePath, name) => {
822
1280
  globalThis.__SERVER_PAGE_DATA_BANNER__ = "";
823
1281
  let pageElements;
824
1282
  let metadata;
825
- let modules = [];
1283
+ let modules = {};
826
1284
  let pageIgnoresLayout = false;
1285
+ let isDynamicPage = false;
827
1286
  try {
828
1287
  const {
829
1288
  page,
830
1289
  metadata: pageMetadata,
831
- isDynamicPage,
832
- requiredClientModules,
1290
+ isDynamic,
1291
+ shippedModules: shippedModules2,
833
1292
  ignoreLayout
834
1293
  } = await import("file://" + filePath);
835
- if (requiredClientModules !== void 0) {
836
- modules = requiredClientModules;
1294
+ if (shippedModules2 !== void 0) {
1295
+ modules = shippedModules2;
837
1296
  }
838
1297
  if (ignoreLayout) {
839
1298
  pageIgnoresLayout = true;
840
1299
  }
841
1300
  pageElements = page;
842
1301
  metadata = pageMetadata;
843
- if (isDynamicPage === true) {
844
- throw new Error("Dynamic page is not yet supported with ts-arc");
1302
+ if (isDynamic === true) {
1303
+ isDynamicPage = isDynamic;
845
1304
  }
846
1305
  } catch (e) {
847
- throw new Error(`Error in Page: ${directory === "" ? "/" : directory}${name}.ts - ${e}`);
1306
+ throw new Error(`Error in Page: ${directory}/${name}.ts - ${e}`);
1307
+ }
1308
+ PAGE_MAP.set(directory === "" ? "/" : directory, {
1309
+ isDynamic: isDynamicPage,
1310
+ filePath
1311
+ });
1312
+ if (isDynamicPage) return false;
1313
+ if (modules !== void 0) {
1314
+ for (const [globalName, path2] of Object.entries(modules)) {
1315
+ modulesToShip.push({ globalName, path: path2 });
1316
+ }
848
1317
  }
849
1318
  if (!metadata || metadata && typeof metadata !== "function") {
850
- console.warn(`WARNING: ${filePath} does not export a metadata function. This is *highly* recommended.`);
1319
+ console.warn(`WARNING: ${filePath} does not export a metadata function.`);
851
1320
  }
852
1321
  if (!pageElements) {
853
1322
  console.warn(`WARNING: ${filePath} should export a const page, which is of type () => BuiltElement<"body">.`);
@@ -874,12 +1343,13 @@ var buildPage = async (DIST_DIR2, directory, filePath, name) => {
874
1343
  name,
875
1344
  true,
876
1345
  modules,
877
- layout
1346
+ layout,
1347
+ directory
878
1348
  );
879
1349
  const {
880
1350
  sendHardReloadInstruction
881
1351
  } = await generateClientPageData(
882
- path.join(DIST_DIR2, directory),
1352
+ directory,
883
1353
  state || {},
884
1354
  [...objectAttributes, ...foundObjectAttributes],
885
1355
  pageLoadHooks || [],
@@ -888,9 +1358,95 @@ var buildPage = async (DIST_DIR2, directory, filePath, name) => {
888
1358
  );
889
1359
  return sendHardReloadInstruction === true;
890
1360
  };
891
- var recursionFlag = Symbol("external-node-modules-recursion");
892
- var shippedPlugins = /* @__PURE__ */ new Map();
893
- var pluginsToShip = [];
1361
+ var buildDynamicPage = async (DIST_DIR2, directory, pageInfo) => {
1362
+ directory = directory === "/" ? "" : directory;
1363
+ const filePath = pageInfo.filePath;
1364
+ initializeState();
1365
+ initializeObjectAttributes();
1366
+ resetLoadHooks();
1367
+ globalThis.__SERVER_PAGE_DATA_BANNER__ = "";
1368
+ let pageElements = async (props) => body();
1369
+ let metadata = async (props) => html();
1370
+ let modules = {};
1371
+ let pageIgnoresLayout = false;
1372
+ let isDynamicPage = false;
1373
+ try {
1374
+ const {
1375
+ page,
1376
+ metadata: pageMetadata,
1377
+ isDynamic,
1378
+ shippedModules: shippedModules2,
1379
+ ignoreLayout
1380
+ } = await import("file://" + filePath);
1381
+ if (shippedModules2 !== void 0) {
1382
+ modules = shippedModules2;
1383
+ }
1384
+ if (ignoreLayout) {
1385
+ pageIgnoresLayout = true;
1386
+ }
1387
+ pageElements = page;
1388
+ metadata = pageMetadata;
1389
+ if (isDynamic === true) {
1390
+ isDynamicPage = isDynamic;
1391
+ }
1392
+ } catch (e) {
1393
+ throw new Error(`Error in Page: ${directory}/page.ts - ${e}`);
1394
+ }
1395
+ if (modules !== void 0) {
1396
+ for (const [globalName, path2] of Object.entries(modules)) {
1397
+ modulesToShip.push({ globalName, path: path2 });
1398
+ }
1399
+ }
1400
+ if (!metadata || metadata && typeof metadata !== "function") {
1401
+ console.warn(`WARNING: ${filePath} does not export a metadata function.`);
1402
+ }
1403
+ if (!pageElements) {
1404
+ console.warn(`WARNING: ${filePath} should export a const page, which is of type () => BuiltElement<"body">.`);
1405
+ }
1406
+ const pageProps = {
1407
+ pageName: directory
1408
+ };
1409
+ if (typeof pageElements === "function") {
1410
+ if (pageElements.constructor.name === "AsyncFunction") {
1411
+ pageElements = await pageElements(pageProps);
1412
+ } else {
1413
+ pageElements = pageElements(pageProps);
1414
+ }
1415
+ }
1416
+ const layout = await fetchPageLayoutHTML(path.dirname(filePath));
1417
+ const resultHTML = await pageToHTML(
1418
+ path.join(DIST_DIR2, directory),
1419
+ pageElements,
1420
+ metadata,
1421
+ DIST_DIR2,
1422
+ "page",
1423
+ false,
1424
+ modules,
1425
+ layout,
1426
+ directory
1427
+ );
1428
+ await shipModules();
1429
+ return { resultHTML };
1430
+ };
1431
+ var shipModules = async () => {
1432
+ for (const plugin of modulesToShip) {
1433
+ {
1434
+ if (shippedModules.has(plugin.globalName)) continue;
1435
+ shippedModules.set(plugin.globalName, true);
1436
+ }
1437
+ esbuild.build({
1438
+ entryPoints: [plugin.path],
1439
+ bundle: true,
1440
+ outfile: path.join(DIST_DIR, "shipped", plugin.globalName + ".js"),
1441
+ format: "iife",
1442
+ platform: "browser",
1443
+ globalName: plugin.globalName,
1444
+ minify: true,
1445
+ treeShaking: true
1446
+ });
1447
+ }
1448
+ modulesToShip = [];
1449
+ };
894
1450
  var build = async () => {
895
1451
  if (options.quiet === true) {
896
1452
  console.log = function() {
@@ -902,9 +1458,9 @@ var build = async () => {
902
1458
  }
903
1459
  try {
904
1460
  {
905
- log(bold(yellow(" -- Elegance.JS -- ")));
1461
+ log2(bold(yellow(" -- Elegance.JS -- ")));
906
1462
  if (options.environment === "production") {
907
- log(
1463
+ log2(
908
1464
  " - ",
909
1465
  bgYellow(bold(black(" NOTE "))),
910
1466
  " : ",
@@ -912,32 +1468,13 @@ var build = async () => {
912
1468
  underline("console.log() "),
913
1469
  white("statements will be shown on the client, and all code will be minified.")
914
1470
  );
915
- log("");
1471
+ log2("");
916
1472
  }
917
1473
  }
918
1474
  if (options.preCompile) {
919
1475
  options.preCompile();
920
1476
  }
921
1477
  const start = performance.now();
922
- {
923
- pluginsToShip = [];
924
- for (const plugin of pluginsToShip) {
925
- {
926
- if (shippedPlugins.has(plugin.globalName)) continue;
927
- shippedPlugins.set(plugin.globalName, true);
928
- }
929
- await esbuild.build({
930
- entryPoints: [plugin.path],
931
- bundle: true,
932
- outfile: path.join(DIST_DIR, "shipped", plugin.globalName + ".js"),
933
- format: "iife",
934
- platform: "browser",
935
- globalName: plugin.globalName,
936
- minify: true,
937
- treeShaking: true
938
- });
939
- }
940
- }
941
1478
  let shouldClientHardReload;
942
1479
  {
943
1480
  const { shouldClientHardReload: doReload } = await buildLayouts();
@@ -947,24 +1484,33 @@ var build = async () => {
947
1484
  const { shouldClientHardReload: doReload } = await buildPages(path.resolve(DIST_DIR));
948
1485
  if (doReload) shouldClientHardReload = true;
949
1486
  }
1487
+ await shipModules();
950
1488
  const pagesBuilt = performance.now();
951
1489
  await buildClient(DIST_DIR);
952
1490
  const end = performance.now();
953
1491
  if (options.publicDirectory) {
954
- log("Recursively copying public directory.. this may take a while.");
1492
+ log2("Recursively copying public directory.. this may take a while.");
955
1493
  const src = path.relative(process.cwd(), options.publicDirectory.path);
956
- if (fs.existsSync(src) === false) {
1494
+ if (fs2.existsSync(src) === false) {
957
1495
  console.warn("WARNING: Public directory not found, an attempt will be made create it..");
958
- fs.mkdirSync(src, { recursive: true });
1496
+ fs2.mkdirSync(src, { recursive: true });
959
1497
  }
960
- await fs.promises.cp(src, path.join(DIST_DIR), { recursive: true });
1498
+ await fs2.promises.cp(src, path.join(DIST_DIR), { recursive: true });
961
1499
  }
962
1500
  {
963
- log(`Took ${Math.round(pagesBuilt - start)}ms to Build Pages.`);
964
- log(`Took ${Math.round(end - pagesBuilt)}ms to Build Client.`);
1501
+ log2(`Took ${Math.round(pagesBuilt - start)}ms to Build Pages.`);
1502
+ log2(`Took ${Math.round(end - pagesBuilt)}ms to Build Client.`);
1503
+ }
1504
+ if (options.server != void 0 && options.server.runServer == true) {
1505
+ startServer({
1506
+ root: options.server.root ?? DIST_DIR,
1507
+ environment: options.environment,
1508
+ port: options.server.port ?? 3e3,
1509
+ host: options.server.host ?? "localhost",
1510
+ DIST_DIR
1511
+ });
965
1512
  }
966
- process.send({ event: "message", data: "set-layouts", layouts: JSON.stringify(Array.from(__SERVER_CURRENT_LAYOUTS__)), currentLayouTId: __SERVER_CURRENT_LAYOUT_ID__ });
967
- process.send({ event: "message", data: "compile-finish" });
1513
+ process.send?.({ event: "message", data: "compile-finish" });
968
1514
  if (shouldClientHardReload) {
969
1515
  process.send({ event: "message", data: "hard-reload" });
970
1516
  } else {
@@ -981,5 +1527,8 @@ var build = async () => {
981
1527
  await build();
982
1528
  })();
983
1529
  export {
1530
+ LAYOUT_MAP2 as LAYOUT_MAP,
1531
+ PAGE_MAP,
1532
+ buildDynamicPage,
984
1533
  processPageElements
985
1534
  };