@blinkk/root 1.0.0-beta.1 → 1.0.0-beta.11

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