@blinkk/root 1.0.0-beta.2 → 1.0.0-beta.20

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-GGQGZ7ZE.js";
6
+ } from "./chunk-DXD5LKU3.js";
7
7
  import {
8
8
  copyGlob,
9
9
  createViteServer,
@@ -16,199 +16,45 @@ import {
16
16
  makeDir,
17
17
  rmDir,
18
18
  writeFile
19
- } from "./chunk-LTSJAEBG.js";
19
+ } from "./chunk-73FJH4HP.js";
20
20
  import {
21
21
  configureServerPlugins,
22
22
  getVitePlugins
23
- } from "./chunk-DTEQ2AIW.js";
23
+ } from "./chunk-QKBMWK5B.js";
24
24
 
25
- // src/cli/commands/dev.ts
25
+ // src/cli/commands/build.ts
26
26
  import path3 from "node:path";
27
27
  import { fileURLToPath } from "node:url";
28
- import { default as express } from "express";
29
-
30
- // src/render/asset-map/dev-asset-map.ts
31
- import path from "node:path";
32
- import { searchForWorkspaceRoot } from "vite";
33
- var DevServerAssetMap = class {
34
- constructor(rootConfig, moduleGraph) {
35
- this.rootConfig = rootConfig;
36
- this.moduleGraph = moduleGraph;
37
- }
38
- async get(src) {
39
- const file = path.resolve(this.rootConfig.rootDir, src);
40
- const viteModules = this.moduleGraph.getModulesByFile(file);
41
- if (viteModules && viteModules.size > 0) {
42
- const [viteModule] = viteModules;
43
- return new DevServerAsset(src, {
44
- assetMap: this,
45
- viteModule
46
- });
47
- }
48
- if (file.startsWith(this.rootConfig.rootDir)) {
49
- const assetUrl = file.slice(this.rootConfig.rootDir.length);
50
- return {
51
- src,
52
- assetUrl,
53
- getCssDeps: async () => [],
54
- getJsDeps: async () => [assetUrl]
55
- };
56
- }
57
- const workspaceRoot = searchForWorkspaceRoot(this.rootConfig.rootDir);
58
- if (await directoryContains(workspaceRoot, file)) {
59
- const assetUrl = `/@fs/${file}`;
60
- return {
61
- src,
62
- assetUrl,
63
- getCssDeps: async () => [],
64
- getJsDeps: async () => [assetUrl]
65
- };
66
- }
67
- console.log(`could not find asset in asset map: ${src}`);
68
- return null;
69
- }
70
- filePathToSrc(file) {
71
- return path.relative(this.rootConfig.rootDir, file);
72
- }
73
- };
74
- var DevServerAsset = class {
75
- constructor(src, options) {
76
- this.src = src;
77
- this.assetMap = options.assetMap;
78
- this.viteModule = options.viteModule;
79
- this.moduleId = this.viteModule.id;
80
- this.assetUrl = this.viteModule.url;
81
- }
82
- async getCssDeps() {
83
- const visited = /* @__PURE__ */ new Set();
84
- const deps = /* @__PURE__ */ new Set();
85
- this.collectCss(this, deps, visited);
86
- return Array.from(deps);
87
- }
88
- async getJsDeps() {
89
- const visited = /* @__PURE__ */ new Set();
90
- const deps = /* @__PURE__ */ new Set();
91
- this.collectJs(this, deps, visited);
92
- return Array.from(deps);
93
- }
94
- getImportedModules() {
95
- return this.viteModule.importedModules;
96
- }
97
- collectJs(asset, urls, visited) {
98
- if (!asset) {
99
- return;
100
- }
101
- if (!asset.moduleId) {
102
- return;
103
- }
104
- if (visited.has(asset.moduleId)) {
105
- return;
106
- }
107
- visited.add(asset.moduleId);
108
- const parts = path.parse(asset.assetUrl);
109
- if ([".js", ".jsx", ".ts", ".tsx"].includes(parts.ext) && asset.moduleId.includes("/elements/")) {
110
- urls.add(asset.assetUrl);
111
- }
112
- asset.getImportedModules().forEach((viteModule) => {
113
- if (viteModule.file) {
114
- const src = this.assetMap.filePathToSrc(viteModule.file);
115
- const importedAsset = new DevServerAsset(src, {
116
- assetMap: this.assetMap,
117
- viteModule
118
- });
119
- this.collectJs(importedAsset, urls, visited);
120
- }
121
- });
122
- }
123
- collectCss(asset, urls, visited) {
124
- if (!asset) {
125
- return;
126
- }
127
- if (!asset.assetUrl) {
128
- return;
129
- }
130
- if (visited.has(asset.assetUrl)) {
131
- return;
132
- }
133
- visited.add(asset.assetUrl);
134
- if (asset.src.endsWith(".scss")) {
135
- const parts = path.parse(asset.src);
136
- if (!parts.name.startsWith("_")) {
137
- urls.add(asset.assetUrl);
138
- }
139
- }
140
- asset.getImportedModules().forEach((viteModule) => {
141
- if (viteModule.file) {
142
- const src = this.assetMap.filePathToSrc(viteModule.file);
143
- const importedAsset = new DevServerAsset(src, {
144
- assetMap: this.assetMap,
145
- viteModule
146
- });
147
- this.collectCss(importedAsset, urls, visited);
148
- }
149
- });
150
- }
151
- };
152
-
153
- // src/cli/commands/dev.ts
154
- import { dim } from "kleur/colors";
155
-
156
- // src/core/middleware.ts
157
- function rootProjectMiddleware(options) {
158
- return (req, _, next) => {
159
- req.rootConfig = options.rootConfig;
160
- next();
161
- };
162
- }
163
-
164
- // src/utils/ports.ts
165
- import { createServer } from "node:net";
166
- function isPortOpen(port) {
167
- return new Promise((resolve, reject) => {
168
- const server = createServer();
169
- server.on("error", (err) => {
170
- if (err.code === "EADDRINUSE") {
171
- resolve(false);
172
- return;
173
- }
174
- reject(err);
175
- });
176
- server.on("close", () => {
177
- resolve(true);
178
- });
179
- server.listen(port, () => {
180
- server.close((err) => {
181
- if (err) {
182
- console.log(`error closing server: ${err}`);
183
- reject(err);
184
- }
185
- });
186
- });
187
- });
188
- }
189
- async function findOpenPort(min, max) {
190
- let port = min;
191
- while (port <= max) {
192
- const isOpen = await isPortOpen(port);
193
- if (isOpen) {
194
- return port;
195
- }
196
- port += 1;
197
- }
198
- throw new Error(`no ports open between ${min} and ${max}`);
199
- }
28
+ import fsExtra from "fs-extra";
29
+ import { dim, cyan } from "kleur/colors";
30
+ import glob2 from "tiny-glob";
31
+ import { build as viteBuild } from "vite";
200
32
 
