@zenithbuild/cli 0.7.5 → 0.7.7
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/adapters/adapter-netlify.js +0 -8
- package/dist/adapters/adapter-vercel.js +6 -14
- package/dist/adapters/copy-hosted-page-runtime.js +2 -1
- package/dist/build/hoisted-code-transforms.d.ts +4 -1
- package/dist/build/hoisted-code-transforms.js +5 -3
- package/dist/build/page-ir-normalization.d.ts +1 -1
- package/dist/build/page-ir-normalization.js +33 -3
- package/dist/build/page-loop.js +46 -2
- package/dist/dev-build-session/helpers.d.ts +29 -0
- package/dist/dev-build-session/helpers.js +223 -0
- package/dist/dev-build-session/session.d.ts +24 -0
- package/dist/dev-build-session/session.js +204 -0
- package/dist/dev-build-session/state.d.ts +37 -0
- package/dist/dev-build-session/state.js +17 -0
- package/dist/dev-build-session.d.ts +1 -24
- package/dist/dev-build-session.js +1 -434
- package/dist/dev-server/css-state.d.ts +7 -0
- package/dist/dev-server/css-state.js +92 -0
- package/dist/dev-server/not-found.d.ts +23 -0
- package/dist/dev-server/not-found.js +129 -0
- package/dist/dev-server/request-handler.d.ts +1 -0
- package/dist/dev-server/request-handler.js +376 -0
- package/dist/dev-server/route-check.d.ts +9 -0
- package/dist/dev-server/route-check.js +100 -0
- package/dist/dev-server/watcher.d.ts +5 -0
- package/dist/dev-server/watcher.js +216 -0
- package/dist/dev-server.js +123 -924
- package/dist/images/payload.js +4 -0
- package/dist/manifest.js +46 -1
- package/dist/preview/create-preview-server.d.ts +18 -0
- package/dist/preview/create-preview-server.js +71 -0
- package/dist/preview/manifest.d.ts +42 -0
- package/dist/preview/manifest.js +57 -0
- package/dist/preview/paths.d.ts +3 -0
- package/dist/preview/paths.js +38 -0
- package/dist/preview/payload.d.ts +6 -0
- package/dist/preview/payload.js +34 -0
- package/dist/preview/request-handler.d.ts +1 -0
- package/dist/preview/request-handler.js +300 -0
- package/dist/preview/server-runner.d.ts +49 -0
- package/dist/preview/server-runner.js +220 -0
- package/dist/preview/server-script-runner-template.d.ts +1 -0
- package/dist/preview/server-script-runner-template.js +425 -0
- package/dist/preview.d.ts +5 -112
- package/dist/preview.js +7 -1119
- package/dist/resource-response.d.ts +15 -0
- package/dist/resource-response.js +91 -2
- package/dist/server-contract/constants.d.ts +5 -0
- package/dist/server-contract/constants.js +5 -0
- package/dist/server-contract/export-validation.d.ts +5 -0
- package/dist/server-contract/export-validation.js +59 -0
- package/dist/server-contract/json-serializable.d.ts +1 -0
- package/dist/server-contract/json-serializable.js +52 -0
- package/dist/server-contract/resolve.d.ts +15 -0
- package/dist/server-contract/resolve.js +271 -0
- package/dist/server-contract/result-helpers.d.ts +51 -0
- package/dist/server-contract/result-helpers.js +59 -0
- package/dist/server-contract/route-result-validation.d.ts +2 -0
- package/dist/server-contract/route-result-validation.js +73 -0
- package/dist/server-contract/stage.d.ts +6 -0
- package/dist/server-contract/stage.js +22 -0
- package/dist/server-contract.d.ts +6 -62
- package/dist/server-contract.js +9 -493
- package/dist/server-middleware.d.ts +10 -0
- package/dist/server-middleware.js +30 -0
- package/dist/server-output.js +13 -1
- package/dist/server-runtime/node-server.js +25 -3
- package/package.json +3 -3
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { existsSync, watch } from 'node:fs';
|
|
2
|
+
import { performance } from 'node:perf_hooks';
|
|
3
|
+
import { isAbsolute, relative, resolve } from 'node:path';
|
|
4
|
+
import { readChangeFingerprint } from '../dev-watch.js';
|
|
5
|
+
import { loadRouteSurfaceState } from '../preview.js';
|
|
6
|
+
export function createDevWatcher(options) {
|
|
7
|
+
const { watchRoots, resolvedOutDir, resolvedOutDirTmp, projectRoot, rebuildDebounceMs, queuedRebuildDebounceMs, buildSession, outDir, configuredBasePath, logger, startupProfile, state, syncCssStateFromBuild, broadcastEvent, trace } = options;
|
|
8
|
+
/** @type {import('fs').FSWatcher[]} */
|
|
9
|
+
let watchers = [];
|
|
10
|
+
let buildDebounce = null;
|
|
11
|
+
let queuedFiles = new Set();
|
|
12
|
+
const lastQueuedFingerprints = new Map();
|
|
13
|
+
let buildInFlight = false;
|
|
14
|
+
function isWithin(parent, child) {
|
|
15
|
+
const rel = relative(parent, child);
|
|
16
|
+
return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
|
|
17
|
+
}
|
|
18
|
+
function toDisplayPath(absPath) {
|
|
19
|
+
const rel = relative(projectRoot, absPath);
|
|
20
|
+
if (rel === '')
|
|
21
|
+
return '.';
|
|
22
|
+
if (!rel.startsWith('..') && !isAbsolute(rel)) {
|
|
23
|
+
return rel;
|
|
24
|
+
}
|
|
25
|
+
return absPath;
|
|
26
|
+
}
|
|
27
|
+
function shouldIgnoreChange(absPath) {
|
|
28
|
+
if (isWithin(resolvedOutDir, absPath)) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
if (isWithin(resolvedOutDirTmp, absPath)) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
const rel = relative(projectRoot, absPath);
|
|
35
|
+
if (rel.startsWith('..') || isAbsolute(rel)) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const segments = rel.split(/[\\/]+/g);
|
|
39
|
+
return segments.includes('node_modules')
|
|
40
|
+
|| segments.includes('.git')
|
|
41
|
+
|| segments.includes('.zenith')
|
|
42
|
+
|| segments.includes('target')
|
|
43
|
+
|| segments.includes('.turbo');
|
|
44
|
+
}
|
|
45
|
+
const triggerBuildDrain = (delayMs = rebuildDebounceMs) => {
|
|
46
|
+
if (buildDebounce !== null) {
|
|
47
|
+
clearTimeout(buildDebounce);
|
|
48
|
+
}
|
|
49
|
+
buildDebounce = setTimeout(() => {
|
|
50
|
+
buildDebounce = null;
|
|
51
|
+
void drainBuildQueue();
|
|
52
|
+
}, delayMs);
|
|
53
|
+
};
|
|
54
|
+
const drainBuildQueue = async () => {
|
|
55
|
+
if (buildInFlight) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const changedPaths = Array.from(queuedFiles);
|
|
59
|
+
const changed = changedPaths.map(toDisplayPath).sort();
|
|
60
|
+
if (changed.length === 0) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
queuedFiles.clear();
|
|
64
|
+
buildInFlight = true;
|
|
65
|
+
const cycleBuildId = state.pendingBuildId + 1;
|
|
66
|
+
state.pendingBuildId = cycleBuildId;
|
|
67
|
+
state.buildStatus = 'building';
|
|
68
|
+
logger.build(`Rebuild (id=${cycleBuildId})`);
|
|
69
|
+
broadcastEvent('build_start', { buildId: cycleBuildId, changedFiles: changed });
|
|
70
|
+
const startTime = Date.now();
|
|
71
|
+
const previousCssAssetPath = state.currentCssAssetPath;
|
|
72
|
+
const previousCssContent = state.currentCssContent;
|
|
73
|
+
const onlyCss = changed.length > 0 && changed.every((filePath) => filePath.endsWith('.css'));
|
|
74
|
+
try {
|
|
75
|
+
const buildResult = await buildSession.build({ changedFiles: changedPaths, logger });
|
|
76
|
+
const cssReady = await syncCssStateFromBuild(buildResult, cycleBuildId);
|
|
77
|
+
if (!onlyCss) {
|
|
78
|
+
state.currentRouteState = await loadRouteSurfaceState(outDir, configuredBasePath);
|
|
79
|
+
}
|
|
80
|
+
const cssChanged = cssReady && (state.currentCssAssetPath !== previousCssAssetPath ||
|
|
81
|
+
state.currentCssContent !== previousCssContent);
|
|
82
|
+
state.buildId = cycleBuildId;
|
|
83
|
+
state.buildStatus = 'ok';
|
|
84
|
+
state.buildError = null;
|
|
85
|
+
state.lastBuildMs = Date.now();
|
|
86
|
+
state.durationMs = state.lastBuildMs - startTime;
|
|
87
|
+
logger.build(`Complete (id=${cycleBuildId}, ${state.durationMs}ms)`);
|
|
88
|
+
broadcastEvent('build_complete', {
|
|
89
|
+
buildId: cycleBuildId,
|
|
90
|
+
durationMs: state.durationMs,
|
|
91
|
+
status: state.buildStatus,
|
|
92
|
+
cssHref: state.currentCssHref,
|
|
93
|
+
changedFiles: changed
|
|
94
|
+
});
|
|
95
|
+
trace('state_snapshot', {
|
|
96
|
+
status: state.buildStatus,
|
|
97
|
+
buildId: cycleBuildId,
|
|
98
|
+
cssHref: state.currentCssHref,
|
|
99
|
+
durationMs: state.durationMs,
|
|
100
|
+
changedFiles: changed
|
|
101
|
+
});
|
|
102
|
+
if (cssChanged && state.currentCssHref.length > 0) {
|
|
103
|
+
logger.css(`ready (${state.currentCssHref})`);
|
|
104
|
+
logger.hmr(`css_update (buildId=${cycleBuildId})`);
|
|
105
|
+
broadcastEvent('css_update', { href: state.currentCssHref, changedFiles: changed });
|
|
106
|
+
}
|
|
107
|
+
if (!onlyCss) {
|
|
108
|
+
logger.hmr(`reload (buildId=${cycleBuildId})`);
|
|
109
|
+
broadcastEvent('reload', { changedFiles: changed });
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
trace('css_only_update', {
|
|
113
|
+
buildId: cycleBuildId,
|
|
114
|
+
cssHref: state.currentCssHref,
|
|
115
|
+
cssChanged,
|
|
116
|
+
changedFiles: changed
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
const fullError = error instanceof Error ? error.message : String(error);
|
|
122
|
+
state.buildStatus = 'error';
|
|
123
|
+
state.buildError = {
|
|
124
|
+
message: fullError.length > 10000
|
|
125
|
+
? `${fullError.slice(0, 10000)}... (truncated)`
|
|
126
|
+
: fullError
|
|
127
|
+
};
|
|
128
|
+
state.lastBuildMs = Date.now();
|
|
129
|
+
state.durationMs = state.lastBuildMs - startTime;
|
|
130
|
+
logger.error('rebuild failed', {
|
|
131
|
+
hint: 'fix the error and save again',
|
|
132
|
+
error
|
|
133
|
+
});
|
|
134
|
+
broadcastEvent('build_error', {
|
|
135
|
+
buildId: cycleBuildId,
|
|
136
|
+
...state.buildError,
|
|
137
|
+
changedFiles: changed
|
|
138
|
+
});
|
|
139
|
+
trace('state_snapshot', {
|
|
140
|
+
status: state.buildStatus,
|
|
141
|
+
buildId: state.buildId,
|
|
142
|
+
cssHref: state.currentCssHref,
|
|
143
|
+
durationMs: state.durationMs,
|
|
144
|
+
error: state.buildError
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
buildInFlight = false;
|
|
149
|
+
if (queuedFiles.size > 0) {
|
|
150
|
+
triggerBuildDrain(queuedRebuildDebounceMs);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
function start() {
|
|
155
|
+
const watcherStartedAt = performance.now();
|
|
156
|
+
const roots = Array.from(watchRoots);
|
|
157
|
+
for (const root of roots) {
|
|
158
|
+
if (!existsSync(root))
|
|
159
|
+
continue;
|
|
160
|
+
try {
|
|
161
|
+
const watcher = watch(root, { recursive: true }, (_eventType, filename) => {
|
|
162
|
+
if (!filename) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const changedPath = resolve(root, String(filename));
|
|
166
|
+
if (shouldIgnoreChange(changedPath)) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
void (async () => {
|
|
170
|
+
const fingerprint = await readChangeFingerprint(changedPath);
|
|
171
|
+
if (lastQueuedFingerprints.get(changedPath) === fingerprint) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
lastQueuedFingerprints.set(changedPath, fingerprint);
|
|
175
|
+
queuedFiles.add(changedPath);
|
|
176
|
+
triggerBuildDrain();
|
|
177
|
+
})();
|
|
178
|
+
});
|
|
179
|
+
watchers.push(watcher);
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// fs.watch recursive may not be supported on this platform/root
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
startupProfile.emit('watcher_ready', {
|
|
186
|
+
roots: roots.length,
|
|
187
|
+
activeWatchers: watchers.length,
|
|
188
|
+
durationMs: startupProfile.roundMs(performance.now() - watcherStartedAt)
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
function close() {
|
|
192
|
+
if (buildDebounce !== null) {
|
|
193
|
+
clearTimeout(buildDebounce);
|
|
194
|
+
buildDebounce = null;
|
|
195
|
+
}
|
|
196
|
+
for (const watcher of watchers) {
|
|
197
|
+
try {
|
|
198
|
+
watcher.close();
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
// ignore close errors
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
watchers = [];
|
|
205
|
+
queuedFiles.clear();
|
|
206
|
+
lastQueuedFingerprints.clear();
|
|
207
|
+
}
|
|
208
|
+
function activeWatcherCount() {
|
|
209
|
+
return watchers.length;
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
start,
|
|
213
|
+
close,
|
|
214
|
+
activeWatcherCount
|
|
215
|
+
};
|
|
216
|
+
}
|