evolit 0.1.0-alpha.14 → 0.1.0-alpha.16
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/package.json +1 -1
- package/src/cli.js +13 -8
- package/src/client-assets.js +17 -0
- package/src/compiler.js +57 -2
- package/src/deployment-runtime.js +58 -2
- package/src/render.js +6 -1
- package/src/server.js +72 -0
- package/src/terminal.js +32 -0
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
import { scaffoldSite } from "./scaffold.js";
|
|
6
|
+
import { terminal } from "./terminal.js";
|
|
6
7
|
|
|
7
8
|
function parseArguments(argv) {
|
|
8
9
|
const [command, ...rest] = argv;
|
|
@@ -24,9 +25,9 @@ function parseArguments(argv) {
|
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
function printUsage() {
|
|
27
|
-
console.log("Usage: evolit <directory
|
|
28
|
-
console.log(
|
|
29
|
-
console.log(
|
|
28
|
+
console.log(`${terminal.bold("Usage:")} evolit <directory>`);
|
|
29
|
+
console.log(` evolit ${terminal.cyan("init")} <directory>`);
|
|
30
|
+
console.log(` evolit ${terminal.cyan("<dev|build|start>")} [--port 3000]`);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
async function createSite(projectRoot, targetDirectory) {
|
|
@@ -35,7 +36,7 @@ async function createSite(projectRoot, targetDirectory) {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
const createdDirectory = await scaffoldSite(path.resolve(projectRoot, targetDirectory));
|
|
38
|
-
console.log(
|
|
39
|
+
console.log(`${terminal.green("✓")} Created evolit site: ${terminal.cyan(createdDirectory)}`);
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
async function run() {
|
|
@@ -55,23 +56,25 @@ async function run() {
|
|
|
55
56
|
if (command === "build") {
|
|
56
57
|
const { buildProject } = await import("./build.js");
|
|
57
58
|
const manifestPath = await buildProject(projectRoot);
|
|
58
|
-
console.log(
|
|
59
|
+
console.log(`${terminal.green("✓")} Built Evolit app: ${terminal.cyan(path.relative(projectRoot, manifestPath))}`);
|
|
59
60
|
return;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
if (command === "start") {
|
|
63
64
|
const { createStartServer } = await import("./server.js");
|
|
65
|
+
console.log(`${terminal.cyan("▲")} ${terminal.bold("Evolit")} ${terminal.cyan("start")}`);
|
|
64
66
|
const server = await createStartServer(projectRoot, options);
|
|
65
67
|
await server.listen();
|
|
66
|
-
console.log(
|
|
68
|
+
console.log(`${terminal.green("✓")} Ready on ${terminal.cyan(`http://localhost:${server.port}`)}`);
|
|
67
69
|
return;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
if (command === "dev") {
|
|
71
73
|
const { createDevServer } = await import("./server.js");
|
|
74
|
+
console.log(`${terminal.cyan("▲")} ${terminal.bold("Evolit")} ${terminal.cyan("dev")}`);
|
|
72
75
|
const server = await createDevServer(projectRoot, options);
|
|
73
76
|
await server.listen();
|
|
74
|
-
console.log(
|
|
77
|
+
console.log(`${terminal.green("✓")} Ready on ${terminal.cyan(`http://localhost:${server.port}`)}`);
|
|
75
78
|
return;
|
|
76
79
|
}
|
|
77
80
|
|
|
@@ -79,6 +82,8 @@ async function run() {
|
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
run().catch((error) => {
|
|
82
|
-
console.error(
|
|
85
|
+
console.error(
|
|
86
|
+
`${terminal.red("[evolit]")} ${terminal.red("✖")} ${error instanceof Error ? error.stack ?? error.message : String(error)}`,
|
|
87
|
+
);
|
|
83
88
|
process.exitCode = 1;
|
|
84
89
|
});
|
package/src/client-assets.js
CHANGED
|
@@ -36,6 +36,23 @@ const sharedRuntimeBuildCache = new Map();
|
|
|
36
36
|
const clientVendorSpecifierCache = new Map();
|
|
37
37
|
const clientModuleMetadataCache = new Map();
|
|
38
38
|
|
|
39
|
+
export function resetDevelopmentAssetCaches(projectRoot) {
|
|
40
|
+
const normalizedProjectRoot = path.resolve(projectRoot);
|
|
41
|
+
const developmentPrefix = `${normalizedProjectRoot}::development::`;
|
|
42
|
+
|
|
43
|
+
for (const cache of [
|
|
44
|
+
sharedRuntimeBuildCache,
|
|
45
|
+
clientVendorSpecifierCache,
|
|
46
|
+
clientModuleMetadataCache,
|
|
47
|
+
]) {
|
|
48
|
+
for (const key of cache.keys()) {
|
|
49
|
+
if (key.startsWith(developmentPrefix)) {
|
|
50
|
+
cache.delete(key);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
39
56
|
function isBareSpecifier(specifier) {
|
|
40
57
|
return (
|
|
41
58
|
typeof specifier === "string" &&
|
package/src/compiler.js
CHANGED
|
@@ -25,6 +25,7 @@ const developmentGraphCache = new Map();
|
|
|
25
25
|
const developmentGraphDependencies = new Map();
|
|
26
26
|
const developmentModuleNamespaceCache = new Map();
|
|
27
27
|
const developmentGraphVersions = new Map();
|
|
28
|
+
const developmentExternalImportWarnings = new Set();
|
|
28
29
|
|
|
29
30
|
function isRelativeSpecifier(specifier) {
|
|
30
31
|
return specifier.startsWith("./") || specifier.startsWith("../");
|
|
@@ -60,6 +61,29 @@ function isStyleAssetPath(filePath) {
|
|
|
60
61
|
return filePath.endsWith(".css");
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
function isPathOutsideProject(projectRoot, filePath) {
|
|
65
|
+
const relativePath = path.relative(projectRoot, filePath);
|
|
66
|
+
return (
|
|
67
|
+
relativePath === ".." ||
|
|
68
|
+
relativePath.startsWith(`..${path.sep}`) ||
|
|
69
|
+
path.isAbsolute(relativePath)
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function reportDevelopmentExternalImport(projectRoot, sourcePath, resolvedImportPath, onDevelopmentEvent) {
|
|
74
|
+
const warningKey = `${path.resolve(projectRoot)}::${sourcePath}::${resolvedImportPath}`;
|
|
75
|
+
if (developmentExternalImportWarnings.has(warningKey)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
developmentExternalImportWarnings.add(warningKey);
|
|
80
|
+
onDevelopmentEvent?.({
|
|
81
|
+
type: "external-import",
|
|
82
|
+
sourcePath,
|
|
83
|
+
resolvedImportPath,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
63
87
|
function createStaticAssetStubSource(relativeAssetPath, mode, target = "server", staticAssetPublicUrls = null) {
|
|
64
88
|
const normalizedAssetPath = relativeAssetPath.split(path.sep).join("/");
|
|
65
89
|
if (normalizedAssetPath.endsWith(".css")) {
|
|
@@ -112,7 +136,7 @@ function getTypedOutputRoot(projectRoot, mode, target = "server") {
|
|
|
112
136
|
}
|
|
113
137
|
|
|
114
138
|
function toOutputPath(projectRoot, outputRoot, sourcePath) {
|
|
115
|
-
const relativePath =
|
|
139
|
+
const relativePath = toOutputRelativePath(projectRoot, sourcePath);
|
|
116
140
|
const extension = path.extname(relativePath);
|
|
117
141
|
if (!extension) {
|
|
118
142
|
return path.join(outputRoot, `${relativePath}.mjs`);
|
|
@@ -124,6 +148,22 @@ function toOutputPath(projectRoot, outputRoot, sourcePath) {
|
|
|
124
148
|
);
|
|
125
149
|
}
|
|
126
150
|
|
|
151
|
+
function toOutputRelativePath(projectRoot, sourcePath) {
|
|
152
|
+
const relativePath = path.relative(projectRoot, sourcePath);
|
|
153
|
+
const isOutsideProject =
|
|
154
|
+
relativePath === ".." ||
|
|
155
|
+
relativePath.startsWith(`..${path.sep}`) ||
|
|
156
|
+
path.isAbsolute(relativePath);
|
|
157
|
+
if (!isOutsideProject) {
|
|
158
|
+
return relativePath;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return path.join(
|
|
162
|
+
"__external__",
|
|
163
|
+
...relativePath.split(path.sep).map((segment) => segment === ".." ? "__up__" : segment),
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
127
167
|
async function rewriteRelativeSpecifiers({
|
|
128
168
|
projectRoot,
|
|
129
169
|
outputRoot,
|
|
@@ -138,6 +178,7 @@ async function rewriteRelativeSpecifiers({
|
|
|
138
178
|
sourceMaps,
|
|
139
179
|
outputPath,
|
|
140
180
|
inputSourceMap,
|
|
181
|
+
onDevelopmentEvent,
|
|
141
182
|
}) {
|
|
142
183
|
const magicSource = new MagicString(code);
|
|
143
184
|
let didRewrite = false;
|
|
@@ -169,13 +210,17 @@ async function rewriteRelativeSpecifiers({
|
|
|
169
210
|
continue;
|
|
170
211
|
}
|
|
171
212
|
|
|
213
|
+
if (mode === "development" && isPathOutsideProject(projectRoot, resolvedImportPath)) {
|
|
214
|
+
reportDevelopmentExternalImport(projectRoot, sourcePath, resolvedImportPath, onDevelopmentEvent);
|
|
215
|
+
}
|
|
216
|
+
|
|
172
217
|
const importerOutputPath = toOutputPath(projectRoot, outputRoot, sourcePath);
|
|
173
218
|
let compiledImportPath = null;
|
|
174
219
|
|
|
175
220
|
if (shouldCompileModule(resolvedImportPath)) {
|
|
176
221
|
compiledImportPath = await compileModule(resolvedImportPath);
|
|
177
222
|
} else if (isStaticAssetPath(resolvedImportPath)) {
|
|
178
|
-
const relativeAssetPath =
|
|
223
|
+
const relativeAssetPath = toOutputRelativePath(projectRoot, resolvedImportPath);
|
|
179
224
|
const assetOutputPath = path.join(outputRoot, relativeAssetPath);
|
|
180
225
|
const stubOutputPath = `${assetOutputPath}.mjs`;
|
|
181
226
|
|
|
@@ -293,6 +338,14 @@ export function invalidateDevelopmentCompilationCache(projectRoot, changedPaths
|
|
|
293
338
|
: null;
|
|
294
339
|
let invalidated = false;
|
|
295
340
|
|
|
341
|
+
if (normalizedChangedPaths == null) {
|
|
342
|
+
for (const warningKey of developmentExternalImportWarnings) {
|
|
343
|
+
if (warningKey.startsWith(prefix)) {
|
|
344
|
+
developmentExternalImportWarnings.delete(warningKey);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
296
349
|
for (const key of developmentGraphCache.keys()) {
|
|
297
350
|
if (!key.startsWith(prefix)) {
|
|
298
351
|
continue;
|
|
@@ -323,6 +376,7 @@ async function compileModuleGraphUncached(entryPath, options = {}) {
|
|
|
323
376
|
ssr = false,
|
|
324
377
|
target = "server",
|
|
325
378
|
staticAssetPublicUrls = null,
|
|
379
|
+
onDevelopmentEvent,
|
|
326
380
|
} = options;
|
|
327
381
|
|
|
328
382
|
const outputRoot = getTypedOutputRoot(projectRoot, mode, target);
|
|
@@ -361,6 +415,7 @@ async function compileModuleGraphUncached(entryPath, options = {}) {
|
|
|
361
415
|
sourceMaps,
|
|
362
416
|
outputPath,
|
|
363
417
|
inputSourceMap: transformed.map ?? null,
|
|
418
|
+
onDevelopmentEvent,
|
|
364
419
|
});
|
|
365
420
|
|
|
366
421
|
await fs.writeFile(outputPath, rewritten.code, "utf8");
|
|
@@ -10,17 +10,20 @@ import {
|
|
|
10
10
|
createAssetResolver,
|
|
11
11
|
createHydrationBootstrap,
|
|
12
12
|
createStaticAssetPublicUrlMap,
|
|
13
|
+
buildSharedVendorRuntime,
|
|
13
14
|
emitBundledClientAssetsWithState,
|
|
14
15
|
getAssetByPublicUrl,
|
|
15
16
|
getSharedOutputRoot,
|
|
16
17
|
normalizeHydrationDataForClient,
|
|
17
18
|
normalizeClientAssetManifest,
|
|
19
|
+
resetDevelopmentAssetCaches,
|
|
18
20
|
resolveRouteClientImports,
|
|
19
21
|
resolveSharedVendorModuleUrl,
|
|
20
22
|
resolveBrowserPackageAssetFilePath,
|
|
21
23
|
rewriteHydrationDataScript,
|
|
22
24
|
} from "./client-assets.js";
|
|
23
25
|
import { loadEvolitConfig } from "./config.js";
|
|
26
|
+
import { DEV_DIRECTORY, INTERNAL_DIRECTORY } from "./constants.js";
|
|
24
27
|
import { createRouteResolver } from "./render.js";
|
|
25
28
|
import {
|
|
26
29
|
createCachedRouteResponse,
|
|
@@ -212,7 +215,14 @@ export function createResponseCacheController({ responseCacheRuntime }) {
|
|
|
212
215
|
};
|
|
213
216
|
}
|
|
214
217
|
|
|
215
|
-
export async function createRequestRenderer({
|
|
218
|
+
export async function createRequestRenderer({
|
|
219
|
+
projectRoot,
|
|
220
|
+
mode,
|
|
221
|
+
assetManifest,
|
|
222
|
+
routeResolver,
|
|
223
|
+
responseCacheRuntime,
|
|
224
|
+
onDevelopmentEvent,
|
|
225
|
+
}) {
|
|
216
226
|
let currentAssetManifest = normalizeClientAssetManifest(assetManifest);
|
|
217
227
|
let currentAssetResolver = createAssetResolver(projectRoot, {
|
|
218
228
|
assetManifest: currentAssetManifest,
|
|
@@ -241,11 +251,18 @@ export async function createRequestRenderer({ projectRoot, mode, assetManifest,
|
|
|
241
251
|
return nextWork;
|
|
242
252
|
}
|
|
243
253
|
|
|
254
|
+
function reportDevelopmentEvent(event) {
|
|
255
|
+
if (mode === "development" && typeof onDevelopmentEvent === "function") {
|
|
256
|
+
onDevelopmentEvent(event);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
244
260
|
function getDevClientModuleKey(clientModule) {
|
|
245
261
|
return path.resolve(projectRoot, clientModule);
|
|
246
262
|
}
|
|
247
263
|
async function createFrameworkRouteResolver() {
|
|
248
264
|
return createRouteResolver(projectRoot, mode, {
|
|
265
|
+
onDevelopmentEvent,
|
|
249
266
|
getStaticAssetPublicUrls() {
|
|
250
267
|
return createStaticAssetPublicUrlMap(currentAssetManifest);
|
|
251
268
|
},
|
|
@@ -338,6 +355,12 @@ export async function createRequestRenderer({ projectRoot, mode, assetManifest,
|
|
|
338
355
|
}
|
|
339
356
|
}
|
|
340
357
|
|
|
358
|
+
reportDevelopmentEvent({
|
|
359
|
+
type: "invalidated",
|
|
360
|
+
changedPathCount: normalizedChangedPaths?.size ?? null,
|
|
361
|
+
affectedClientEntryCount: affectedClientModules.size,
|
|
362
|
+
});
|
|
363
|
+
|
|
341
364
|
if (usesFrameworkRouteResolver) {
|
|
342
365
|
effectiveRouteResolver = await createFrameworkRouteResolver();
|
|
343
366
|
}
|
|
@@ -360,6 +383,7 @@ export async function createRequestRenderer({ projectRoot, mode, assetManifest,
|
|
|
360
383
|
mode,
|
|
361
384
|
sourceMaps: false,
|
|
362
385
|
target: "client",
|
|
386
|
+
onDevelopmentEvent,
|
|
363
387
|
});
|
|
364
388
|
}
|
|
365
389
|
return;
|
|
@@ -378,6 +402,7 @@ export async function createRequestRenderer({ projectRoot, mode, assetManifest,
|
|
|
378
402
|
mode,
|
|
379
403
|
sourceMaps: true,
|
|
380
404
|
target: "client",
|
|
405
|
+
onDevelopmentEvent,
|
|
381
406
|
});
|
|
382
407
|
devPreparedClientModules.add(clientModuleKey);
|
|
383
408
|
devClientModuleDependencies.set(
|
|
@@ -392,10 +417,16 @@ export async function createRequestRenderer({ projectRoot, mode, assetManifest,
|
|
|
392
417
|
|
|
393
418
|
if (!discoveredClientEntry && currentAssetManifest) {
|
|
394
419
|
developmentMetrics.clientArtifactCacheHits += 1;
|
|
420
|
+
reportDevelopmentEvent({ type: "client-assets-cache-hit" });
|
|
395
421
|
return;
|
|
396
422
|
}
|
|
397
423
|
|
|
398
424
|
developmentMetrics.clientArtifactBuilds += 1;
|
|
425
|
+
const buildStartedAt = performance.now();
|
|
426
|
+
reportDevelopmentEvent({
|
|
427
|
+
type: "client-assets-building",
|
|
428
|
+
entryCount: devBundledEntries.size,
|
|
429
|
+
});
|
|
399
430
|
const bundledClientAssets = await emitBundledClientAssetsWithState(projectRoot, {
|
|
400
431
|
mode: "development",
|
|
401
432
|
entryClientModules: devBundledEntries,
|
|
@@ -415,6 +446,11 @@ export async function createRequestRenderer({ projectRoot, mode, assetManifest,
|
|
|
415
446
|
},
|
|
416
447
|
) ?? currentHydrationModuleUrl;
|
|
417
448
|
devRollupCache = bundledClientAssets.rollupCache;
|
|
449
|
+
reportDevelopmentEvent({
|
|
450
|
+
type: "client-assets-ready",
|
|
451
|
+
durationMs: Math.round(performance.now() - buildStartedAt),
|
|
452
|
+
entryCount: devBundledEntries.size,
|
|
453
|
+
});
|
|
418
454
|
});
|
|
419
455
|
},
|
|
420
456
|
async resolveRoutePolicy(request) {
|
|
@@ -453,7 +489,26 @@ export async function createDeploymentRuntime({
|
|
|
453
489
|
assetManifest,
|
|
454
490
|
responseCacheRuntime,
|
|
455
491
|
routeResolver,
|
|
492
|
+
onDevelopmentEvent,
|
|
456
493
|
} = {}) {
|
|
494
|
+
function reportDevelopmentEvent(event) {
|
|
495
|
+
if (mode === "development" && typeof onDevelopmentEvent === "function") {
|
|
496
|
+
onDevelopmentEvent(event);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (mode === "development") {
|
|
501
|
+
reportDevelopmentEvent({ type: "initializing" });
|
|
502
|
+
const developmentRoot = path.join(projectRoot, INTERNAL_DIRECTORY, DEV_DIRECTORY);
|
|
503
|
+
await fs.rm(developmentRoot, { recursive: true, force: true });
|
|
504
|
+
invalidateDevelopmentCompilationCache(projectRoot);
|
|
505
|
+
resetDevelopmentAssetCaches(projectRoot);
|
|
506
|
+
await buildSharedVendorRuntime(projectRoot, "development", {
|
|
507
|
+
additionalEntrySpecifiers: [],
|
|
508
|
+
});
|
|
509
|
+
reportDevelopmentEvent({ type: "vendor-runtime-ready" });
|
|
510
|
+
}
|
|
511
|
+
|
|
457
512
|
const effectiveResponseCacheRuntime = responseCacheRuntime
|
|
458
513
|
?? await resolveResponseCacheRuntime(
|
|
459
514
|
projectRoot,
|
|
@@ -461,7 +516,7 @@ export async function createDeploymentRuntime({
|
|
|
461
516
|
await loadEvolitConfig(projectRoot),
|
|
462
517
|
);
|
|
463
518
|
const runtimeState = {
|
|
464
|
-
assetManifest: normalizeClientAssetManifest(assetManifest),
|
|
519
|
+
assetManifest: mode === "development" ? null : normalizeClientAssetManifest(assetManifest),
|
|
465
520
|
};
|
|
466
521
|
const assets = createPublicAssetOrigin({
|
|
467
522
|
projectRoot,
|
|
@@ -476,6 +531,7 @@ export async function createDeploymentRuntime({
|
|
|
476
531
|
assetManifest: runtimeState.assetManifest,
|
|
477
532
|
responseCacheRuntime: effectiveResponseCacheRuntime,
|
|
478
533
|
routeResolver,
|
|
534
|
+
onDevelopmentEvent,
|
|
479
535
|
});
|
|
480
536
|
|
|
481
537
|
return {
|
package/src/render.js
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
isEvolitHttpSignal,
|
|
13
13
|
runWithRequestContext,
|
|
14
14
|
} from "./request-context.js";
|
|
15
|
+
import { terminal } from "./terminal.js";
|
|
15
16
|
import {
|
|
16
17
|
LITSX_COMPONENT,
|
|
17
18
|
LITSX_SERVER_COMPONENT,
|
|
@@ -89,7 +90,10 @@ function createBoundaryError(error, { mode, route, reportRouteError }) {
|
|
|
89
90
|
if (typeof reportRouteError === "function") {
|
|
90
91
|
reportRouteError(report);
|
|
91
92
|
} else {
|
|
92
|
-
console.error(
|
|
93
|
+
console.error(
|
|
94
|
+
`${terminal.red("[evolit]")} ${terminal.red("✖")} Route rendering failed ${terminal.dim(`(${digest})`)} at ${terminal.cyan(route.pathname)}`,
|
|
95
|
+
originalError,
|
|
96
|
+
);
|
|
93
97
|
}
|
|
94
98
|
|
|
95
99
|
const boundaryError = new Error("An unexpected server error occurred.");
|
|
@@ -133,6 +137,7 @@ function createRouteModuleOptions(projectRoot, mode, options = {}) {
|
|
|
133
137
|
mode,
|
|
134
138
|
ssr: true,
|
|
135
139
|
staticAssetPublicUrls: resolvedStaticAssetPublicUrls,
|
|
140
|
+
onDevelopmentEvent: options.onDevelopmentEvent,
|
|
136
141
|
};
|
|
137
142
|
}
|
|
138
143
|
|
package/src/server.js
CHANGED
|
@@ -9,6 +9,7 @@ import { APP_DIRECTORY, BUILD_DIRECTORY, INTERNAL_DIRECTORY, MANIFEST_FILENAME }
|
|
|
9
9
|
import { normalizeClientAssetManifest } from "./client-assets.js";
|
|
10
10
|
import { pathExists, readJson } from "./fs-utils.js";
|
|
11
11
|
import { createDefaultRouteCacheKey, resolveResponseCacheRuntime } from "./response-cache.js";
|
|
12
|
+
import { terminal } from "./terminal.js";
|
|
12
13
|
|
|
13
14
|
function getPort(explicitPort) {
|
|
14
15
|
const port = explicitPort ?? process.env.PORT ?? "3000";
|
|
@@ -139,6 +140,58 @@ async function createRecursiveDirectoryWatcher(rootDirectory, onChange) {
|
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
async function createServer(projectRoot, mode, explicitPort, options = {}) {
|
|
143
|
+
const reportDevelopmentEvent = mode === "development"
|
|
144
|
+
? options.onDevelopmentEvent ?? ((event) => {
|
|
145
|
+
switch (event.type) {
|
|
146
|
+
case "initializing":
|
|
147
|
+
console.log(`${terminal.cyan("[evolit]")} ${terminal.yellow("○")} Preparing development runtime…`);
|
|
148
|
+
break;
|
|
149
|
+
case "vendor-runtime-ready":
|
|
150
|
+
console.log(`${terminal.cyan("[evolit]")} ${terminal.green("✓")} Vendor runtime ready.`);
|
|
151
|
+
break;
|
|
152
|
+
case "client-assets-building":
|
|
153
|
+
console.log(`${terminal.cyan("[evolit]")} ${terminal.magenta("○")} Compiling client assets (${event.entryCount} entries)…`);
|
|
154
|
+
break;
|
|
155
|
+
case "client-assets-ready":
|
|
156
|
+
console.log(`${terminal.cyan("[evolit]")} ${terminal.green("✓")} Client assets compiled in ${terminal.dim(`${event.durationMs}ms`)} (${event.entryCount} entries).`);
|
|
157
|
+
break;
|
|
158
|
+
case "client-assets-cache-hit":
|
|
159
|
+
console.log(`${terminal.cyan("[evolit]")} ${terminal.blue("●")} Client assets served from cache.`);
|
|
160
|
+
break;
|
|
161
|
+
case "invalidated":
|
|
162
|
+
console.log(
|
|
163
|
+
`${terminal.cyan("[evolit]")} ${terminal.yellow("△")} Source change detected${event.changedPathCount == null ? "" : ` (${event.changedPathCount} files)`}; invalidated ${event.affectedClientEntryCount} client entries.`,
|
|
164
|
+
);
|
|
165
|
+
break;
|
|
166
|
+
case "external-import":
|
|
167
|
+
console.warn(
|
|
168
|
+
`${terminal.yellow("[evolit]")} ${terminal.yellow("⚠")} Development import ${terminal.cyan(JSON.stringify(event.resolvedImportPath))} from ${terminal.cyan(JSON.stringify(event.sourcePath))} is outside the project root. It will be emitted under ${terminal.cyan("/_evolit/static/__external__/")}.`,
|
|
169
|
+
);
|
|
170
|
+
break;
|
|
171
|
+
case "request": {
|
|
172
|
+
const statusColor = event.status >= 500
|
|
173
|
+
? terminal.red
|
|
174
|
+
: event.status >= 400
|
|
175
|
+
? terminal.yellow
|
|
176
|
+
: event.status >= 300
|
|
177
|
+
? terminal.blue
|
|
178
|
+
: terminal.green;
|
|
179
|
+
console.log(
|
|
180
|
+
`${terminal.cyan("[evolit]")} ${terminal.dim(event.method)} ${event.pathname} ${statusColor(event.status)} in ${terminal.dim(`${event.durationMs}ms`)}${event.cacheState ? ` ${terminal.blue(`(${event.cacheState})`)}` : ""}`,
|
|
181
|
+
);
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
case "request-error":
|
|
185
|
+
console.error(
|
|
186
|
+
`${terminal.red("[evolit]")} ${terminal.red("✖")} ${event.method} ${event.pathname} failed`,
|
|
187
|
+
event.error,
|
|
188
|
+
);
|
|
189
|
+
break;
|
|
190
|
+
default:
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
: null;
|
|
142
195
|
const evolitConfig = options.evolitConfig ?? await loadEvolitConfig(projectRoot);
|
|
143
196
|
const responseCacheRuntime = options.responseCacheStore
|
|
144
197
|
? {
|
|
@@ -151,6 +204,7 @@ async function createServer(projectRoot, mode, explicitPort, options = {}) {
|
|
|
151
204
|
mode,
|
|
152
205
|
assetManifest: normalizeClientAssetManifest(options.assetManifest),
|
|
153
206
|
responseCacheRuntime,
|
|
207
|
+
onDevelopmentEvent: reportDevelopmentEvent,
|
|
154
208
|
});
|
|
155
209
|
const port = getPort(explicitPort);
|
|
156
210
|
const liveReloadServer = mode === "development" ? new WebSocketServer({ noServer: true }) : null;
|
|
@@ -226,6 +280,7 @@ async function createServer(projectRoot, mode, explicitPort, options = {}) {
|
|
|
226
280
|
headers: req.headers,
|
|
227
281
|
...(method === "GET" || method === "HEAD" ? {} : { body: req, duplex: "half" }),
|
|
228
282
|
});
|
|
283
|
+
const requestStartedAt = performance.now();
|
|
229
284
|
const response = await deploymentRuntime.handle(request);
|
|
230
285
|
const servedResponse = mode === "development"
|
|
231
286
|
? injectLiveReloadSnippet(response)
|
|
@@ -233,7 +288,24 @@ async function createServer(projectRoot, mode, explicitPort, options = {}) {
|
|
|
233
288
|
|
|
234
289
|
res.writeHead(servedResponse.status, servedResponse.headers);
|
|
235
290
|
await writeResponseBody(res, servedResponse.body);
|
|
291
|
+
if (mode === "development" && !pathname.startsWith("/_evolit/")) {
|
|
292
|
+
const durationMs = Math.round(performance.now() - requestStartedAt);
|
|
293
|
+
reportDevelopmentEvent({
|
|
294
|
+
type: "request",
|
|
295
|
+
method,
|
|
296
|
+
pathname,
|
|
297
|
+
status: servedResponse.status,
|
|
298
|
+
durationMs,
|
|
299
|
+
cacheState: servedResponse.headers?.["x-evolit-cache"] ?? null,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
236
302
|
} catch (error) {
|
|
303
|
+
reportDevelopmentEvent?.({
|
|
304
|
+
type: "request-error",
|
|
305
|
+
method: req.method ?? "GET",
|
|
306
|
+
pathname: req.url ?? "/",
|
|
307
|
+
error,
|
|
308
|
+
});
|
|
237
309
|
res.writeHead(500, { "content-type": "text/plain; charset=utf-8" });
|
|
238
310
|
res.end(error instanceof Error ? error.stack ?? error.message : String(error));
|
|
239
311
|
}
|
package/src/terminal.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const enabled = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
|
|
2
|
+
|
|
3
|
+
function style(open, value) {
|
|
4
|
+
return enabled ? `\u001B[${open}m${value}\u001B[0m` : String(value);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const terminal = {
|
|
8
|
+
bold(value) {
|
|
9
|
+
return style("1", value);
|
|
10
|
+
},
|
|
11
|
+
dim(value) {
|
|
12
|
+
return style("2", value);
|
|
13
|
+
},
|
|
14
|
+
red(value) {
|
|
15
|
+
return style("31", value);
|
|
16
|
+
},
|
|
17
|
+
green(value) {
|
|
18
|
+
return style("32", value);
|
|
19
|
+
},
|
|
20
|
+
yellow(value) {
|
|
21
|
+
return style("33", value);
|
|
22
|
+
},
|
|
23
|
+
blue(value) {
|
|
24
|
+
return style("34", value);
|
|
25
|
+
},
|
|
26
|
+
magenta(value) {
|
|
27
|
+
return style("35", value);
|
|
28
|
+
},
|
|
29
|
+
cyan(value) {
|
|
30
|
+
return style("36", value);
|
|
31
|
+
},
|
|
32
|
+
};
|