201
33
  // src/node/element-graph.ts
202
34
  import fs from "node:fs";
203
- import path2 from "node:path";
204
- import { searchForWorkspaceRoot as searchForWorkspaceRoot2 } from "vite";
35
+ import path from "node:path";
205
36
  import glob from "tiny-glob";
37
+ import { searchForWorkspaceRoot } from "vite";
206
38
  var ElementGraph = class {
207
39
  constructor(sourceFiles) {
208
40
  this.sourceFiles = {};
209
41
  this.deps = {};
210
42
  this.sourceFiles = sourceFiles;
211
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
+ }
212
58
  getDeps(tagName, visited) {
213
59
  visited ??= /* @__PURE__ */ new Set();
214
60
  if (visited.has(tagName)) {
@@ -243,15 +89,15 @@ var ElementGraph = class {
243
89
  async function getElements(rootConfig) {
244
90
  var _a, _b;
245
91
  const rootDir = rootConfig.rootDir;
246
- const workspaceRoot = searchForWorkspaceRoot2(rootDir);
247
- const elementsDirs = [path2.join(rootDir, "elements")];
92
+ const workspaceRoot = searchForWorkspaceRoot(rootDir);
93
+ const elementsDirs = [path.join(rootDir, "elements")];
248
94
  const elementsInclude = ((_a = rootConfig.elements) == null ? void 0 : _a.include) || [];
249
95
  const excludePatterns = ((_b = rootConfig.elements) == null ? void 0 : _b.exclude) || [];
250
96
  const excludeElement = (moduleId) => {
251
97
  return excludePatterns.some((pattern) => Boolean(moduleId.match(pattern)));
252
98
  };
253
99
  for (const dirPath of elementsInclude) {
254
- const elementsDir = path2.resolve(rootDir, dirPath);
100
+ const elementsDir = path.resolve(rootDir, dirPath);
255
101
  if (!directoryContains(rootDir, elementsDir)) {
256
102
  throw new Error(
257
103
  `the elements dir (${dirPath}) should be within the project's workspace (${workspaceRoot})`
@@ -264,11 +110,11 @@ async function getElements(rootConfig) {
264
110
  if (await isDirectory(dirPath)) {
265
111
  const files = await glob("**/*", { cwd: dirPath });
266
112
  files.forEach((file) => {
267
- const parts = path2.parse(file);
113
+ const parts = path.parse(file);
268
114
  if (isJsFile(parts.base) && isValidTagName(parts.name)) {
269
115
  const tagName = parts.name;
270
- const filePath = path2.join(dirPath, file);
271
- const relPath = path2.relative(rootDir, filePath);
116
+ const filePath = path.join(dirPath, file);
117
+ const relPath = path.relative(rootDir, filePath);
272
118
  if (!excludeElement(relPath)) {
273
119
  elementFilePaths[tagName] = { filePath, relPath };
274
120
  }
@@ -280,146 +126,9 @@ async function getElements(rootConfig) {
280
126
  return graph;
281
127
  }
282
128
 
283
- // src/cli/commands/dev.ts
284
- import glob2 from "tiny-glob";
285
- var __dirname = path3.dirname(fileURLToPath(import.meta.url));
286
- async function dev(rootProjectDir) {
287
- process.env.NODE_ENV = "development";
288
- const rootDir = path3.resolve(rootProjectDir || process.cwd());
289
- const defaultPort = parseInt(process.env.PORT || "4007");
290
- const port = await findOpenPort(defaultPort, defaultPort + 10);
291
- console.log();
292
- console.log(`${dim("\u2503")} project: ${rootDir}`);
293
- console.log(`${dim("\u2503")} server: http://localhost:${port}`);
294
- console.log(`${dim("\u2503")} mode: development`);
295
- console.log();
296
- const server = await createServer2({ rootDir, port });
297
- server.listen(port);
298
- }
299
- async function createServer2(options) {
300
- const rootDir = path3.resolve((options == null ? void 0 : options.rootDir) || process.cwd());
301
- const rootConfig = await loadRootConfig(rootDir, { command: "dev" });
302
- const port = options == null ? void 0 : options.port;
303
- const server = express();
304
- server.disable("x-powered-by");
305
- server.use(rootProjectMiddleware({ rootConfig }));
306
- server.use(await viteServerMiddleware({ rootConfig, port }));
307
- const plugins = rootConfig.plugins || [];
308
- await configureServerPlugins(
309
- server,
310
- async () => {
311
- var _a;
312
- const userMiddlewares = ((_a = rootConfig.server) == null ? void 0 : _a.middlewares) || [];
313
- for (const middleware of userMiddlewares) {
314
- server.use(middleware);
315
- }
316
- server.use(rootDevServerMiddleware());
317
- server.use(rootDevServer404Middleware());
318
- server.use(rootDevServer500Middleware());
319
- },
320
- plugins,
321
- { type: "dev", rootConfig }
322
- );
323
- return server;
324
- }
325
- async function viteServerMiddleware(options) {
326
- const rootConfig = options.rootConfig;
327
- const rootDir = rootConfig.rootDir;
328
- const elementGraph = await getElements(rootConfig);
329
- const elements = Object.values(elementGraph.sourceFiles).map((sourceFile) => {
330
- return sourceFile.relPath;
331
- });
332
- const bundleScripts = [];
333
- if (await isDirectory(path3.join(rootDir, "bundles"))) {
334
- const bundleFiles = await glob2("bundles/*", { cwd: rootDir });
335
- bundleFiles.forEach((file) => {
336
- const parts = path3.parse(file);
337
- if (isJsFile(parts.base)) {
338
- bundleScripts.push(file);
339
- }
340
- });
341
- }
342
- const optimizeDeps = [...elements, ...bundleScripts];
343
- const viteServer = await createViteServer(rootConfig, {
344
- port: options.port,
345
- optimizeDeps
346
- });
347
- return async (req, res, next) => {
348
- try {
349
- req.viteServer = viteServer;
350
- const renderModulePath = path3.resolve(__dirname, "./render.js");
351
- const render = await viteServer.ssrLoadModule(
352
- renderModulePath
353
- );
354
- const assetMap = new DevServerAssetMap(
355
- rootConfig,
356
- viteServer.moduleGraph
357
- );
358
- req.renderer = new render.Renderer(rootConfig, { assetMap, elementGraph });
359
- viteServer.middlewares(req, res, next);
360
- } catch (e) {
361
- next(e);
362
- }
363
- };
364
- }
365
- function rootDevServerMiddleware() {
366
- return async (req, res, next) => {
367
- const renderer = req.renderer;
368
- const viteServer = req.viteServer;
369
- try {
370
- await renderer.handle(req, res, next);
371
- } catch (err) {
372
- viteServer.ssrFixStacktrace(err);
373
- next(err);
374
- }
375
- };
376
- }
377
- function rootDevServer404Middleware() {
378
- return async (req, res) => {
379
- console.error(`\u2753 404 ${req.originalUrl}`);
380
- if (req.renderer) {
381
- const url = req.path;
382
- const ext = path3.extname(url);
383
- if (!ext) {
384
- const renderer = req.renderer;
385
- const data = await renderer.renderDevServer404(req);
386
- const html = data.html || "";
387
- res.status(404).set({ "Content-Type": "text/html" }).end(html);
388
- return;
389
- }
390
- }
391
- res.status(404).set({ "Content-Type": "text/plain" }).end("404");
392
- };
393
- }
394
- function rootDevServer500Middleware() {
395
- return async (err, req, res, next) => {
396
- console.error(`\u2757 500 ${req.originalUrl}`);
397
- console.error(String(err.stack || err));
398
- if (req.renderer) {
399
- const url = req.path;
400
- const ext = path3.extname(url);
401
- if (!ext) {
402
- const renderer = req.renderer;
403
- const data = await renderer.renderDevServer500(req, err);
404
- const html = data.html || "";
405
- res.status(500).set({ "Content-Type": "text/html" }).end(html);
406
- return;
407
- }
408
- }
409
- next(err);
410
- };
411
- }
412
-
413
- // src/cli/commands/build.ts
414
- import path5 from "node:path";
415
- import { fileURLToPath as fileURLToPath2 } from "node:url";
416
- import fsExtra from "fs-extra";
417
- import glob3 from "tiny-glob";
418
- import { build as viteBuild } from "vite";
419
-
420
129
  // src/render/asset-map/build-asset-map.ts
421
130
  import fs2 from "node:fs";
422
- import path4 from "node:path";
131
+ import path2 from "node:path";
423
132
  var BuildAssetMap = class {
424
133
  constructor(rootConfig) {
425
134
  this.rootConfig = rootConfig;
@@ -489,12 +198,12 @@ var BuildAssetMap = class {
489
198
  }
490
199
  };
491
200
  function realPathRelativeTo(rootDir, src) {
492
- const fullPath = path4.resolve(rootDir, src);
201
+ const fullPath = path2.resolve(rootDir, src);
493
202
  if (!fs2.existsSync(fullPath)) {
494
203
  return src;
495
204
  }
496
- const realpath = fs2.realpathSync(path4.resolve(rootDir, src));
497
- return path4.relative(rootDir, realpath);
205
+ const realpath = fs2.realpathSync(path2.resolve(rootDir, src));
206
+ return path2.relative(rootDir, realpath);
498
207
  }
499
208
  var BuildAsset = class {
500
209
  constructor(assetMap, assetData) {
@@ -571,30 +280,29 @@ var BuildAsset = class {
571
280
  };
572
281
 
573
282
  // src/cli/commands/build.ts
574
- import { dim as dim2, cyan } from "kleur/colors";
575
- var __dirname2 = path5.dirname(fileURLToPath2(import.meta.url));
283
+ var __dirname = path3.dirname(fileURLToPath(import.meta.url));
576
284
  async function build(rootProjectDir, options) {
577
285
  var _a, _b, _c, _d, _e, _f, _g, _h;
578
286
  const mode = (options == null ? void 0 : options.mode) || "production";
579
287
  process.env.NODE_ENV = mode;
580
- const rootDir = path5.resolve(rootProjectDir || process.cwd());
288
+ const rootDir = path3.resolve(rootProjectDir || process.cwd());
581
289
  const rootConfig = await loadRootConfig(rootDir, { command: "build" });
582
- const distDir = path5.join(rootDir, "dist");
290
+ const distDir = path3.join(rootDir, "dist");
583
291
  const ssrOnly = (options == null ? void 0 : options.ssrOnly) || false;
584
292
  console.log();
585
- console.log(`${dim2("\u2503")} project: ${rootDir}`);
586
- console.log(`${dim2("\u2503")} output: ${distDir}/html`);
587
- console.log(`${dim2("\u2503")} mode: ${mode}`);
293
+ console.log(`${dim("\u2503")} project: ${rootDir}`);
294
+ console.log(`${dim("\u2503")} output: ${distDir}/html`);
295
+ console.log(`${dim("\u2503")} mode: ${mode}`);
588
296
  console.log();
589
297
  await rmDir(distDir);
590
298
  await makeDir(distDir);
591
299
  const routeFiles = [];
592
- if (await isDirectory(path5.join(rootDir, "routes"))) {
593
- const pageFiles = await glob3("routes/**/*", { cwd: rootDir });
300
+ if (await isDirectory(path3.join(rootDir, "routes"))) {
301
+ const pageFiles = await glob2("routes/**/*", { cwd: rootDir });
594
302
  pageFiles.forEach((file) => {
595
- const parts = path5.parse(file);
303
+ const parts = path3.parse(file);
596
304
  if (!parts.name.startsWith("_") && isJsFile(parts.base)) {
597
- routeFiles.push(path5.resolve(rootDir, file));
305
+ routeFiles.push(path3.resolve(rootDir, file));
598
306
  }
599
307
  });
600
308
  }
@@ -603,12 +311,12 @@ async function build(rootProjectDir, options) {
603
311
  return sourceFile.filePath;
604
312
  });
605
313
  const bundleScripts = [];
606
- if (await isDirectory(path5.join(rootDir, "bundles"))) {
607
- const bundleFiles = await glob3("bundles/*", { cwd: rootDir });
314
+ if (await isDirectory(path3.join(rootDir, "bundles"))) {
315
+ const bundleFiles = await glob2("bundles/*", { cwd: rootDir });
608
316
  bundleFiles.forEach((file) => {
609
- const parts = path5.parse(file);
317
+ const parts = path3.parse(file);
610
318
  if (isJsFile(parts.base)) {
611
- bundleScripts.push(path5.resolve(rootDir, file));
319
+ bundleScripts.push(path3.resolve(rootDir, file));
612
320
  }
613
321
  });
614
322
  }
@@ -631,7 +339,7 @@ async function build(rootProjectDir, options) {
631
339
  plugins: vitePlugins
632
340
  };
633
341
  const ssrInput = {
634
- render: path5.resolve(__dirname2, "./render.js")
342
+ render: path3.resolve(__dirname, "./render.js")
635
343
  };
636
344
  rootPlugins.forEach((plugin) => {
637
345
  if (plugin.ssrInput) {
@@ -661,7 +369,7 @@ async function build(rootProjectDir, options) {
661
369
  assetFileNames: "assets/[name].[hash][extname]"
662
370
  }
663
371
  },
664
- outDir: path5.join(distDir, "server"),
372
+ outDir: path3.join(distDir, "server"),
665
373
  ssr: true,
666
374
  ssrManifest: false,
667
375
  cssCodeSplit: true,
@@ -692,7 +400,7 @@ async function build(rootProjectDir, options) {
692
400
  ...(_e = (_d = viteConfig == null ? void 0 : viteConfig.build) == null ? void 0 : _d.rollupOptions) == null ? void 0 : _e.output
693
401
  }
694
402
  },
695
- outDir: path5.join(distDir, "routes"),
403
+ outDir: path3.join(distDir, "routes"),
696
404
  ssr: false,
697
405
  ssrManifest: false,
698
406
  manifest: true,
@@ -721,7 +429,7 @@ async function build(rootProjectDir, options) {
721
429
  ...(_h = (_g = viteConfig == null ? void 0 : viteConfig.build) == null ? void 0 : _g.rollupOptions) == null ? void 0 : _h.output
722
430
  }
723
431
  },
724
- outDir: path5.join(distDir, "client"),
432
+ outDir: path3.join(distDir, "client"),
725
433
  ssr: false,
726
434
  ssrManifest: false,
727
435
  manifest: true,
@@ -733,18 +441,18 @@ async function build(rootProjectDir, options) {
733
441
  }
734
442
  });
735
443
  } else {
736
- await writeFile(path5.join(distDir, "client/manifest.json"), "{}");
444
+ await writeFile(path3.join(distDir, "client/manifest.json"), "{}");
737
445
  }
738
446
  await copyGlob(
739
447
  "*.css",
740
- path5.join(distDir, "routes/assets"),
741
- path5.join(distDir, "client/assets")
448
+ path3.join(distDir, "routes/assets"),
449
+ path3.join(distDir, "client/assets")
742
450
  );
743
451
  const routesManifest = await loadJson(
744
- path5.join(distDir, "routes/manifest.json")
452
+ path3.join(distDir, "routes/manifest.json")
745
453
  );
746
454
  const clientManifest = await loadJson(
747
- path5.join(distDir, "client/manifest.json")
455
+ path3.join(distDir, "client/manifest.json")
748
456
  );
749
457
  function collectRouteCss(asset, cssDeps, visited) {
750
458
  if (!asset || !asset.file || visited.has(asset.file)) {
@@ -780,112 +488,423 @@ async function build(rootProjectDir, options) {
780
488
  );
781
489
  const rootManifest = assetMap.toJson();
782
490
  await writeFile(
783
- path5.join(distDir, "client/root-manifest.json"),
491
+ path3.join(distDir, "client/root-manifest.json"),
784
492
  JSON.stringify(rootManifest, null, 2)
785
493
  );
786
- const buildDir = path5.join(distDir, "html");
787
- const publicDir = path5.join(rootDir, "public");
788
- if (fsExtra.existsSync(path5.join(rootDir, "public"))) {
494
+ const elementGraphJson = elementGraph.toJson();
495
+ await writeFile(
496
+ path3.join(distDir, "client/root-element-graph.json"),
497
+ JSON.stringify(elementGraphJson, null, 2)
498
+ );
499
+ const buildDir = path3.join(distDir, "html");
500
+ const publicDir = path3.join(rootDir, "public");
501
+ if (fsExtra.existsSync(path3.join(rootDir, "public"))) {
789
502
  fsExtra.copySync(publicDir, buildDir, { dereference: true });
790
503
  } else {
791
504
  await makeDir(buildDir);
792
505
  }
793
- await makeDir(path5.join(buildDir, "assets"));
794
- await makeDir(path5.join(buildDir, "chunks"));
506
+ await makeDir(path3.join(buildDir, "assets"));
507
+ await makeDir(path3.join(buildDir, "chunks"));
795
508
  console.log("\njs/css output:");
796
509
  await Promise.all(
797
510
  Object.keys(rootManifest).map(async (src) => {
798
511
  if (isRouteFile(src)) {
799
512
  return;
800
513
  }
801
- const assetData = rootManifest[src];
802
- if (!assetData.assetUrl) {
803
- return;
514
+ const assetData = rootManifest[src];
515
+ if (!assetData.assetUrl) {
516
+ return;
517
+ }
518
+ const assetRelPath = assetData.assetUrl.slice(1);
519
+ const assetFrom = path3.join(distDir, "client", assetRelPath);
520
+ const assetTo = path3.join(buildDir, assetRelPath);
521
+ if (!await fileExists(assetFrom)) {
522
+ console.log(`${assetFrom} does not exist`);
523
+ return;
524
+ }
525
+ await fsExtra.copy(assetFrom, assetTo);
526
+ printFileOutput(fileSize(assetTo), "dist/html/", assetRelPath);
527
+ })
528
+ );
529
+ if (!ssrOnly) {
530
+ const render = await import(path3.join(distDir, "server/render.js"));
531
+ const renderer = new render.Renderer(rootConfig, { assetMap, elementGraph });
532
+ const sitemap = await renderer.getSitemap();
533
+ console.log("\nhtml output:");
534
+ await Promise.all(
535
+ Object.keys(sitemap).map(async (urlPath) => {
536
+ const { route, params } = sitemap[urlPath];
537
+ const data = await renderer.renderRoute(route, {
538
+ routeParams: params
539
+ });
540
+ if (data.notFound) {
541
+ return;
542
+ }
543
+ let outFilePath = path3.join(urlPath.slice(1), "index.html");
544
+ if (outFilePath.endsWith("404/index.html")) {
545
+ outFilePath = outFilePath.replace("404/index.html", "404.html");
546
+ }
547
+ const outPath = path3.join(buildDir, outFilePath);
548
+ let html = data.html || "";
549
+ if (rootConfig.prettyHtml !== false) {
550
+ html = await htmlPretty(html, rootConfig.prettyHtmlOptions);
551
+ }
552
+ if (rootConfig.minifyHtml !== false) {
553
+ html = await htmlMinify(html, rootConfig.minifyHtmlOptions);
554
+ }
555
+ await writeFile(outPath, html);
556
+ printFileOutput(fileSize(outPath), "dist/html/", outFilePath);
557
+ })
558
+ );
559
+ }
560
+ }
561
+ function isRouteFile(filepath) {
562
+ return filepath.startsWith("routes") && isJsFile(filepath);
563
+ }
564
+ function fileSize(filepath) {
565
+ const stats = fsExtra.statSync(filepath);
566
+ const bytes = stats.size;
567
+ const k = 1024;
568
+ if (bytes < k) {
569
+ return (bytes / k).toFixed(2) + " kB";
570
+ }
571
+ const units = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
572
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
573
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + units[i];
574
+ }
575
+ function printFileOutput(fileSize2, outputDir, outputFile) {
576
+ const indent = " ".repeat(2);
577
+ const paddedSize = fileSize2.padStart(9, " ");
578
+ console.log(
579
+ `${indent}${dim(paddedSize)} ${dim(outputDir)}${cyan(outputFile)}`
580
+ );
581
+ }
582
+
583
+ // src/cli/commands/dev.ts
584
+ import path5 from "node:path";
585
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
586
+ import { default as express } from "express";
587
+ import { dim as dim2 } from "kleur/colors";
588
+ import glob3 from "tiny-glob";
589
+
590
+ // src/core/middleware.ts
591
+ function rootProjectMiddleware(options) {
592
+ return (req, _, next) => {
593
+ req.rootConfig = options.rootConfig;
594
+ next();
595
+ };
596
+ }
597
+
598
+ // src/render/asset-map/dev-asset-map.ts
599
+ import path4 from "node:path";
600
+ import { searchForWorkspaceRoot as searchForWorkspaceRoot2 } from "vite";
601
+ var DevServerAssetMap = class {
602
+ constructor(rootConfig, moduleGraph) {
603
+ this.rootConfig = rootConfig;
604
+ this.moduleGraph = moduleGraph;
605
+ }
606
+ async get(src) {
607
+ const file = path4.resolve(this.rootConfig.rootDir, src);
608
+ const viteModules = this.moduleGraph.getModulesByFile(file);
609
+ if (viteModules && viteModules.size > 0) {
610
+ const [viteModule] = viteModules;
611
+ return new DevServerAsset(src, {
612
+ assetMap: this,
613
+ viteModule
614
+ });
615
+ }
616
+ if (file.startsWith(this.rootConfig.rootDir)) {
617
+ const assetUrl = file.slice(this.rootConfig.rootDir.length);
618
+ return {
619
+ src,
620
+ assetUrl,
621
+ getCssDeps: async () => [],
622
+ getJsDeps: async () => [assetUrl]
623
+ };
624
+ }
625
+ const workspaceRoot = searchForWorkspaceRoot2(this.rootConfig.rootDir);
626
+ if (await directoryContains(workspaceRoot, file)) {
627
+ const assetUrl = `/@fs/${file}`;
628
+ return {
629
+ src,
630
+ assetUrl,
631
+ getCssDeps: async () => [],
632
+ getJsDeps: async () => [assetUrl]
633
+ };
634
+ }
635
+ console.log(`could not find asset in asset map: ${src}`);
636
+ return null;
637
+ }
638
+ filePathToSrc(file) {
639
+ return path4.relative(this.rootConfig.rootDir, file);
640
+ }
641
+ };
642
+ var DevServerAsset = class {
643
+ constructor(src, options) {
644
+ this.src = src;
645
+ this.assetMap = options.assetMap;
646
+ this.viteModule = options.viteModule;
647
+ this.moduleId = this.viteModule.id;
648
+ this.assetUrl = this.viteModule.url;
649
+ }
650
+ async getCssDeps() {
651
+ const visited = /* @__PURE__ */ new Set();
652
+ const deps = /* @__PURE__ */ new Set();
653
+ this.collectCss(this, deps, visited);
654
+ return Array.from(deps);
655
+ }
656
+ async getJsDeps() {
657
+ const visited = /* @__PURE__ */ new Set();
658
+ const deps = /* @__PURE__ */ new Set();
659
+ this.collectJs(this, deps, visited);
660
+ return Array.from(deps);
661
+ }
662
+ getImportedModules() {
663
+ return this.viteModule.importedModules;
664
+ }
665
+ collectJs(asset, urls, visited) {
666
+ if (!asset) {
667
+ return;
668
+ }
669
+ if (!asset.moduleId) {
670
+ return;
671
+ }
672
+ if (visited.has(asset.moduleId)) {
673
+ return;
674
+ }
675
+ visited.add(asset.moduleId);
676
+ const parts = path4.parse(asset.assetUrl);
677
+ if ([".js", ".jsx", ".ts", ".tsx"].includes(parts.ext) && asset.moduleId.includes("/elements/")) {
678
+ urls.add(asset.assetUrl);
679
+ }
680
+ asset.getImportedModules().forEach((viteModule) => {
681
+ if (viteModule.file) {
682
+ const src = this.assetMap.filePathToSrc(viteModule.file);
683
+ const importedAsset = new DevServerAsset(src, {
684
+ assetMap: this.assetMap,
685
+ viteModule
686
+ });
687
+ this.collectJs(importedAsset, urls, visited);
688
+ }
689
+ });
690
+ }
691
+ collectCss(asset, urls, visited) {
692
+ if (!asset) {
693
+ return;
694
+ }
695
+ if (!asset.assetUrl) {
696
+ return;
697
+ }
698
+ if (visited.has(asset.assetUrl)) {
699
+ return;
700
+ }
701
+ visited.add(asset.assetUrl);
702
+ if (asset.src.endsWith(".scss")) {
703
+ const parts = path4.parse(asset.src);
704
+ if (!parts.name.startsWith("_")) {
705
+ urls.add(asset.assetUrl);
706
+ }
707
+ }
708
+ asset.getImportedModules().forEach((viteModule) => {
709
+ if (viteModule.file) {
710
+ const src = this.assetMap.filePathToSrc(viteModule.file);
711
+ const importedAsset = new DevServerAsset(src, {
712
+ assetMap: this.assetMap,
713
+ viteModule
714
+ });
715
+ this.collectCss(importedAsset, urls, visited);
804
716
  }
805
- const assetRelPath = assetData.assetUrl.slice(1);
806
- const assetFrom = path5.join(distDir, "client", assetRelPath);
807
- const assetTo = path5.join(buildDir, assetRelPath);
808
- if (!await fileExists(assetFrom)) {
809
- console.log(`${assetFrom} does not exist`);
717
+ });
718
+ }
719
+ };
720
+
721
+ // src/utils/ports.ts
722
+ import { createServer } from "node:net";
723
+ function isPortOpen(port) {
724
+ return new Promise((resolve, reject) => {
725
+ const server = createServer();
726
+ server.on("error", (err) => {
727
+ if (err.code === "EADDRINUSE") {
728
+ resolve(false);
810
729
  return;
811
730
  }
812
- await fsExtra.copy(assetFrom, assetTo);
813
- printFileOutput(fileSize(assetTo), "dist/html/", assetRelPath);
814
- })
815
- );
816
- if (!ssrOnly) {
817
- const render = await import(path5.join(distDir, "server/render.js"));
818
- const renderer = new render.Renderer(rootConfig, { assetMap, elementGraph });
819
- const sitemap = await renderer.getSitemap();
820
- console.log("\nhtml output:");
821
- await Promise.all(
822
- Object.keys(sitemap).map(async (urlPath) => {
823
- const { route, params } = sitemap[urlPath];
824
- const data = await renderer.renderRoute(route, {
825
- routeParams: params
826
- });
827
- if (data.notFound) {
828
- return;
829
- }
830
- let outFilePath = path5.join(urlPath.slice(1), "index.html");
831
- if (outFilePath.endsWith("404/index.html")) {
832
- outFilePath = outFilePath.replace("404/index.html", "404.html");
833
- }
834
- const outPath = path5.join(buildDir, outFilePath);
835
- let html = data.html || "";
836
- if (rootConfig.prettyHtml !== false) {
837
- html = await htmlPretty(html, rootConfig.prettyHtmlOptions);
838
- }
839
- if (rootConfig.minifyHtml !== false) {
840
- html = await htmlMinify(html, rootConfig.minifyHtmlOptions);
731
+ reject(err);
732
+ });
733
+ server.on("close", () => {
734
+ resolve(true);
735
+ });
736
+ server.listen(port, () => {
737
+ server.close((err) => {
738
+ if (err) {
739
+ console.log(`error closing server: ${err}`);
740
+ reject(err);
841
741
  }
842
- await writeFile(outPath, html);
843
- printFileOutput(fileSize(outPath), "dist/html/", outFilePath);
844
- })
845
- );
742
+ });
743
+ });
744
+ });
745
+ }
746
+ async function findOpenPort(min, max) {
747
+ let port = min;
748
+ while (port <= max) {
749
+ const isOpen = await isPortOpen(port);
750
+ if (isOpen) {
751
+ return port;
752
+ }
753
+ port += 1;
846
754
  }
755
+ throw new Error(`no ports open between ${min} and ${max}`);
847
756
  }
848
- function isRouteFile(filepath) {
849
- return filepath.startsWith("routes") && isJsFile(filepath);
757
+
758
+ // src/cli/commands/dev.ts
759
+ var __dirname2 = path5.dirname(fileURLToPath2(import.meta.url));
760
+ async function dev(rootProjectDir, options) {
761
+ process.env.NODE_ENV = "development";
762
+ const rootDir = path5.resolve(rootProjectDir || process.cwd());
763
+ const defaultPort = parseInt(process.env.PORT || "4007");
764
+ const host = (options == null ? void 0 : options.host) || "localhost";
765
+ const port = await findOpenPort(defaultPort, defaultPort + 10);
766
+ console.log();
767
+ console.log(`${dim2("\u2503")} project: ${rootDir}`);
768
+ console.log(`${dim2("\u2503")} server: http://${host}:${port}`);
769
+ console.log(`${dim2("\u2503")} mode: development`);
770
+ console.log();
771
+ const server = await createDevServer({ rootDir, port });
772
+ server.listen(port, host);
850
773
  }
851
- function fileSize(filepath) {
852
- const stats = fsExtra.statSync(filepath);
853
- const bytes = stats.size;
854
- const k = 1024;
855
- if (bytes < k) {
856
- return (bytes / k).toFixed(2) + " kB";
774
+ async function createDevServer(options) {
775
+ const rootDir = path5.resolve((options == null ? void 0 : options.rootDir) || process.cwd());
776
+ const rootConfig = await loadRootConfig(rootDir, { command: "dev" });
777
+ const port = options == null ? void 0 : options.port;
778
+ const server = express();
779
+ server.disable("x-powered-by");
780
+ server.use(rootProjectMiddleware({ rootConfig }));
781
+ server.use(await viteServerMiddleware({ rootConfig, port }));
782
+ const plugins = rootConfig.plugins || [];
783
+ await configureServerPlugins(
784
+ server,
785
+ async () => {
786
+ var _a;
787
+ const userMiddlewares = ((_a = rootConfig.server) == null ? void 0 : _a.middlewares) || [];
788
+ for (const middleware of userMiddlewares) {
789
+ server.use(middleware);
790
+ }
791
+ server.use(rootDevServerMiddleware());
792
+ server.use(rootDevServer404Middleware());
793
+ server.use(rootDevServer500Middleware());
794
+ },
795
+ plugins,
796
+ { type: "dev", rootConfig }
797
+ );
798
+ return server;
799
+ }
800
+ async function viteServerMiddleware(options) {
801
+ const rootConfig = options.rootConfig;
802
+ const rootDir = rootConfig.rootDir;
803
+ const elementGraph = await getElements(rootConfig);
804
+ const elements = Object.values(elementGraph.sourceFiles).map((sourceFile) => {
805
+ return sourceFile.relPath;
806
+ });
807
+ const bundleScripts = [];
808
+ if (await isDirectory(path5.join(rootDir, "bundles"))) {
809
+ const bundleFiles = await glob3("bundles/*", { cwd: rootDir });
810
+ bundleFiles.forEach((file) => {
811
+ const parts = path5.parse(file);
812
+ if (isJsFile(parts.base)) {
813
+ bundleScripts.push(file);
814
+ }
815
+ });
857
816
  }
858
- const units = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
859
- const i = Math.floor(Math.log(bytes) / Math.log(k));
860
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + units[i];
817
+ const optimizeDeps = [...elements, ...bundleScripts];
818
+ const viteServer = await createViteServer(rootConfig, {
819
+ port: options.port,
820
+ optimizeDeps
821
+ });
822
+ return async (req, res, next) => {
823
+ try {
824
+ req.viteServer = viteServer;
825
+ const renderModulePath = path5.resolve(__dirname2, "./render.js");
826
+ const render = await viteServer.ssrLoadModule(
827
+ renderModulePath
828
+ );
829
+ const assetMap = new DevServerAssetMap(
830
+ rootConfig,
831
+ viteServer.moduleGraph
832
+ );
833
+ req.renderer = new render.Renderer(rootConfig, { assetMap, elementGraph });
834
+ viteServer.middlewares(req, res, next);
835
+ } catch (e) {
836
+ next(e);
837
+ }
838
+ };
861
839
  }
862
- function printFileOutput(fileSize2, outputDir, outputFile) {
863
- const indent = " ".repeat(2);
864
- const paddedSize = fileSize2.padStart(9, " ");
865
- console.log(
866
- `${indent}${dim2(paddedSize)} ${dim2(outputDir)}${cyan(outputFile)}`
867
- );
840
+ function rootDevServerMiddleware() {
841
+ return async (req, res, next) => {
842
+ const renderer = req.renderer;
843
+ const viteServer = req.viteServer;
844
+ try {
845
+ await renderer.handle(req, res, next);
846
+ } catch (err) {
847
+ viteServer.ssrFixStacktrace(err);
848
+ next(err);
849
+ }
850
+ };
851
+ }
852
+ function rootDevServer404Middleware() {
853
+ return async (req, res) => {
854
+ console.error(`\u2753 404 ${req.originalUrl}`);
855
+ if (req.renderer) {
856
+ const url = req.path;
857
+ const ext = path5.extname(url);
858
+ if (!ext) {
859
+ const renderer = req.renderer;
860
+ const data = await renderer.renderDevServer404(req);
861
+ const html = data.html || "";
862
+ res.status(404).set({ "Content-Type": "text/html" }).end(html);
863
+ return;
864
+ }
865
+ }
866
+ res.status(404).set({ "Content-Type": "text/plain" }).end("404");
867
+ };
868
+ }
869
+ function rootDevServer500Middleware() {
870
+ return async (err, req, res, next) => {
871
+ console.error(`\u2757 500 ${req.originalUrl}`);
872
+ console.error(String(err.stack || err));
873
+ if (req.renderer) {
874
+ const url = req.path;
875
+ const ext = path5.extname(url);
876
+ if (!ext) {
877
+ const renderer = req.renderer;
878
+ const data = await renderer.renderDevServer500(req, err);
879
+ const html = data.html || "";
880
+ res.status(500).set({ "Content-Type": "text/html" }).end(html);
881
+ return;
882
+ }
883
+ }
884
+ next(err);
885
+ };
868
886
  }
869
887
 
870
888
  // src/cli/commands/preview.ts
871
889
  import path6 from "node:path";
890
+ import compression from "compression";
872
891
  import { default as express2 } from "express";
873
892
  import { dim as dim3 } from "kleur/colors";
874
893
  import sirv from "sirv";
875
- import compression from "compression";
876
- async function preview(rootProjectDir) {
894
+ async function preview(rootProjectDir, options) {
877
895
  process.env.NODE_ENV = "development";
878
896
  const rootDir = path6.resolve(rootProjectDir || process.cwd());
879
- const server = await createServer3({ rootDir });
897
+ const server = await createPreviewServer({ rootDir });
880
898
  const port = parseInt(process.env.PORT || "4007");
899
+ const host = (options == null ? void 0 : options.host) || "localhost";
881
900
  console.log();
882
901
  console.log(`${dim3("\u2503")} project: ${rootDir}`);
883
- console.log(`${dim3("\u2503")} server: http://localhost:${port}`);
902
+ console.log(`${dim3("\u2503")} server: http://${host}:${port}`);
884
903
  console.log(`${dim3("\u2503")} mode: preview`);
885
904
  console.log();
886
- server.listen(port);
905
+ server.listen(port, host);
887
906
  }
888
- async function createServer3(options) {
907
+ async function createPreviewServer(options) {
889
908
  const rootDir = options.rootDir;
890
909
  const rootConfig = await loadRootConfig(rootDir, { command: "preview" });
891
910
  const distDir = path6.join(rootDir, "dist");
@@ -923,9 +942,19 @@ async function rootPreviewRendererMiddleware(options) {
923
942
  `could not find ${manifestPath}. run \`root build\` before \`root preview\`.`
924
943
  );
925
944
  }
945
+ const elementGraphJsonPath = path6.join(
946
+ distDir,
947
+ "client/root-element-graph.json"
948
+ );
949
+ if (!await fileExists(elementGraphJsonPath)) {
950
+ throw new Error(
951
+ `could not find ${elementGraphJsonPath}. run \`root build\` before \`root start\`.`
952
+ );
953
+ }
926
954
  const rootManifest = await loadJson(manifestPath);
927
955
  const assetMap = BuildAssetMap.fromRootManifest(rootConfig, rootManifest);
928
- const elementGraph = await getElements(rootConfig);
956
+ const elementGraphJson = await loadJson(elementGraphJsonPath);
957
+ const elementGraph = ElementGraph.fromJson(elementGraphJson);
929
958
  const renderer = new render.Renderer(rootConfig, { assetMap, elementGraph });
930
959
  return async (req, _, next) => {
931
960
  req.renderer = renderer;
@@ -939,6 +968,8 @@ function rootPreviewServerMiddleware() {
939
968
  await renderer.handle(req, res, next);
940
969
  } catch (e) {
941
970
  try {
971
+ console.error(`error rendering ${req.originalUrl}`);
972
+ console.error(e.stack || e);
942
973
  const { html } = await renderer.renderError(e);
943
974
  res.status(500).set({ "Content-Type": "text/html" }).end(html);
944
975
  } catch (e2) {
@@ -952,14 +983,16 @@ function rootPreviewServerMiddleware() {
952
983
  function rootPreviewServer404Middleware() {
953
984
  return async (req, res) => {
954
985
  console.error(`\u2753 404 ${req.originalUrl}`);
955
- const url = req.path;
956
- const ext = path6.extname(url);
957
- const renderer = req.renderer;
958
- if (!ext) {
959
- const data = await renderer.render404();
960
- const html = data.html || "";
961
- res.status(404).set({ "Content-Type": "text/html" }).end(html);
962
- return;
986
+ if (req.renderer) {
987
+ const url = req.path;
988
+ const ext = path6.extname(url);
989
+ if (!ext) {
990
+ const renderer = req.renderer;
991
+ const data = await renderer.renderDevServer404(req);
992
+ const html = data.html || "";
993
+ res.status(404).set({ "Content-Type": "text/html" }).end(html);
994
+ return;
995
+ }
963
996
  }
964
997
  res.status(404).set({ "Content-Type": "text/plain" }).end("404");
965
998
  };
@@ -968,14 +1001,16 @@ function rootPreviewServer500Middleware() {
968
1001
  return async (err, req, res, next) => {
969
1002
  console.error(`\u2757 500 ${req.originalUrl}`);
970
1003
  console.error(String(err.stack || err));
971
- const url = req.path;
972
- const ext = path6.extname(url);
973
- const renderer = req.renderer;
974
- if (!ext) {
975
- const data = await renderer.renderError(err);
976
- const html = data.html || "";
977
- res.status(500).set({ "Content-Type": "text/html" }).end(html);
978
- return;
1004
+ if (req.renderer) {
1005
+ const url = req.path;
1006
+ const ext = path6.extname(url);
1007
+ if (!ext) {
1008
+ const renderer = req.renderer;
1009
+ const data = await renderer.renderDevServer500(req, err);
1010
+ const html = data.html || "";
1011
+ res.status(500).set({ "Content-Type": "text/html" }).end(html);
1012
+ return;
1013
+ }
979
1014
  }
980
1015
  next(err);
981
1016
  };
@@ -983,23 +1018,24 @@ function rootPreviewServer500Middleware() {
983
1018
 
984
1019
  // src/cli/commands/start.ts
985
1020
  import path7 from "node:path";
1021
+ import compression2 from "compression";
986
1022
  import { default as express3 } from "express";
987
1023
  import { dim as dim4 } from "kleur/colors";
988
1024
  import sirv2 from "sirv";
989
- import compression2 from "compression";
990
- async function start(rootProjectDir) {
1025
+ async function start(rootProjectDir, options) {
991
1026
  process.env.NODE_ENV = "production";
992
1027
  const rootDir = path7.resolve(rootProjectDir || process.cwd());
993
- const server = await createServer4({ rootDir });
1028
+ const server = await createProdServer({ rootDir });
994
1029
  const port = parseInt(process.env.PORT || "4007");
1030
+ const host = (options == null ? void 0 : options.host) || "localhost";
995
1031
  console.log();
996
1032
  console.log(`${dim4("\u2503")} project: ${rootDir}`);
997
- console.log(`${dim4("\u2503")} server: http://localhost:${port}`);
1033
+ console.log(`${dim4("\u2503")} server: http://${host}:${port}`);
998
1034
  console.log(`${dim4("\u2503")} mode: production`);
999
1035
  console.log();
1000
- server.listen(port);
1036
+ server.listen(port, host);
1001
1037
  }
1002
- async function createServer4(options) {
1038
+ async function createProdServer(options) {
1003
1039
  const rootDir = options.rootDir;
1004
1040
  const rootConfig = await loadRootConfig(rootDir, { command: "start" });
1005
1041
  const distDir = path7.join(rootDir, "dist");
@@ -1037,9 +1073,19 @@ async function rootProdRendererMiddleware(options) {
1037
1073
  `could not find ${manifestPath}. run \`root build\` before \`root start\`.`
1038
1074
  );
1039
1075
  }
1076
+ const elementGraphJsonPath = path7.join(
1077
+ distDir,
1078
+ "client/root-element-graph.json"
1079
+ );
1080
+ if (!await fileExists(elementGraphJsonPath)) {
1081
+ throw new Error(
1082
+ `could not find ${elementGraphJsonPath}. run \`root build\` before \`root start\`.`
1083
+ );
1084
+ }
1040
1085
  const rootManifest = await loadJson(manifestPath);
1041
1086
  const assetMap = BuildAssetMap.fromRootManifest(rootConfig, rootManifest);
1042
- const elementGraph = await getElements(rootConfig);
1087
+ const elementGraphJson = await loadJson(elementGraphJsonPath);
1088
+ const elementGraph = ElementGraph.fromJson(elementGraphJson);
1043
1089
  const renderer = new render.Renderer(rootConfig, { assetMap, elementGraph });
1044
1090
  return async (req, _, next) => {
1045
1091
  req.renderer = renderer;
@@ -1053,6 +1099,8 @@ function rootProdServerMiddleware() {
1053
1099
  await renderer.handle(req, res, next);
1054
1100
  } catch (e) {
1055
1101
  try {
1102
+ console.error(`error rendering ${req.originalUrl}`);
1103
+ console.error(e.stack || e);
1056
1104
  const { html } = await renderer.renderError(e);
1057
1105
  res.status(500).set({ "Content-Type": "text/html" }).end(html);
1058
1106
  } catch (e2) {
@@ -1100,6 +1148,9 @@ function rootProdServer500Middleware() {
1100
1148
  }
1101
1149
  export {
1102
1150
  build,
1151
+ createDevServer,
1152
+ createPreviewServer,
1153
+ createProdServer,
1103
1154
  dev,
1104
1155
  preview,
1105
1156
  start