@blinkk/root 1.0.0-beta.3 → 1.0.0-beta.30

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/cli.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  htmlPretty,
4
4
  isValidTagName,
5
5
  parseTagNames
6
- } from "./chunk-WVRC46JG.js";
6
+ } from "./chunk-DXD5LKU3.js";
7
7
  import {
8
8
  copyGlob,
9
9
  createViteServer,
@@ -16,22 +16,125 @@ import {
16
16
  makeDir,
17
17
  rmDir,
18
18
  writeFile
19
- } from "./chunk-LTSJAEBG.js";
19
+ } from "./chunk-XGD3RBFM.js";
20
20
  import {
21
21
  configureServerPlugins,
22
22
  getVitePlugins
23
- } from "./chunk-DTEQ2AIW.js";
23
+ } from "./chunk-QKBMWK5B.js";
24
24
 
25
25
  // src/cli/commands/build.ts
26
26
  import path3 from "node:path";
27
27
  import { fileURLToPath } from "node:url";
28
28
  import fsExtra from "fs-extra";
29
+ import { dim, cyan } from "kleur/colors";
29
30
  import glob2 from "tiny-glob";
30
31
  import { build as viteBuild } from "vite";
31
32
 
32
- // src/render/asset-map/build-asset-map.ts
33
+ // src/node/element-graph.ts
33
34
  import fs from "node:fs";
34
35
  import path from "node:path";
