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

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) {
@@ -229,7 +216,7 @@ var ElementGraph = class {
229
216
  if (!srcFile) {
230
217
  throw new Error(`could not find file path for tagName <${tagName}>`);
231
218
  }
232
- const src = fs.readFileSync(srcFile.filePath, "utf-8");
219
+ const src = fs2.readFileSync(srcFile.filePath, "utf-8");
233
220
  const tagNames = parseTagNames(src);
234
221
  const deps = /* @__PURE__ */ new Set();
235
222
  for (const depTagName of tagNames) {
@@ -243,7 +230,7 @@ var ElementGraph = class {
243
230
  async function getElements(rootConfig) {
244
231
  var _a, _b;
245
232
  const rootDir = rootConfig.rootDir;
246
- const workspaceRoot = searchForWorkspaceRoot2(rootDir);
233
+ const workspaceRoot = searchForWorkspaceRoot(rootDir);
247
234
  const elementsDirs = [path2.join(rootDir, "elements")];
248
235
  const elementsInclude = ((_a = rootConfig.elements) == null ? void 0 : _a.include) || [];
249
236
  const excludePatterns = ((_b = rootConfig.elements) == null ? void 0 : _b.exclude) || [];
@@ -280,54 +267,36 @@ async function getElements(rootConfig) {
280
267
  return graph;
281
268
  }
282
269
 
283
- // src/cli/commands/dev.ts
284
- import glob2 from "tiny-glob";
270
+ // src/cli/commands/build.ts
285
271
  var __dirname = path3.dirname(fileURLToPath(import.meta.url));
286
- async function dev(rootProjectDir) {
287
- process.env.NODE_ENV = "development";
272
+ async function build(rootProjectDir, options) {
273
+ var _a, _b, _c, _d, _e, _f, _g, _h;
274
+ const mode = (options == null ? void 0 : options.mode) || "production";
275
+ process.env.NODE_ENV = mode;
288
276
  const rootDir = path3.resolve(rootProjectDir || process.cwd());
289
- const defaultPort = parseInt(process.env.PORT || "4007");
290
- const port = await findOpenPort(defaultPort, defaultPort + 10);
277
+ const rootConfig = await loadRootConfig(rootDir, { command: "build" });
278
+ const distDir = path3.join(rootDir, "dist");
279
+ const ssrOnly = (options == null ? void 0 : options.ssrOnly) || false;
291
280
  console.log();
292
281
  console.log(`${dim("\u2503")} project: ${rootDir}`);
293
- console.log(`${dim("\u2503")} server: http://localhost:${port}`);
294
- console.log(`${dim("\u2503")} mode: development`);
282
+ console.log(`${dim("\u2503")} output: ${distDir}/html`);
283
+ console.log(`${dim("\u2503")} mode: ${mode}`);
295
284
  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);
285
+ await rmDir(distDir);
286
+ await makeDir(distDir);
287
+ const routeFiles = [];
288
+ if (await isDirectory(path3.join(rootDir, "routes"))) {
289
+ const pageFiles = await glob2("routes/**/*", { cwd: rootDir });
290
+ pageFiles.forEach((file) => {
291
+ const parts = path3.parse(file);
292
+ if (!parts.name.startsWith("_") && isJsFile(parts.base)) {
293
+ routeFiles.push(path3.resolve(rootDir, file));
315
294
  }
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;
295
+ });
296
+ }
328
297
  const elementGraph = await getElements(rootConfig);
329
298
  const elements = Object.values(elementGraph.sourceFiles).map((sourceFile) => {
330
- return sourceFile.relPath;
299
+ return sourceFile.filePath;
331
300
  });
332
301
  const bundleScripts = [];
333
302
  if (await isDirectory(path3.join(rootDir, "bundles"))) {
@@ -335,272 +304,489 @@ async function viteServerMiddleware(options) {
335
304
  bundleFiles.forEach((file) => {
336
305
  const parts = path3.parse(file);
337
306
  if (isJsFile(parts.base)) {
338
- bundleScripts.push(file);
307
+ bundleScripts.push(path3.resolve(rootDir, file));
339
308
  }
340
309
  });
341
310
  }
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
- }
311
+ const rootPlugins = rootConfig.plugins || [];
312
+ const viteConfig = rootConfig.vite || {};
313
+ const vitePlugins = [
314
+ ...viteConfig.plugins || [],
315
+ ...getVitePlugins(rootPlugins)
316
+ ];
317
+ const baseConfig = {
318
+ ...viteConfig,
319
+ root: rootDir,
320
+ mode,
321
+ esbuild: {
322
+ ...viteConfig.esbuild,
323
+ jsx: "automatic",
324
+ jsxImportSource: "preact",
325
+ treeShaking: true
326
+ },
327
+ plugins: vitePlugins
363
328
  };
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
- }
329
+ const ssrInput = {
330
+ render: path3.resolve(__dirname, "./render.js")
375
331
  };
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
- }
332
+ rootPlugins.forEach((plugin) => {
333
+ if (plugin.ssrInput) {
334
+ Object.assign(ssrInput, plugin.ssrInput());
390
335
  }
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
- }
336
+ });
337
+ const noExternalConfig = (_a = viteConfig.ssr) == null ? void 0 : _a.noExternal;
338
+ const noExternal = [];
339
+ if (noExternalConfig) {
340
+ if (Array.isArray(noExternalConfig)) {
341
+ noExternal.push(...noExternalConfig);
342
+ } else {
343
+ noExternal.push(noExternalConfig);
408
344
  }
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
345
  }
428
- async get(src) {
429
- const asset = this.srcToAsset.get(src);
430
- if (asset) {
431
- return asset;
346
+ await viteBuild({
347
+ ...baseConfig,
348
+ publicDir: false,
349
+ build: {
350
+ ...viteConfig == null ? void 0 : viteConfig.build,
351
+ rollupOptions: {
352
+ ...(_b = viteConfig == null ? void 0 : viteConfig.build) == null ? void 0 : _b.rollupOptions,
353
+ input: ssrInput,
354
+ output: {
355
+ format: "esm",
356
+ chunkFileNames: "chunks/[name].[hash].min.js",
357
+ assetFileNames: "assets/[name].[hash][extname]"
358
+ }
359
+ },
360
+ outDir: path3.join(distDir, "server"),
361
+ ssr: true,
362
+ ssrManifest: false,
363
+ cssCodeSplit: true,
364
+ target: "esnext",
365
+ minify: false,
366
+ modulePreload: { polyfill: false },
367
+ reportCompressedSize: false
368
+ },
369
+ ssr: {
370
+ ...viteConfig.ssr,
371
+ target: "node",
372
+ noExternal: ["@blinkk/root", ...noExternal]
432
373
  }
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
- }
374
+ });
375
+ await viteBuild({
376
+ ...baseConfig,
377
+ publicDir: false,
378
+ build: {
379
+ ...viteConfig == null ? void 0 : viteConfig.build,
380
+ rollupOptions: {
381
+ ...(_c = viteConfig == null ? void 0 : viteConfig.build) == null ? void 0 : _c.rollupOptions,
382
+ input: [...routeFiles],
383
+ output: {
384
+ format: "esm",
385
+ entryFileNames: "assets/[name].[hash].min.js",
386
+ chunkFileNames: "chunks/[name].[hash].min.js",
387
+ assetFileNames: "assets/[name].[hash][extname]",
388
+ ...(_e = (_d = viteConfig == null ? void 0 : viteConfig.build) == null ? void 0 : _d.rollupOptions) == null ? void 0 : _e.output
389
+ }
390
+ },
391
+ outDir: path3.join(distDir, "routes"),
392
+ ssr: false,
393
+ ssrManifest: false,
394
+ manifest: true,
395
+ cssCodeSplit: true,
396
+ target: "esnext",
397
+ minify: true,
398
+ modulePreload: { polyfill: false },
399
+ reportCompressedSize: false
439
400
  }
440
- console.log(`could not find build asset: ${src}`);
441
- return null;
442
- }
443
- add(asset) {
444
- this.srcToAsset.set(asset.src, asset);
401
+ });
402
+ const clientInput = [...elements, ...bundleScripts];
403
+ if (clientInput.length > 0) {
404
+ await viteBuild({
405
+ ...baseConfig,
406
+ publicDir: false,
407
+ build: {
408
+ ...viteConfig == null ? void 0 : viteConfig.build,
409
+ rollupOptions: {
410
+ ...(_f = viteConfig == null ? void 0 : viteConfig.build) == null ? void 0 : _f.rollupOptions,
411
+ input: [...elements, ...bundleScripts],
412
+ output: {
413
+ format: "esm",
414
+ entryFileNames: "assets/[name].[hash].min.js",
415
+ chunkFileNames: "chunks/[name].[hash].min.js",
416
+ assetFileNames: "assets/[name].[hash][extname]",
417
+ ...(_h = (_g = viteConfig == null ? void 0 : viteConfig.build) == null ? void 0 : _g.rollupOptions) == null ? void 0 : _h.output
418
+ }
419
+ },
420
+ outDir: path3.join(distDir, "client"),
421
+ ssr: false,
422
+ ssrManifest: false,
423
+ manifest: true,
424
+ cssCodeSplit: true,
425
+ target: "esnext",
426
+ minify: true,
427
+ modulePreload: { polyfill: false },
428
+ reportCompressedSize: false
429
+ }
430
+ });
431
+ } else {
432
+ await writeFile(path3.join(distDir, "client/manifest.json"), "{}");
445
433
  }
446
- toJson() {
447
- const result = {};
448
- for (const src of this.srcToAsset.keys()) {
449
- result[src] = this.srcToAsset.get(src).toJson();
434
+ await copyGlob(
435
+ "*.css",
436
+ path3.join(distDir, "routes/assets"),
437
+ path3.join(distDir, "client/assets")
438
+ );
439
+ const routesManifest = await loadJson(
440
+ path3.join(distDir, "routes/manifest.json")
441
+ );
442
+ const clientManifest = await loadJson(
443
+ path3.join(distDir, "client/manifest.json")
444
+ );
445
+ function collectRouteCss(asset, cssDeps, visited) {
446
+ if (!asset || !asset.file || visited.has(asset.file)) {
447
+ return;
448
+ }
449
+ visited.add(asset.file);
450
+ if (asset.css) {
451
+ asset.css.forEach((dep) => cssDeps.add(dep));
452
+ }
453
+ if (asset.imports) {
454
+ asset.imports.forEach((manifestKey) => {
455
+ collectRouteCss(routesManifest[manifestKey], cssDeps, visited);
456
+ });
450
457
  }
451
- return result;
452
458
  }
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);
459
+ Object.keys(routesManifest).forEach((manifestKey) => {
460
+ const asset = routesManifest[manifestKey];
461
+ if (asset.isEntry) {
462
+ const visited = /* @__PURE__ */ new Set();
463
+ const cssDeps = /* @__PURE__ */ new Set();
464
+ collectRouteCss(asset, cssDeps, visited);
465
+ asset.css = Array.from(cssDeps);
466
+ asset.imports = [];
467
+ clientManifest[manifestKey] = asset;
468
+ } else if (asset.file.endsWith(".css")) {
469
+ clientManifest[manifestKey] = asset;
470
+ }
471
+ });
472
+ const assetMap = BuildAssetMap.fromViteManifest(
473
+ rootConfig,
474
+ clientManifest,
475
+ elementGraph
476
+ );
477
+ const rootManifest = assetMap.toJson();
478
+ await writeFile(
479
+ path3.join(distDir, "client/root-manifest.json"),
480
+ JSON.stringify(rootManifest, null, 2)
481
+ );
482
+ const buildDir = path3.join(distDir, "html");
483
+ const publicDir = path3.join(rootDir, "public");
484
+ if (fsExtra.existsSync(path3.join(rootDir, "public"))) {
485
+ fsExtra.copySync(publicDir, buildDir, { dereference: true });
486
+ } else {
487
+ await makeDir(buildDir);
488
+ }
489
+ await makeDir(path3.join(buildDir, "assets"));
490
+ await makeDir(path3.join(buildDir, "chunks"));
491
+ console.log("\njs/css output:");
492
+ await Promise.all(
493
+ Object.keys(rootManifest).map(async (src) => {
494
+ if (isRouteFile(src)) {
495
+ return;
464
496
  }
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/") && isJsFile(src) ? "" : `/${manifestChunk.file}`;
471
- const assetData = {
497
+ const assetData = rootManifest[src];
498
+ if (!assetData.assetUrl) {
499
+ return;
500
+ }
501
+ const assetRelPath = assetData.assetUrl.slice(1);
502
+ const assetFrom = path3.join(distDir, "client", assetRelPath);
503
+ const assetTo = path3.join(buildDir, assetRelPath);
504
+ if (!await fileExists(assetFrom)) {
505
+ console.log(`${assetFrom} does not exist`);
506
+ return;
507
+ }
508
+ await fsExtra.copy(assetFrom, assetTo);
509
+ printFileOutput(fileSize(assetTo), "dist/html/", assetRelPath);
510
+ })
511
+ );
512
+ if (!ssrOnly) {
513
+ const render = await import(path3.join(distDir, "server/render.js"));
514
+ const renderer = new render.Renderer(rootConfig, { assetMap, elementGraph });
515
+ const sitemap = await renderer.getSitemap();
516
+ console.log("\nhtml output:");
517
+ await Promise.all(
518
+ Object.keys(sitemap).map(async (urlPath) => {
519
+ const { route, params } = sitemap[urlPath];
520
+ const data = await renderer.renderRoute(route, {
521
+ routeParams: params
522
+ });
523
+ if (data.notFound) {
524
+ return;
525
+ }
526
+ let outFilePath = path3.join(urlPath.slice(1), "index.html");
527
+ if (outFilePath.endsWith("404/index.html")) {
528
+ outFilePath = outFilePath.replace("404/index.html", "404.html");
529
+ }
530
+ const outPath = path3.join(buildDir, outFilePath);
531
+ let html = data.html || "";
532
+ if (rootConfig.prettyHtml !== false) {
533
+ html = await htmlPretty(html, rootConfig.prettyHtmlOptions);
534
+ }
535
+ if (rootConfig.minifyHtml !== false) {
536
+ html = await htmlMinify(html, rootConfig.minifyHtmlOptions);
537
+ }
538
+ await writeFile(outPath, html);
539
+ printFileOutput(fileSize(outPath), "dist/html/", outFilePath);
540
+ })
541
+ );
542
+ }
543
+ }
544
+ function isRouteFile(filepath) {
545
+ return filepath.startsWith("routes") && isJsFile(filepath);
546
+ }
547
+ function fileSize(filepath) {
548
+ const stats = fsExtra.statSync(filepath);
549
+ const bytes = stats.size;
550
+ const k = 1024;
551
+ if (bytes < k) {
552
+ return (bytes / k).toFixed(2) + " kB";
553
+ }
554
+ const units = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
555
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
556
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + units[i];
557
+ }
558
+ function printFileOutput(fileSize2, outputDir, outputFile) {
559
+ const indent = " ".repeat(2);
560
+ const paddedSize = fileSize2.padStart(9, " ");
561
+ console.log(
562
+ `${indent}${dim(paddedSize)} ${dim(outputDir)}${cyan(outputFile)}`
563
+ );
564
+ }
565
+
566
+ // src/cli/commands/dev.ts
567
+ import path5 from "node:path";
568
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
569
+ import { default as express } from "express";
570
+
571
+ // src/render/asset-map/dev-asset-map.ts
572
+ import path4 from "node:path";
573
+ import { searchForWorkspaceRoot as searchForWorkspaceRoot2 } from "vite";
574
+ var DevServerAssetMap = class {
575
+ constructor(rootConfig, moduleGraph) {
576
+ this.rootConfig = rootConfig;
577
+ this.moduleGraph = moduleGraph;
578
+ }
579
+ async get(src) {
580
+ const file = path4.resolve(this.rootConfig.rootDir, src);
581
+ const viteModules = this.moduleGraph.getModulesByFile(file);
582
+ if (viteModules && viteModules.size > 0) {
583
+ const [viteModule] = viteModules;
584
+ return new DevServerAsset(src, {
585
+ assetMap: this,
586
+ viteModule
587
+ });
588
+ }
589
+ if (file.startsWith(this.rootConfig.rootDir)) {
590
+ const assetUrl = file.slice(this.rootConfig.rootDir.length);
591
+ return {
472
592
  src,
473
593
  assetUrl,
474
- importedModules: manifestChunk.imports || [],
475
- importedCss: (manifestChunk.css || []).map((relPath) => `/${relPath}`),
476
- isElement
594
+ getCssDeps: async () => [],
595
+ getJsDeps: async () => [assetUrl]
477
596
  };
478
- assetMap.add(new BuildAsset(assetMap, assetData));
479
- });
480
- return assetMap;
597
+ }
598
+ const workspaceRoot = searchForWorkspaceRoot2(this.rootConfig.rootDir);
599
+ if (await directoryContains(workspaceRoot, file)) {
600
+ const assetUrl = `/@fs/${file}`;
601
+ return {
602
+ src,
603
+ assetUrl,
604
+ getCssDeps: async () => [],
605
+ getJsDeps: async () => [assetUrl]
606
+ };
607
+ }
608
+ console.log(`could not find asset in asset map: ${src}`);
609
+ return null;
481
610
  }
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;
611
+ filePathToSrc(file) {
612
+ return path4.relative(this.rootConfig.rootDir, file);
489
613
  }
490
614
  };
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;
615
+ var DevServerAsset = class {
616
+ constructor(src, options) {
617
+ this.src = src;
618
+ this.assetMap = options.assetMap;
619
+ this.viteModule = options.viteModule;
620
+ this.moduleId = this.viteModule.id;
621
+ this.assetUrl = this.viteModule.url;
507
622
  }
508
623
  async getCssDeps() {
509
624
  const visited = /* @__PURE__ */ new Set();
510
625
  const deps = /* @__PURE__ */ new Set();
511
- await this.collectCss(this, deps, visited);
626
+ this.collectCss(this, deps, visited);
512
627
  return Array.from(deps);
513
628
  }
514
629
  async getJsDeps() {
515
630
  const visited = /* @__PURE__ */ new Set();
516
631
  const deps = /* @__PURE__ */ new Set();
517
- await this.collectJs(this, deps, visited);
632
+ this.collectJs(this, deps, visited);
518
633
  return Array.from(deps);
519
634
  }
520
- async collectJs(asset, urls, visited) {
635
+ getImportedModules() {
636
+ return this.viteModule.importedModules;
637
+ }
638
+ collectJs(asset, urls, visited) {
521
639
  if (!asset) {
522
640
  return;
523
641
  }
524
- if (!asset.src) {
642
+ if (!asset.moduleId) {
525
643
  return;
526
644
  }
527
- if (visited.has(asset.src)) {
645
+ if (visited.has(asset.moduleId)) {
528
646
  return;
529
647
  }
530
- visited.add(asset.src);
531
- if (asset.isElement) {
648
+ visited.add(asset.moduleId);
649
+ const parts = path4.parse(asset.assetUrl);
650
+ if ([".js", ".jsx", ".ts", ".tsx"].includes(parts.ext) && asset.moduleId.includes("/elements/")) {
532
651
  urls.add(asset.assetUrl);
533
652
  }
534
- await Promise.all(
535
- asset.importedModules.map(async (src) => {
536
- const importedAsset = await this.assetMap.get(src);
653
+ asset.getImportedModules().forEach((viteModule) => {
654
+ if (viteModule.file) {
655
+ const src = this.assetMap.filePathToSrc(viteModule.file);
656
+ const importedAsset = new DevServerAsset(src, {
657
+ assetMap: this.assetMap,
658
+ viteModule
659
+ });
537
660
  this.collectJs(importedAsset, urls, visited);
538
- })
539
- );
661
+ }
662
+ });
540
663
  }
541
- async collectCss(asset, urls, visited) {
664
+ collectCss(asset, urls, visited) {
542
665
  if (!asset) {
543
666
  return;
544
667
  }
545
- if (!asset.src) {
668
+ if (!asset.assetUrl) {
546
669
  return;
547
670
  }
548
- if (visited.has(asset.src)) {
671
+ if (visited.has(asset.assetUrl)) {
549
672
  return;
550
673
  }
551
- visited.add(asset.src);
552
- if (asset.importedCss) {
553
- asset.importedCss.forEach((cssUrl) => urls.add(cssUrl));
674
+ visited.add(asset.assetUrl);
675
+ if (asset.src.endsWith(".scss")) {
676
+ const parts = path4.parse(asset.src);
677
+ if (!parts.name.startsWith("_")) {
678
+ urls.add(asset.assetUrl);
679
+ }
554
680
  }
555
- await Promise.all(
556
- asset.importedModules.map(async (moduleId) => {
557
- const importedAsset = await this.assetMap.get(moduleId);
681
+ asset.getImportedModules().forEach((viteModule) => {
682
+ if (viteModule.file) {
683
+ const src = this.assetMap.filePathToSrc(viteModule.file);
684
+ const importedAsset = new DevServerAsset(src, {
685
+ assetMap: this.assetMap,
686
+ viteModule
687
+ });
558
688
  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
- };
689
+ }
690
+ });
570
691
  }
571
692
  };
572
693
 
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;
694
+ // src/cli/commands/dev.ts
695
+ import { dim as dim2 } from "kleur/colors";
696
+
697
+ // src/core/middleware.ts
698
+ function rootProjectMiddleware(options) {
699
+ return (req, _, next) => {
700
+ req.rootConfig = options.rootConfig;
701
+ next();
702
+ };
703
+ }
704
+
705
+ // src/utils/ports.ts
706
+ import { createServer } from "node:net";
707
+ function isPortOpen(port) {
708
+ return new Promise((resolve, reject) => {
709
+ const server = createServer();
710
+ server.on("error", (err) => {
711
+ if (err.code === "EADDRINUSE") {
712
+ resolve(false);
713
+ return;
714
+ }
715
+ reject(err);
716
+ });
717
+ server.on("close", () => {
718
+ resolve(true);
719
+ });
720
+ server.listen(port, () => {
721
+ server.close((err) => {
722
+ if (err) {
723
+ console.log(`error closing server: ${err}`);
724
+ reject(err);
725
+ }
726
+ });
727
+ });
728
+ });
729
+ }
730
+ async function findOpenPort(min, max) {
731
+ let port = min;
732
+ while (port <= max) {
733
+ const isOpen = await isPortOpen(port);
734
+ if (isOpen) {
735
+ return port;
736
+ }
737
+ port += 1;
738
+ }
739
+ throw new Error(`no ports open between ${min} and ${max}`);
740
+ }
741
+
742
+ // src/cli/commands/dev.ts
743
+ import glob3 from "tiny-glob";
744
+ var __dirname2 = path5.dirname(fileURLToPath2(import.meta.url));
745
+ async function dev(rootProjectDir) {
746
+ process.env.NODE_ENV = "development";
580
747
  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;
748
+ const defaultPort = parseInt(process.env.PORT || "4007");
749
+ const port = await findOpenPort(defaultPort, defaultPort + 10);
584
750
  console.log();
585
751
  console.log(`${dim2("\u2503")} project: ${rootDir}`);
586
- console.log(`${dim2("\u2503")} output: ${distDir}/html`);
587
- console.log(`${dim2("\u2503")} mode: ${mode}`);
752
+ console.log(`${dim2("\u2503")} server: http://localhost:${port}`);
753
+ console.log(`${dim2("\u2503")} mode: development`);
588
754
  console.log();
589
- await rmDir(distDir);
590
- await makeDir(distDir);
591
- const routeFiles = [];
592
- if (await isDirectory(path5.join(rootDir, "routes"))) {
593
- const pageFiles = await glob3("routes/**/*", { cwd: rootDir });
594
- pageFiles.forEach((file) => {
595
- const parts = path5.parse(file);
596
- if (!parts.name.startsWith("_") && isJsFile(parts.base)) {
597
- routeFiles.push(path5.resolve(rootDir, file));
755
+ const server = await createDevServer({ rootDir, port });
756
+ server.listen(port);
757
+ }
758
+ async function createDevServer(options) {
759
+ const rootDir = path5.resolve((options == null ? void 0 : options.rootDir) || process.cwd());
760
+ const rootConfig = await loadRootConfig(rootDir, { command: "dev" });
761
+ const port = options == null ? void 0 : options.port;
762
+ const server = express();
763
+ server.disable("x-powered-by");
764
+ server.use(rootProjectMiddleware({ rootConfig }));
765
+ server.use(await viteServerMiddleware({ rootConfig, port }));
766
+ const plugins = rootConfig.plugins || [];
767
+ await configureServerPlugins(
768
+ server,
769
+ async () => {
770
+ var _a;
771
+ const userMiddlewares = ((_a = rootConfig.server) == null ? void 0 : _a.middlewares) || [];
772
+ for (const middleware of userMiddlewares) {
773
+ server.use(middleware);
598
774
  }
599
- });
600
- }
775
+ server.use(rootDevServerMiddleware());
776
+ server.use(rootDevServer404Middleware());
777
+ server.use(rootDevServer500Middleware());
778
+ },
779
+ plugins,
780
+ { type: "dev", rootConfig }
781
+ );
782
+ return server;
783
+ }
784
+ async function viteServerMiddleware(options) {
785
+ const rootConfig = options.rootConfig;
786
+ const rootDir = rootConfig.rootDir;
601
787
  const elementGraph = await getElements(rootConfig);
602
788
  const elements = Object.values(elementGraph.sourceFiles).map((sourceFile) => {
603
- return sourceFile.filePath;
789
+ return sourceFile.relPath;
604
790
  });
605
791
  const bundleScripts = [];
606
792
  if (await isDirectory(path5.join(rootDir, "bundles"))) {
@@ -608,263 +794,79 @@ async function build(rootProjectDir, options) {
608
794
  bundleFiles.forEach((file) => {
609
795
  const parts = path5.parse(file);
610
796
  if (isJsFile(parts.base)) {
611
- bundleScripts.push(path5.resolve(rootDir, file));
797
+ bundleScripts.push(file);
612
798
  }
613
799
  });
614
800
  }
615
- const rootPlugins = rootConfig.plugins || [];
616
- const viteConfig = rootConfig.vite || {};
617
- const vitePlugins = [
618
- ...viteConfig.plugins || [],
619
- ...getVitePlugins(rootPlugins)
620
- ];
621
- const baseConfig = {
622
- ...viteConfig,
623
- root: rootDir,
624
- mode,
625
- esbuild: {
626
- ...viteConfig.esbuild,
627
- jsx: "automatic",
628
- jsxImportSource: "preact",
629
- treeShaking: true
630
- },
631
- plugins: vitePlugins
632
- };
633
- const ssrInput = {
634
- render: path5.resolve(__dirname2, "./render.js")
635
- };
636
- rootPlugins.forEach((plugin) => {
637
- if (plugin.ssrInput) {
638
- Object.assign(ssrInput, plugin.ssrInput());
639
- }
640
- });
641
- const noExternalConfig = (_a = viteConfig.ssr) == null ? void 0 : _a.noExternal;
642
- const noExternal = [];
643
- if (noExternalConfig) {
644
- if (Array.isArray(noExternalConfig)) {
645
- noExternal.push(...noExternalConfig);
646
- } else {
647
- noExternal.push(noExternalConfig);
648
- }
649
- }
650
- await viteBuild({
651
- ...baseConfig,
652
- publicDir: false,
653
- build: {
654
- ...viteConfig == null ? void 0 : viteConfig.build,
655
- rollupOptions: {
656
- ...(_b = viteConfig == null ? void 0 : viteConfig.build) == null ? void 0 : _b.rollupOptions,
657
- input: ssrInput,
658
- output: {
659
- format: "esm",
660
- chunkFileNames: "chunks/[name].[hash].min.js",
661
- assetFileNames: "assets/[name].[hash][extname]"
662
- }
663
- },
664
- outDir: path5.join(distDir, "server"),
665
- ssr: true,
666
- ssrManifest: false,
667
- cssCodeSplit: true,
668
- target: "esnext",
669
- minify: false,
670
- modulePreload: { polyfill: false },
671
- reportCompressedSize: false
672
- },
673
- ssr: {
674
- ...viteConfig.ssr,
675
- target: "node",
676
- noExternal: ["@blinkk/root", ...noExternal]
677
- }
678
- });
679
- await viteBuild({
680
- ...baseConfig,
681
- publicDir: false,
682
- build: {
683
- ...viteConfig == null ? void 0 : viteConfig.build,
684
- rollupOptions: {
685
- ...(_c = viteConfig == null ? void 0 : viteConfig.build) == null ? void 0 : _c.rollupOptions,
686
- input: [...routeFiles],
687
- output: {
688
- format: "esm",
689
- entryFileNames: "assets/[name].[hash].min.js",
690
- chunkFileNames: "chunks/[name].[hash].min.js",
691
- assetFileNames: "assets/[name].[hash][extname]",
692
- ...(_e = (_d = viteConfig == null ? void 0 : viteConfig.build) == null ? void 0 : _d.rollupOptions) == null ? void 0 : _e.output
693
- }
694
- },
695
- outDir: path5.join(distDir, "routes"),
696
- ssr: false,
697
- ssrManifest: false,
698
- manifest: true,
699
- cssCodeSplit: true,
700
- target: "esnext",
701
- minify: true,
702
- modulePreload: { polyfill: false },
703
- reportCompressedSize: false
704
- }
801
+ const optimizeDeps = [...elements, ...bundleScripts];
802
+ const viteServer = await createViteServer(rootConfig, {
803
+ port: options.port,
804
+ optimizeDeps
705
805
  });
706
- const clientInput = [...elements, ...bundleScripts];
707
- if (clientInput.length > 0) {
708
- await viteBuild({
709
- ...baseConfig,
710
- publicDir: false,
711
- build: {
712
- ...viteConfig == null ? void 0 : viteConfig.build,
713
- rollupOptions: {
714
- ...(_f = viteConfig == null ? void 0 : viteConfig.build) == null ? void 0 : _f.rollupOptions,
715
- input: [...elements, ...bundleScripts],
716
- output: {
717
- format: "esm",
718
- entryFileNames: "assets/[name].[hash].min.js",
719
- chunkFileNames: "chunks/[name].[hash].min.js",
720
- assetFileNames: "assets/[name].[hash][extname]",
721
- ...(_h = (_g = viteConfig == null ? void 0 : viteConfig.build) == null ? void 0 : _g.rollupOptions) == null ? void 0 : _h.output
722
- }
723
- },
724
- outDir: path5.join(distDir, "client"),
725
- ssr: false,
726
- ssrManifest: false,
727
- manifest: true,
728
- cssCodeSplit: true,
729
- target: "esnext",
730
- minify: true,
731
- modulePreload: { polyfill: false },
732
- reportCompressedSize: false
733
- }
734
- });
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);
760
- });
806
+ return async (req, res, next) => {
807
+ try {
808
+ req.viteServer = viteServer;
809
+ const renderModulePath = path5.resolve(__dirname2, "./render.js");
810
+ const render = await viteServer.ssrLoadModule(
811
+ renderModulePath
812
+ );
813
+ const assetMap = new DevServerAssetMap(
814
+ rootConfig,
815
+ viteServer.moduleGraph
816
+ );
817
+ req.renderer = new render.Renderer(rootConfig, { assetMap, elementGraph });
818
+ viteServer.middlewares(req, res, next);
819
+ } catch (e) {
820
+ next(e);
761
821
  }
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;
822
+ };
823
+ }
824
+ function rootDevServerMiddleware() {
825
+ return async (req, res, next) => {
826
+ const renderer = req.renderer;
827
+ const viteServer = req.viteServer;
828
+ try {
829
+ await renderer.handle(req, res, next);
830
+ } catch (err) {
831
+ viteServer.ssrFixStacktrace(err);
832
+ next(err);
774
833
  }
775
- });
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
- await makeDir(buildDir);
792
- }
793
- await makeDir(path5.join(buildDir, "assets"));
794
- await makeDir(path5.join(buildDir, "chunks"));
795
- console.log("\njs/css output:");
796
- await Promise.all(
797
- Object.keys(rootManifest).map(async (src) => {
798
- if (isRouteFile(src)) {
799
- return;
800
- }
801
- const assetData = rootManifest[src];
802
- if (!assetData.assetUrl) {
834
+ };
835
+ }
836
+ function rootDevServer404Middleware() {
837
+ return async (req, res) => {
838
+ console.error(`\u2753 404 ${req.originalUrl}`);
839
+ if (req.renderer) {
840
+ const url = req.path;
841
+ const ext = path5.extname(url);
842
+ if (!ext) {
843
+ const renderer = req.renderer;
844
+ const data = await renderer.renderDevServer404(req);
845
+ const html = data.html || "";
846
+ res.status(404).set({ "Content-Type": "text/html" }).end(html);
803
847
  return;
804
848
  }
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`);
849
+ }
850
+ res.status(404).set({ "Content-Type": "text/plain" }).end("404");
851
+ };
852
+ }
853
+ function rootDevServer500Middleware() {
854
+ return async (err, req, res, next) => {
855
+ console.error(`\u2757 500 ${req.originalUrl}`);
856
+ console.error(String(err.stack || err));
857
+ if (req.renderer) {
858
+ const url = req.path;
859
+ const ext = path5.extname(url);
860
+ if (!ext) {
861
+ const renderer = req.renderer;
862
+ const data = await renderer.renderDevServer500(req, err);
863
+ const html = data.html || "";
864
+ res.status(500).set({ "Content-Type": "text/html" }).end(html);
810
865
  return;
811
866
  }
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);
841
- }
842
- await writeFile(outPath, html);
843
- printFileOutput(fileSize(outPath), "dist/html/", outFilePath);
844
- })
845
- );
846
- }
847
- }
848
- function isRouteFile(filepath) {
849
- return filepath.startsWith("routes") && isJsFile(filepath);
850
- }
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";
857
- }
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];
861
- }
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
- );
867
+ }
868
+ next(err);
869
+ };
868
870
  }
869
871
 
870
872
  // src/cli/commands/preview.ts
@@ -876,7 +878,7 @@ import compression from "compression";
876
878
  async function preview(rootProjectDir) {
877
879
  process.env.NODE_ENV = "development";
878
880
  const rootDir = path6.resolve(rootProjectDir || process.cwd());
879
- const server = await createServer3({ rootDir });
881
+ const server = await createPreviewServer({ rootDir });
880
882
  const port = parseInt(process.env.PORT || "4007");
881
883
  console.log();
882
884
  console.log(`${dim3("\u2503")} project: ${rootDir}`);
@@ -885,7 +887,7 @@ async function preview(rootProjectDir) {
885
887
  console.log();
886
888
  server.listen(port);
887
889
  }
888
- async function createServer3(options) {
890
+ async function createPreviewServer(options) {
889
891
  const rootDir = options.rootDir;
890
892
  const rootConfig = await loadRootConfig(rootDir, { command: "preview" });
891
893
  const distDir = path6.join(rootDir, "dist");
@@ -990,7 +992,7 @@ import compression2 from "compression";
990
992
  async function start(rootProjectDir) {
991
993
  process.env.NODE_ENV = "production";
992
994
  const rootDir = path7.resolve(rootProjectDir || process.cwd());
993
- const server = await createServer4({ rootDir });
995
+ const server = await createProdServer({ rootDir });
994
996
  const port = parseInt(process.env.PORT || "4007");
995
997
  console.log();
996
998
  console.log(`${dim4("\u2503")} project: ${rootDir}`);
@@ -999,7 +1001,7 @@ async function start(rootProjectDir) {
999
1001
  console.log();
1000
1002
  server.listen(port);
1001
1003
  }
1002
- async function createServer4(options) {
1004
+ async function createProdServer(options) {
1003
1005
  const rootDir = options.rootDir;
1004
1006
  const rootConfig = await loadRootConfig(rootDir, { command: "start" });
1005
1007
  const distDir = path7.join(rootDir, "dist");
@@ -1100,6 +1102,9 @@ function rootProdServer500Middleware() {
1100
1102
  }
1101
1103
  export {
1102
1104
  build,
1105
+ createDevServer,
1106
+ createPreviewServer,
1107
+ createProdServer,
1103
1108
  dev,
1104
1109
  preview,
1105
1110
  start