36
+ import glob from "tiny-glob";
37
+ import { searchForWorkspaceRoot } from "vite";
38
+ var ElementGraph = class {
39
+ constructor(sourceFiles) {
40
+ this.sourceFiles = {};
41
+ this.deps = {};
42
+ this.sourceFiles = sourceFiles;
43
+ }
44
+ toJson() {
45
+ for (const tagName in this.sourceFiles) {
46
+ this.deps[tagName] ??= this.parseDepsFromSource(tagName);
47
+ }
48
+ return {
49
+ sourceFiles: this.sourceFiles,
50
+ deps: this.deps
51
+ };
52
+ }
53
+ static fromJson(data) {
54
+ const graph = new ElementGraph(data.sourceFiles);
55
+ graph.deps = data.deps;
56
+ return graph;
57
+ }
58
+ getDeps(tagName, visited) {
59
+ visited ??= /* @__PURE__ */ new Set();
60
+ if (visited.has(tagName)) {
61
+ return [];
62
+ }
63
+ visited.add(tagName);
64
+ this.deps[tagName] ??= this.parseDepsFromSource(tagName);
65
+ const deps = new Set(this.deps[tagName]);
66
+ for (const depTagName of this.deps[tagName]) {
67
+ for (const childDep of this.getDeps(depTagName, visited)) {
68
+ deps.add(childDep);
69
+ }
70
+ }
71
+ return Array.from(deps);
72
+ }
73
+ parseDepsFromSource(tagName) {
74
+ const srcFile = this.sourceFiles[tagName];
75
+ if (!srcFile) {
76
+ throw new Error(`could not find file path for tagName <${tagName}>`);
77
+ }
78
+ const src = fs.readFileSync(srcFile.filePath, "utf-8");
79
+ const tagNames = parseTagNames(src);
80
+ const deps = /* @__PURE__ */ new Set();
81
+ for (const depTagName of tagNames) {
82
+ if (depTagName !== tagName && depTagName in this.sourceFiles) {
83
+ deps.add(depTagName);
84
+ }
85
+ }
86
+ return Array.from(deps);
87
+ }
88
+ };
89
+ async function getElements(rootConfig) {
90
+ var _a;
91
+ const rootDir = rootConfig.rootDir;
92
+ const elementsDirs = getElementsDirs(rootConfig);
93
+ const excludePatterns = ((_a = rootConfig.elements) == null ? void 0 : _a.exclude) || [];
94
+ const excludeElement = (moduleId) => {
95
+ return excludePatterns.some((pattern) => Boolean(moduleId.match(pattern)));
96
+ };
97
+ const elementFilePaths = {};
98
+ for (const dirPath of elementsDirs) {
99
+ if (await isDirectory(dirPath)) {
100
+ const files = await glob("**/*", { cwd: dirPath });
101
+ files.forEach((file) => {
102
+ const parts = path.parse(file);
103
+ if (isJsFile(parts.base) && isValidTagName(parts.name)) {
104
+ const tagName = parts.name;
105
+ const filePath = path.join(dirPath, file);
106
+ const relPath = path.relative(rootDir, filePath);
107
+ if (!excludeElement(relPath)) {
108
+ elementFilePaths[tagName] = { filePath, relPath };
109
+ }
110
+ }
111
+ });
112
+ }
113
+ }
114
+ const graph = new ElementGraph(elementFilePaths);
115
+ return graph;
116
+ }
117
+ function getElementsDirs(rootConfig) {
118
+ var _a;
119
+ const rootDir = rootConfig.rootDir;
120
+ const workspaceRoot = searchForWorkspaceRoot(rootDir);
121
+ const elementsDirs = [path.join(rootDir, "elements")];
122
+ const elementsInclude = ((_a = rootConfig.elements) == null ? void 0 : _a.include) || [];
123
+ for (const dirPath of elementsInclude) {
124
+ const elementsDir = path.resolve(rootDir, dirPath);
125
+ if (!directoryContains(rootDir, elementsDir)) {
126
+ throw new Error(
127
+ `the elements dir (${dirPath}) should be within the project's workspace (${workspaceRoot})`
128
+ );
129
+ }
130
+ elementsDirs.push(elementsDir);
131
+ }
132
+ return elementsDirs;
133
+ }
134
+
135
+ // src/render/asset-map/build-asset-map.ts
136
+ import fs2 from "node:fs";
137
+ import path2 from "node:path";
35
138
  var BuildAssetMap = class {
36
139
  constructor(rootConfig) {
37
140
  this.rootConfig = rootConfig;
@@ -101,12 +204,12 @@ var BuildAssetMap = class {
101
204
  }
102
205
  };
103
206
  function realPathRelativeTo(rootDir, src) {
104
- const fullPath = path.resolve(rootDir, src);
105
- if (!fs.existsSync(fullPath)) {
207
+ const fullPath = path2.resolve(rootDir, src);
208
+ if (!fs2.existsSync(fullPath)) {
106
209
  return src;
107
210
  }
108
- const realpath = fs.realpathSync(path.resolve(rootDir, src));
109
- return path.relative(rootDir, realpath);
211
+ const realpath = fs2.realpathSync(path2.resolve(rootDir, src));
212
+ return path2.relative(rootDir, realpath);
110
213
  }
111
214
  var BuildAsset = class {
112
215
  constructor(assetMap, assetData) {
@@ -182,91 +285,6 @@ var BuildAsset = class {
182
285
  }
183
286
  };
184
287
 
185
- // src/cli/commands/build.ts
186
- import { dim, cyan } from "kleur/colors";
187
-
188
- // src/node/element-graph.ts
189
- import fs2 from "node:fs";
190
- import path2 from "node:path";
191
- import { searchForWorkspaceRoot } from "vite";
192
- import glob from "tiny-glob";
193
- var ElementGraph = class {
194
- constructor(sourceFiles) {
195
- this.sourceFiles = {};
196
- this.deps = {};
197
- this.sourceFiles = sourceFiles;
198
- }
199
- getDeps(tagName, visited) {
200
- visited ??= /* @__PURE__ */ new Set();
201
- if (visited.has(tagName)) {
202
- return [];
203
- }
204
- visited.add(tagName);
205
- this.deps[tagName] ??= this.parseDepsFromSource(tagName);
206
- const deps = new Set(this.deps[tagName]);
207
- for (const depTagName of this.deps[tagName]) {
208
- for (const childDep of this.getDeps(depTagName, visited)) {
209
- deps.add(childDep);
210
- }
211
- }
212
- return Array.from(deps);
213
- }
214
- parseDepsFromSource(tagName) {
215
- const srcFile = this.sourceFiles[tagName];
216
- if (!srcFile) {
217
- throw new Error(`could not find file path for tagName <${tagName}>`);
218
- }
219
- const src = fs2.readFileSync(srcFile.filePath, "utf-8");
220
- const tagNames = parseTagNames(src);
221
- const deps = /* @__PURE__ */ new Set();
222
- for (const depTagName of tagNames) {
223
- if (depTagName !== tagName && depTagName in this.sourceFiles) {
224
- deps.add(depTagName);
225
- }
226
- }
227
- return Array.from(deps);
228
- }
229
- };
230
- async function getElements(rootConfig) {
231
- var _a, _b;
232
- const rootDir = rootConfig.rootDir;
233
- const workspaceRoot = searchForWorkspaceRoot(rootDir);
234
- const elementsDirs = [path2.join(rootDir, "elements")];
235
- const elementsInclude = ((_a = rootConfig.elements) == null ? void 0 : _a.include) || [];
236
- const excludePatterns = ((_b = rootConfig.elements) == null ? void 0 : _b.exclude) || [];
237
- const excludeElement = (moduleId) => {
238
- return excludePatterns.some((pattern) => Boolean(moduleId.match(pattern)));
239
- };
240
- for (const dirPath of elementsInclude) {
241
- const elementsDir = path2.resolve(rootDir, dirPath);
242
- if (!directoryContains(rootDir, elementsDir)) {
243
- throw new Error(
244
- `the elements dir (${dirPath}) should be within the project's workspace (${workspaceRoot})`
245
- );
246
- }
247
- elementsDirs.push(elementsDir);
248
- }
249
- const elementFilePaths = {};
250
- for (const dirPath of elementsDirs) {
251
- if (await isDirectory(dirPath)) {
252
- const files = await glob("**/*", { cwd: dirPath });
253
- files.forEach((file) => {
254
- const parts = path2.parse(file);
255
- if (isJsFile(parts.base) && isValidTagName(parts.name)) {
256
- const tagName = parts.name;
257
- const filePath = path2.join(dirPath, file);
258
- const relPath = path2.relative(rootDir, filePath);
259
- if (!excludeElement(relPath)) {
260
- elementFilePaths[tagName] = { filePath, relPath };
261
- }
262
- }
263
- });
264
- }
265
- }
266
- const graph = new ElementGraph(elementFilePaths);
267
- return graph;
268
- }
269
-
270
288
  // src/cli/commands/build.ts
271
289
  var __dirname = path3.dirname(fileURLToPath(import.meta.url));
272
290
  async function build(rootProjectDir, options) {
@@ -354,7 +372,8 @@ async function build(rootProjectDir, options) {
354
372
  output: {
355
373
  format: "esm",
356
374
  chunkFileNames: "chunks/[name].[hash].min.js",
357
- assetFileNames: "assets/[name].[hash][extname]"
375
+ assetFileNames: "assets/[name].[hash][extname]",
376
+ sanitizeFileName
358
377
  }
359
378
  },
360
379
  outDir: path3.join(distDir, "server"),
@@ -385,6 +404,7 @@ async function build(rootProjectDir, options) {
385
404
  entryFileNames: "assets/[name].[hash].min.js",
386
405
  chunkFileNames: "chunks/[name].[hash].min.js",
387
406
  assetFileNames: "assets/[name].[hash][extname]",
407
+ sanitizeFileName,
388
408
  ...(_e = (_d = viteConfig == null ? void 0 : viteConfig.build) == null ? void 0 : _d.rollupOptions) == null ? void 0 : _e.output
389
409
  }
390
410
  },
@@ -414,6 +434,7 @@ async function build(rootProjectDir, options) {
414
434
  entryFileNames: "assets/[name].[hash].min.js",
415
435
  chunkFileNames: "chunks/[name].[hash].min.js",
416
436
  assetFileNames: "assets/[name].[hash][extname]",
437
+ sanitizeFileName,
417
438
  ...(_h = (_g = viteConfig == null ? void 0 : viteConfig.build) == null ? void 0 : _g.rollupOptions) == null ? void 0 : _h.output
418
439
  }
419
440
  },
@@ -432,9 +453,9 @@ async function build(rootProjectDir, options) {
432
453
  await writeFile(path3.join(distDir, "client/manifest.json"), "{}");
433
454
  }
434
455
  await copyGlob(
435
- "*.css",
436
- path3.join(distDir, "routes/assets"),
437
- path3.join(distDir, "client/assets")
456
+ "**/*.css",
457
+ path3.join(distDir, "routes"),
458
+ path3.join(distDir, "client")
438
459
  );
439
460
  const routesManifest = await loadJson(
440
461
  path3.join(distDir, "routes/manifest.json")
@@ -479,6 +500,11 @@ async function build(rootProjectDir, options) {
479
500
  path3.join(distDir, "client/root-manifest.json"),
480
501
  JSON.stringify(rootManifest, null, 2)
481
502
  );
503
+ const elementGraphJson = elementGraph.toJson();
504
+ await writeFile(
505
+ path3.join(distDir, "client/root-element-graph.json"),
506
+ JSON.stringify(elementGraphJson, null, 2)
507
+ );
482
508
  const buildDir = path3.join(distDir, "html");
483
509
  const publicDir = path3.join(rootDir, "public");
484
510
  if (fsExtra.existsSync(path3.join(rootDir, "public"))) {
@@ -486,8 +512,6 @@ async function build(rootProjectDir, options) {
486
512
  } else {
487
513
  await makeDir(buildDir);
488
514
  }
489
- await makeDir(path3.join(buildDir, "assets"));
490
- await makeDir(path3.join(buildDir, "chunks"));
491
515
  console.log("\njs/css output:");
492
516
  await Promise.all(
493
517
  Object.keys(rootManifest).map(async (src) => {
@@ -562,11 +586,24 @@ function printFileOutput(fileSize2, outputDir, outputFile) {
562
586
  `${indent}${dim(paddedSize)} ${dim(outputDir)}${cyan(outputFile)}`
563
587
  );
564
588
  }
589
+ function sanitizeFileName(name) {
590
+ return name.replaceAll("...", "").replaceAll("_", "").replaceAll("[", "").replaceAll("]", "").replace(/[^\x00-\x7F]/g, "").replace(/\x00/g, "").toLowerCase();
591
+ }
565
592
 
566
593
  // src/cli/commands/dev.ts
567
594
  import path5 from "node:path";
568
595
  import { fileURLToPath as fileURLToPath2 } from "node:url";
569
596
  import { default as express } from "express";
597
+ import { dim as dim2 } from "kleur/colors";
598
+ import glob3 from "tiny-glob";
599
+
600
+ // src/core/middleware.ts
601
+ function rootProjectMiddleware(options) {
602
+ return (req, _, next) => {
603
+ req.rootConfig = options.rootConfig;
604
+ next();
605
+ };
606
+ }
570
607
 
571
608
  // src/render/asset-map/dev-asset-map.ts
572
609
  import path4 from "node:path";
@@ -691,17 +728,6 @@ var DevServerAsset = class {
691
728
  }
692
729
  };
693
730
 
694
- // src/cli/commands/dev.ts
695
- import { dim as dim2 } from "kleur/colors";
696
-
697
- // src/core/middleware.ts
698
- function rootProjectMiddleware(options) {
699
- return (req, _, next) => {
700
- req.rootConfig = options.rootConfig;
701
- next();
702
- };
703
- }
704
-
705
731
  // src/utils/ports.ts
706
732
  import { createServer } from "node:net";
707
733
  function isPortOpen(port) {
@@ -740,20 +766,20 @@ async function findOpenPort(min, max) {
740
766
  }
741
767
 
742
768
  // src/cli/commands/dev.ts
743
- import glob3 from "tiny-glob";
744
769
  var __dirname2 = path5.dirname(fileURLToPath2(import.meta.url));
745
- async function dev(rootProjectDir) {
770
+ async function dev(rootProjectDir, options) {
746
771
  process.env.NODE_ENV = "development";
747
772
  const rootDir = path5.resolve(rootProjectDir || process.cwd());
748
773
  const defaultPort = parseInt(process.env.PORT || "4007");
774
+ const host = (options == null ? void 0 : options.host) || "localhost";
749
775
  const port = await findOpenPort(defaultPort, defaultPort + 10);
750
776
  console.log();
751
777
  console.log(`${dim2("\u2503")} project: ${rootDir}`);
752
- console.log(`${dim2("\u2503")} server: http://localhost:${port}`);
778
+ console.log(`${dim2("\u2503")} server: http://${host}:${port}`);
753
779
  console.log(`${dim2("\u2503")} mode: development`);
754
780
  console.log();
755
781
  const server = await createDevServer({ rootDir, port });
756
- server.listen(port);
782
+ server.listen(port, host);
757
783
  }
758
784
  async function createDevServer(options) {
759
785
  const rootDir = path5.resolve((options == null ? void 0 : options.rootDir) || process.cwd());
@@ -784,7 +810,7 @@ async function createDevServer(options) {
784
810
  async function viteServerMiddleware(options) {
785
811
  const rootConfig = options.rootConfig;
786
812
  const rootDir = rootConfig.rootDir;
787
- const elementGraph = await getElements(rootConfig);
813
+ let elementGraph = await getElements(rootConfig);
788
814
  const elements = Object.values(elementGraph.sourceFiles).map((sourceFile) => {
789
815
  return sourceFile.relPath;
790
816
  });
@@ -803,6 +829,21 @@ async function viteServerMiddleware(options) {
803
829
  port: options.port,
804
830
  optimizeDeps
805
831
  });
832
+ function isInElementsDir(changedFilePath) {
833
+ const filePath = path5.resolve(changedFilePath);
834
+ const elementsDirs = getElementsDirs(rootConfig);
835
+ return elementsDirs.some((dirPath) => filePath.startsWith(dirPath));
836
+ }
837
+ viteServer.watcher.on("add", async (filePath) => {
838
+ if (isInElementsDir(filePath)) {
839
+ elementGraph = await getElements(rootConfig);
840
+ }
841
+ });
842
+ viteServer.watcher.on("unlink", async (filePath) => {
843
+ if (isInElementsDir(filePath)) {
844
+ elementGraph = await getElements(rootConfig);
845
+ }
846
+ });
806
847
  return async (req, res, next) => {
807
848
  try {
808
849
  req.viteServer = viteServer;
@@ -871,21 +912,22 @@ function rootDevServer500Middleware() {
871
912
 
872
913
  // src/cli/commands/preview.ts
873
914
  import path6 from "node:path";
915
+ import compression from "compression";
874
916
  import { default as express2 } from "express";
875
917
  import { dim as dim3 } from "kleur/colors";
876
918
  import sirv from "sirv";
877
- import compression from "compression";
878
- async function preview(rootProjectDir) {
919
+ async function preview(rootProjectDir, options) {
879
920
  process.env.NODE_ENV = "development";
880
921
  const rootDir = path6.resolve(rootProjectDir || process.cwd());
881
922
  const server = await createPreviewServer({ rootDir });
882
923
  const port = parseInt(process.env.PORT || "4007");
924
+ const host = (options == null ? void 0 : options.host) || "localhost";
883
925
  console.log();
884
926
  console.log(`${dim3("\u2503")} project: ${rootDir}`);
885
- console.log(`${dim3("\u2503")} server: http://localhost:${port}`);
927
+ console.log(`${dim3("\u2503")} server: http://${host}:${port}`);
886
928
  console.log(`${dim3("\u2503")} mode: preview`);
887
929
  console.log();
888
- server.listen(port);
930
+ server.listen(port, host);
889
931
  }
890
932
  async function createPreviewServer(options) {
891
933
  const rootDir = options.rootDir;
@@ -925,9 +967,19 @@ async function rootPreviewRendererMiddleware(options) {
925
967
  `could not find ${manifestPath}. run \`root build\` before \`root preview\`.`
926
968
  );
927
969
  }
970
+ const elementGraphJsonPath = path6.join(
971
+ distDir,
972
+ "client/root-element-graph.json"
973
+ );
974
+ if (!await fileExists(elementGraphJsonPath)) {
975
+ throw new Error(
976
+ `could not find ${elementGraphJsonPath}. run \`root build\` before \`root start\`.`
977
+ );
978
+ }
928
979
  const rootManifest = await loadJson(manifestPath);
929
980
  const assetMap = BuildAssetMap.fromRootManifest(rootConfig, rootManifest);
930
- const elementGraph = await getElements(rootConfig);
981
+ const elementGraphJson = await loadJson(elementGraphJsonPath);
982
+ const elementGraph = ElementGraph.fromJson(elementGraphJson);
931
983
  const renderer = new render.Renderer(rootConfig, { assetMap, elementGraph });
932
984
  return async (req, _, next) => {
933
985
  req.renderer = renderer;
@@ -941,6 +993,8 @@ function rootPreviewServerMiddleware() {
941
993
  await renderer.handle(req, res, next);
942
994
  } catch (e) {
943
995
  try {
996
+ console.error(`error rendering ${req.originalUrl}`);
997
+ console.error(e.stack || e);
944
998
  const { html } = await renderer.renderError(e);
945
999
  res.status(500).set({ "Content-Type": "text/html" }).end(html);
946
1000
  } catch (e2) {
@@ -954,14 +1008,16 @@ function rootPreviewServerMiddleware() {
954
1008
  function rootPreviewServer404Middleware() {
955
1009
  return async (req, res) => {
956
1010
  console.error(`\u2753 404 ${req.originalUrl}`);
957
- const url = req.path;
958
- const ext = path6.extname(url);
959
- const renderer = req.renderer;
960
- if (!ext) {
961
- const data = await renderer.render404();
962
- const html = data.html || "";
963
- res.status(404).set({ "Content-Type": "text/html" }).end(html);
964
- return;
1011
+ if (req.renderer) {
1012
+ const url = req.path;
1013
+ const ext = path6.extname(url);
1014
+ if (!ext) {
1015
+ const renderer = req.renderer;
1016
+ const data = await renderer.renderDevServer404(req);
1017
+ const html = data.html || "";
1018
+ res.status(404).set({ "Content-Type": "text/html" }).end(html);
1019
+ return;
1020
+ }
965
1021
  }
966
1022
  res.status(404).set({ "Content-Type": "text/plain" }).end("404");
967
1023
  };
@@ -970,14 +1026,16 @@ function rootPreviewServer500Middleware() {
970
1026
  return async (err, req, res, next) => {
971
1027
  console.error(`\u2757 500 ${req.originalUrl}`);
972
1028
  console.error(String(err.stack || err));
973
- const url = req.path;
974
- const ext = path6.extname(url);
975
- const renderer = req.renderer;
976
- if (!ext) {
977
- const data = await renderer.renderError(err);
978
- const html = data.html || "";
979
- res.status(500).set({ "Content-Type": "text/html" }).end(html);
980
- return;
1029
+ if (req.renderer) {
1030
+ const url = req.path;
1031
+ const ext = path6.extname(url);
1032
+ if (!ext) {
1033
+ const renderer = req.renderer;
1034
+ const data = await renderer.renderDevServer500(req, err);
1035
+ const html = data.html || "";
1036
+ res.status(500).set({ "Content-Type": "text/html" }).end(html);
1037
+ return;
1038
+ }
981
1039
  }
982
1040
  next(err);
983
1041
  };
@@ -985,21 +1043,22 @@ function rootPreviewServer500Middleware() {
985
1043
 
986
1044
  // src/cli/commands/start.ts
987
1045
  import path7 from "node:path";
1046
+ import compression2 from "compression";
988
1047
  import { default as express3 } from "express";
989
1048
  import { dim as dim4 } from "kleur/colors";
990
1049
  import sirv2 from "sirv";
991
- import compression2 from "compression";
992
- async function start(rootProjectDir) {
1050
+ async function start(rootProjectDir, options) {
993
1051
  process.env.NODE_ENV = "production";
994
1052
  const rootDir = path7.resolve(rootProjectDir || process.cwd());
995
1053
  const server = await createProdServer({ rootDir });
996
1054
  const port = parseInt(process.env.PORT || "4007");
1055
+ const host = (options == null ? void 0 : options.host) || "localhost";
997
1056
  console.log();
998
1057
  console.log(`${dim4("\u2503")} project: ${rootDir}`);
999
- console.log(`${dim4("\u2503")} server: http://localhost:${port}`);
1058
+ console.log(`${dim4("\u2503")} server: http://${host}:${port}`);
1000
1059
  console.log(`${dim4("\u2503")} mode: production`);
1001
1060
  console.log();
1002
- server.listen(port);
1061
+ server.listen(port, host);
1003
1062
  }
1004
1063
  async function createProdServer(options) {
1005
1064
  const rootDir = options.rootDir;
@@ -1039,9 +1098,19 @@ async function rootProdRendererMiddleware(options) {
1039
1098
  `could not find ${manifestPath}. run \`root build\` before \`root start\`.`
1040
1099
  );
1041
1100
  }
1101
+ const elementGraphJsonPath = path7.join(
1102
+ distDir,
1103
+ "client/root-element-graph.json"
1104
+ );
1105
+ if (!await fileExists(elementGraphJsonPath)) {
1106
+ throw new Error(
1107
+ `could not find ${elementGraphJsonPath}. run \`root build\` before \`root start\`.`
1108
+ );
1109
+ }
1042
1110
  const rootManifest = await loadJson(manifestPath);
1043
1111
  const assetMap = BuildAssetMap.fromRootManifest(rootConfig, rootManifest);
1044
- const elementGraph = await getElements(rootConfig);
1112
+ const elementGraphJson = await loadJson(elementGraphJsonPath);
1113
+ const elementGraph = ElementGraph.fromJson(elementGraphJson);
1045
1114
  const renderer = new render.Renderer(rootConfig, { assetMap, elementGraph });
1046
1115
  return async (req, _, next) => {
1047
1116
  req.renderer = renderer;
@@ -1055,6 +1124,8 @@ function rootProdServerMiddleware() {
1055
1124
  await renderer.handle(req, res, next);
1056
1125
  } catch (e) {
1057
1126
  try {
1127
+ console.error(`error rendering ${req.originalUrl}`);
1128
+ console.error(e.stack || e);
1058
1129
  const { html } = await renderer.renderError(e);
1059
1130
  res.status(500).set({ "Content-Type": "text/html" }).end(html);
1060
1131
  } catch (e2) {