@caryhu/codemine-forge 0.0.1-alpha.0 → 0.0.1-alpha.2
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/README.md +8 -4
- package/dist/build-pipeline/browser-bundle.d.ts +2 -1
- package/dist/build-pipeline/browser-bundle.js +384 -24
- package/dist/build-pipeline/diagnostics.d.ts +1 -0
- package/dist/build-pipeline/diagnostics.js +11 -2
- package/dist/build-pipeline/index.d.ts +2 -2
- package/dist/build-pipeline/index.js +2 -1
- package/dist/build-pipeline/pipeline.d.ts +2 -0
- package/dist/build-pipeline/pipeline.js +149 -28
- package/dist/build-pipeline/types.d.ts +24 -2
- package/dist/build-pipeline/vite-config.d.ts +8 -0
- package/dist/build-pipeline/vite-config.js +215 -0
- package/dist/dev-server-preview/index.d.ts +2 -2
- package/dist/dev-server-preview/index.js +2 -1
- package/dist/dev-server-preview/preview-document.d.ts +2 -1
- package/dist/dev-server-preview/preview-document.js +203 -1
- package/dist/dev-server-preview/server.d.ts +8 -1
- package/dist/dev-server-preview/server.js +248 -33
- package/dist/dev-server-preview/types.d.ts +40 -1
- package/dist/forge-controller.js +1 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.js +16 -2
- package/dist/package-resolution/diagnostics.d.ts +1 -0
- package/dist/package-resolution/diagnostics.js +10 -1
- package/dist/package-resolution/errors.d.ts +2 -1
- package/dist/package-resolution/errors.js +2 -1
- package/dist/package-resolution/index.d.ts +2 -2
- package/dist/package-resolution/index.js +2 -1
- package/dist/package-resolution/resolver.d.ts +3 -0
- package/dist/package-resolution/resolver.js +123 -29
- package/dist/package-resolution/types.d.ts +7 -1
- package/dist/telemetry/index.d.ts +68 -0
- package/dist/telemetry/index.js +65 -0
- package/dist/terminal-commands/npm-build.js +3 -1
- package/dist/terminal-commands/npm-dev.js +3 -1
- package/dist/terminal-commands/npm-install.js +7 -2
- package/dist/terminal-commands/terminal.d.ts +1 -0
- package/dist/terminal-commands/terminal.js +1 -0
- package/dist/terminal-commands/types.d.ts +2 -0
- package/dist/virtual-file-system/diagnostics.d.ts +1 -0
- package/dist/virtual-file-system/diagnostics.js +9 -1
- package/dist/virtual-file-system/index.d.ts +4 -2
- package/dist/virtual-file-system/index.js +7 -1
- package/dist/virtual-file-system/snapshot-serialization.d.ts +3 -0
- package/dist/virtual-file-system/snapshot-serialization.js +242 -0
- package/dist/virtual-file-system/storage.d.ts +1 -1
- package/dist/virtual-file-system/storage.js +41 -18
- package/dist/virtual-file-system/types.d.ts +28 -1
- package/dist/virtual-file-system/types.js +2 -0
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createForgePreviewDocument = void 0;
|
|
3
|
+
exports.createForgePreviewErrorOverlayDocument = exports.createForgePreviewDocument = void 0;
|
|
4
4
|
const DEFAULT_HTML_SHELL = [
|
|
5
5
|
"<!doctype html>",
|
|
6
6
|
"<html>",
|
|
@@ -15,6 +15,9 @@ const DEFAULT_HTML_SHELL = [
|
|
|
15
15
|
"</html>",
|
|
16
16
|
].join("\n");
|
|
17
17
|
const PREVIEW_MODULE_SPECIFIER_PREFIX = "__forge_preview__/";
|
|
18
|
+
const PREVIEW_ERROR_OVERLAY_START = "<!-- forge-preview-error-overlay:start -->";
|
|
19
|
+
const PREVIEW_ERROR_OVERLAY_END = "<!-- forge-preview-error-overlay:end -->";
|
|
20
|
+
const PREVIEW_RUNTIME_ERROR_CLIENT_ID = "forge-preview-runtime-error-client";
|
|
18
21
|
function createForgePreviewDocument(options) {
|
|
19
22
|
const htmlShell = options.htmlShell ?? DEFAULT_HTML_SHELL;
|
|
20
23
|
const browserBundle = options.buildResult.browserBundle;
|
|
@@ -26,6 +29,7 @@ function createForgePreviewDocument(options) {
|
|
|
26
29
|
: previewModuleSpecifiers.get(browserBundle.entryOutputPath) ??
|
|
27
30
|
browserBundle.entryOutputPath;
|
|
28
31
|
const metadata = {
|
|
32
|
+
adapter: options.adapter,
|
|
29
33
|
browserBundle: browserBundle === undefined
|
|
30
34
|
? undefined
|
|
31
35
|
: {
|
|
@@ -58,6 +62,7 @@ function createForgePreviewDocument(options) {
|
|
|
58
62
|
previewImportMap === undefined
|
|
59
63
|
? ""
|
|
60
64
|
: `<script type="importmap" id="forge-browser-bundle-importmap">${escapeJsonForScript(JSON.stringify(previewImportMap))}</script>`,
|
|
65
|
+
browserBundle === undefined ? "" : createPreviewRuntimeErrorClientScript(),
|
|
61
66
|
browserBundle === undefined
|
|
62
67
|
? ""
|
|
63
68
|
: `<script type="module" id="forge-browser-bundle-entry">import ${JSON.stringify(previewEntrySpecifier)};</script>`,
|
|
@@ -72,6 +77,16 @@ function createForgePreviewDocument(options) {
|
|
|
72
77
|
};
|
|
73
78
|
}
|
|
74
79
|
exports.createForgePreviewDocument = createForgePreviewDocument;
|
|
80
|
+
function createForgePreviewErrorOverlayDocument(previewDocument, diagnostics) {
|
|
81
|
+
if (diagnostics.length === 0) {
|
|
82
|
+
return previewDocument;
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
...previewDocument,
|
|
86
|
+
html: injectBeforeBodyEnd(removePreviewErrorOverlay(previewDocument.html), createPreviewErrorOverlay(diagnostics)),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
exports.createForgePreviewErrorOverlayDocument = createForgePreviewErrorOverlayDocument;
|
|
75
90
|
function createPreviewImportMap(outputFiles, previewModuleSpecifiers) {
|
|
76
91
|
const importMapFile = outputFiles.find((file) => file.contentType === "application/importmap+json");
|
|
77
92
|
if (importMapFile === undefined) {
|
|
@@ -130,12 +145,199 @@ function injectBeforeBodyEnd(html, injection) {
|
|
|
130
145
|
}
|
|
131
146
|
return `${html}\n${injection}`;
|
|
132
147
|
}
|
|
148
|
+
function createPreviewRuntimeErrorClientScript() {
|
|
149
|
+
return [
|
|
150
|
+
`<script id="${PREVIEW_RUNTIME_ERROR_CLIENT_ID}">`,
|
|
151
|
+
"(function(){",
|
|
152
|
+
' var overlaySelector = "[data-forge-preview-runtime-error-overlay]";',
|
|
153
|
+
' var styleSelector = "style[data-forge-preview-runtime-error-overlay-style]";',
|
|
154
|
+
" function stringify(value){",
|
|
155
|
+
' if (value instanceof Error) return value.message || value.name || "Runtime error";',
|
|
156
|
+
' if (typeof value === "string") return value;',
|
|
157
|
+
" if (value === undefined) return \"undefined\";",
|
|
158
|
+
" if (value === null) return \"null\";",
|
|
159
|
+
" try {",
|
|
160
|
+
" var json = JSON.stringify(value);",
|
|
161
|
+
" if (typeof json === \"string\") return json;",
|
|
162
|
+
" } catch (error) {}",
|
|
163
|
+
" return String(value);",
|
|
164
|
+
" }",
|
|
165
|
+
" function stackFor(value){",
|
|
166
|
+
" return value instanceof Error && typeof value.stack === \"string\" && value.stack.length > 0",
|
|
167
|
+
" ? value.stack",
|
|
168
|
+
" : undefined;",
|
|
169
|
+
" }",
|
|
170
|
+
" function formatLocation(event){",
|
|
171
|
+
" var parts = [];",
|
|
172
|
+
" if (typeof event.filename === \"string\" && event.filename.length > 0) parts.push(event.filename);",
|
|
173
|
+
" if (typeof event.lineno === \"number\" && event.lineno > 0) parts.push(\"line \" + event.lineno);",
|
|
174
|
+
" if (typeof event.colno === \"number\" && event.colno > 0) parts.push(\"column \" + event.colno);",
|
|
175
|
+
" return parts.length > 0 ? parts.join(\" · \") : undefined;",
|
|
176
|
+
" }",
|
|
177
|
+
" function createTextElement(tagName, attributeName, text){",
|
|
178
|
+
" var element = document.createElement(tagName);",
|
|
179
|
+
" element.setAttribute(attributeName, \"\");",
|
|
180
|
+
" element.textContent = text;",
|
|
181
|
+
" return element;",
|
|
182
|
+
" }",
|
|
183
|
+
" function ensureStyle(){",
|
|
184
|
+
" if (document.querySelector(styleSelector)) return;",
|
|
185
|
+
" var style = document.createElement(\"style\");",
|
|
186
|
+
" style.setAttribute(\"data-forge-preview-runtime-error-overlay-style\", \"\");",
|
|
187
|
+
" style.textContent = [",
|
|
188
|
+
' "[data-forge-preview-runtime-error-overlay]{position:fixed;inset:0;z-index:2147483647;display:flex;align-items:flex-start;justify-content:center;padding:32px 16px;background:rgba(15,23,42,.72);color:#f8fafc;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,\'Segoe UI\',sans-serif;box-sizing:border-box;}",',
|
|
189
|
+
' "[data-forge-preview-runtime-error-overlay] *{box-sizing:border-box;}",',
|
|
190
|
+
' "[data-forge-preview-runtime-error-overlay-panel]{width:min(760px,100%);max-height:calc(100vh - 64px);overflow:auto;border:1px solid rgba(248,113,113,.45);border-radius:8px;background:#111827;box-shadow:0 24px 80px rgba(0,0,0,.42);padding:20px;}",',
|
|
191
|
+
' "[data-forge-preview-runtime-error-overlay-kicker]{margin:0 0 6px;color:#fca5a5;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;}",',
|
|
192
|
+
' "[data-forge-preview-runtime-error-overlay-title]{margin:0 0 16px;font-size:20px;line-height:1.25;font-weight:700;}",',
|
|
193
|
+
' "[data-forge-preview-runtime-error-overlay-message]{margin:0 0 12px;color:#f8fafc;font-size:14px;line-height:1.5;}",',
|
|
194
|
+
' "[data-forge-preview-runtime-error-overlay-detail]{margin:0;color:#cbd5e1;white-space:pre-wrap;font-family:ui-monospace,SFMono-Regular,Consolas,\'Liberation Mono\',monospace;font-size:12px;line-height:1.45;}",',
|
|
195
|
+
" ].join(\"\");",
|
|
196
|
+
" (document.head || document.documentElement).appendChild(style);",
|
|
197
|
+
" }",
|
|
198
|
+
" function showOverlay(diagnostic){",
|
|
199
|
+
" ensureStyle();",
|
|
200
|
+
" var existing = document.querySelector(overlaySelector);",
|
|
201
|
+
" if (existing && existing.parentNode) existing.parentNode.removeChild(existing);",
|
|
202
|
+
" var overlay = document.createElement(\"div\");",
|
|
203
|
+
" overlay.setAttribute(\"data-forge-preview-runtime-error-overlay\", \"\");",
|
|
204
|
+
" overlay.setAttribute(\"role\", \"alert\");",
|
|
205
|
+
" overlay.setAttribute(\"aria-live\", \"assertive\");",
|
|
206
|
+
" var panel = document.createElement(\"section\");",
|
|
207
|
+
" panel.setAttribute(\"data-forge-preview-runtime-error-overlay-panel\", \"\");",
|
|
208
|
+
" panel.appendChild(createTextElement(\"p\", \"data-forge-preview-runtime-error-overlay-kicker\", \"Forge preview\"));",
|
|
209
|
+
" panel.appendChild(createTextElement(\"h1\", \"data-forge-preview-runtime-error-overlay-title\", diagnostic.title));",
|
|
210
|
+
" panel.appendChild(createTextElement(\"p\", \"data-forge-preview-runtime-error-overlay-message\", diagnostic.message));",
|
|
211
|
+
" if (diagnostic.detail) {",
|
|
212
|
+
" panel.appendChild(createTextElement(\"pre\", \"data-forge-preview-runtime-error-overlay-detail\", diagnostic.detail));",
|
|
213
|
+
" }",
|
|
214
|
+
" overlay.appendChild(panel);",
|
|
215
|
+
" (document.body || document.documentElement).appendChild(overlay);",
|
|
216
|
+
" }",
|
|
217
|
+
" window.addEventListener(\"error\", function(event){",
|
|
218
|
+
" showOverlay({",
|
|
219
|
+
" title: \"Preview runtime error\",",
|
|
220
|
+
" message: event.error === undefined || event.error === null",
|
|
221
|
+
" ? event.message || \"Runtime error\"",
|
|
222
|
+
" : stringify(event.error),",
|
|
223
|
+
" detail: stackFor(event.error) || formatLocation(event),",
|
|
224
|
+
" });",
|
|
225
|
+
" event.preventDefault();",
|
|
226
|
+
" });",
|
|
227
|
+
" window.addEventListener(\"unhandledrejection\", function(event){",
|
|
228
|
+
" showOverlay({",
|
|
229
|
+
" title: \"Unhandled promise rejection\",",
|
|
230
|
+
" message: stringify(event.reason),",
|
|
231
|
+
" detail: stackFor(event.reason),",
|
|
232
|
+
" });",
|
|
233
|
+
" event.preventDefault();",
|
|
234
|
+
" });",
|
|
235
|
+
"})();",
|
|
236
|
+
"</script>",
|
|
237
|
+
].join("\n");
|
|
238
|
+
}
|
|
239
|
+
function createPreviewErrorOverlay(diagnostics) {
|
|
240
|
+
const items = diagnostics.map(createPreviewErrorOverlayItem).join("\n");
|
|
241
|
+
return [
|
|
242
|
+
PREVIEW_ERROR_OVERLAY_START,
|
|
243
|
+
'<style data-forge-preview-error-overlay>',
|
|
244
|
+
"[data-forge-preview-error-overlay]{position:fixed;inset:0;z-index:2147483647;display:flex;align-items:flex-start;justify-content:center;padding:32px 16px;background:rgba(15,23,42,.72);color:#f8fafc;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;box-sizing:border-box;}",
|
|
245
|
+
"[data-forge-preview-error-overlay] *{box-sizing:border-box;}",
|
|
246
|
+
"[data-forge-preview-error-overlay-panel]{width:min(760px,100%);max-height:calc(100vh - 64px);overflow:auto;border:1px solid rgba(248,113,113,.45);border-radius:8px;background:#111827;box-shadow:0 24px 80px rgba(0,0,0,.42);padding:20px;}",
|
|
247
|
+
"[data-forge-preview-error-overlay-kicker]{margin:0 0 6px;color:#fca5a5;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;}",
|
|
248
|
+
"[data-forge-preview-error-overlay-title]{margin:0 0 16px;font-size:20px;line-height:1.25;font-weight:700;}",
|
|
249
|
+
"[data-forge-preview-error-overlay-list]{display:grid;gap:12px;margin:0;padding:0;list-style:none;}",
|
|
250
|
+
"[data-forge-preview-error-overlay-item]{display:grid;gap:6px;border-top:1px solid rgba(148,163,184,.22);padding-top:12px;}",
|
|
251
|
+
"[data-forge-preview-error-overlay-code]{width:max-content;border-radius:4px;background:rgba(248,113,113,.16);color:#fecaca;padding:2px 6px;font-family:ui-monospace,SFMono-Regular,Consolas,'Liberation Mono',monospace;font-size:12px;}",
|
|
252
|
+
"[data-forge-preview-error-overlay-message]{margin:0;color:#f8fafc;font-size:14px;line-height:1.5;}",
|
|
253
|
+
"[data-forge-preview-error-overlay-detail]{margin:0;color:#cbd5e1;font-family:ui-monospace,SFMono-Regular,Consolas,'Liberation Mono',monospace;font-size:12px;line-height:1.45;}",
|
|
254
|
+
"</style>",
|
|
255
|
+
'<div data-forge-preview-error-overlay role="alert" aria-live="assertive">',
|
|
256
|
+
' <section data-forge-preview-error-overlay-panel>',
|
|
257
|
+
' <p data-forge-preview-error-overlay-kicker>Forge preview</p>',
|
|
258
|
+
' <h1 data-forge-preview-error-overlay-title>Preview refresh failed</h1>',
|
|
259
|
+
` <ul data-forge-preview-error-overlay-list>${items}</ul>`,
|
|
260
|
+
" </section>",
|
|
261
|
+
"</div>",
|
|
262
|
+
PREVIEW_ERROR_OVERLAY_END,
|
|
263
|
+
].join("\n");
|
|
264
|
+
}
|
|
265
|
+
function createPreviewErrorOverlayItem(diagnostic) {
|
|
266
|
+
const detail = formatDiagnosticDetail(diagnostic);
|
|
267
|
+
return [
|
|
268
|
+
" <li data-forge-preview-error-overlay-item>",
|
|
269
|
+
` <code data-forge-preview-error-overlay-code>${escapeHtml(diagnostic.code)}</code>`,
|
|
270
|
+
` <p data-forge-preview-error-overlay-message>${escapeHtml(diagnostic.message)}</p>`,
|
|
271
|
+
detail === undefined
|
|
272
|
+
? ""
|
|
273
|
+
: ` <p data-forge-preview-error-overlay-detail>${escapeHtml(detail)}</p>`,
|
|
274
|
+
" </li>",
|
|
275
|
+
]
|
|
276
|
+
.filter((line) => line.length > 0)
|
|
277
|
+
.join("\n");
|
|
278
|
+
}
|
|
279
|
+
function formatDiagnosticDetail(diagnostic) {
|
|
280
|
+
const parts = [
|
|
281
|
+
getStringDiagnosticProperty(diagnostic, "path"),
|
|
282
|
+
getStringDiagnosticProperty(diagnostic, "importer"),
|
|
283
|
+
getStringDiagnosticProperty(diagnostic, "importSpecifier"),
|
|
284
|
+
getStringDiagnosticProperty(diagnostic, "dependencyName"),
|
|
285
|
+
getStringDiagnosticProperty(diagnostic, "requestedRange"),
|
|
286
|
+
getStringDiagnosticProperty(diagnostic, "script"),
|
|
287
|
+
getStringDiagnosticProperty(diagnostic, "sessionId"),
|
|
288
|
+
formatLimitDiagnostic(diagnostic),
|
|
289
|
+
].filter((part) => part !== undefined && part.length > 0);
|
|
290
|
+
return parts.length === 0 ? undefined : parts.join(" · ");
|
|
291
|
+
}
|
|
292
|
+
function getStringDiagnosticProperty(diagnostic, key) {
|
|
293
|
+
const value = diagnostic[key];
|
|
294
|
+
return typeof value === "string" ? value : undefined;
|
|
295
|
+
}
|
|
296
|
+
function formatLimitDiagnostic(diagnostic) {
|
|
297
|
+
const limit = diagnostic.limit;
|
|
298
|
+
if (typeof limit !== "object" || limit === null) {
|
|
299
|
+
return undefined;
|
|
300
|
+
}
|
|
301
|
+
const name = limit.name;
|
|
302
|
+
const actual = limit.actual;
|
|
303
|
+
const maximum = limit.maximum;
|
|
304
|
+
if (typeof name !== "string" ||
|
|
305
|
+
typeof actual !== "number" ||
|
|
306
|
+
typeof maximum !== "number") {
|
|
307
|
+
return undefined;
|
|
308
|
+
}
|
|
309
|
+
return `${name}: ${actual}/${maximum}`;
|
|
310
|
+
}
|
|
311
|
+
function removePreviewErrorOverlay(html) {
|
|
312
|
+
return html.replace(new RegExp(`${escapeRegExp(PREVIEW_ERROR_OVERLAY_START)}[\\s\\S]*?${escapeRegExp(PREVIEW_ERROR_OVERLAY_END)}\\n?`, "g"), "");
|
|
313
|
+
}
|
|
133
314
|
function removeProjectModuleScripts(html) {
|
|
134
315
|
return html.replace(/<script\b(?=[^>]*\btype\s*=\s*["']module["'])[^>]*>[\s\S]*?<\/script\s*>/gi, "");
|
|
135
316
|
}
|
|
136
317
|
function escapeJsonForScript(json) {
|
|
137
318
|
return json.replace(/</g, "\\u003c");
|
|
138
319
|
}
|
|
320
|
+
function escapeHtml(value) {
|
|
321
|
+
return value.replace(/[&<>"']/g, (character) => {
|
|
322
|
+
switch (character) {
|
|
323
|
+
case "&":
|
|
324
|
+
return "&";
|
|
325
|
+
case "<":
|
|
326
|
+
return "<";
|
|
327
|
+
case ">":
|
|
328
|
+
return ">";
|
|
329
|
+
case '"':
|
|
330
|
+
return """;
|
|
331
|
+
case "'":
|
|
332
|
+
return "'";
|
|
333
|
+
default:
|
|
334
|
+
return character;
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
function escapeRegExp(value) {
|
|
339
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
340
|
+
}
|
|
139
341
|
function toBase64(bytes) {
|
|
140
342
|
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
141
343
|
let output = "";
|
|
@@ -14,10 +14,17 @@ export declare class DefaultDevServerPreview implements ForgeDevServerPreview {
|
|
|
14
14
|
stop(sessionId: string): Promise<ForgeDevSessionStopResult>;
|
|
15
15
|
getSession(sessionId: string): ForgeDevSession | undefined;
|
|
16
16
|
watch(listener: ForgeDevServerPreviewEventListener): ForgeUnsubscribe;
|
|
17
|
-
private
|
|
17
|
+
private readDevManifest;
|
|
18
18
|
private failStart;
|
|
19
19
|
private handleFileEvent;
|
|
20
|
+
private handleWorkspaceChange;
|
|
21
|
+
private wasCssPathBundled;
|
|
22
|
+
private wasWorkspaceModuleBundled;
|
|
23
|
+
private performStyleUpdate;
|
|
24
|
+
private performModuleUpdate;
|
|
20
25
|
private performRebuild;
|
|
26
|
+
private completeSuccessfulRefresh;
|
|
27
|
+
private completeFailedRebuild;
|
|
21
28
|
private createPreviewDocument;
|
|
22
29
|
private allocateSessionId;
|
|
23
30
|
private emit;
|
|
@@ -34,25 +34,35 @@ class DefaultDevServerPreview {
|
|
|
34
34
|
},
|
|
35
35
|
};
|
|
36
36
|
this.sessions.set(sessionId, record);
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
39
|
-
return this.failStart(record,
|
|
37
|
+
const devManifestResult = await this.readDevManifest();
|
|
38
|
+
if (!devManifestResult.success) {
|
|
39
|
+
return this.failStart(record, devManifestResult.diagnostics, "dev-script");
|
|
40
40
|
}
|
|
41
|
+
record.adapter = selectDevServerAdapter(devManifestResult.manifest);
|
|
42
|
+
record.session = {
|
|
43
|
+
...record.session,
|
|
44
|
+
adapter: record.adapter,
|
|
45
|
+
};
|
|
41
46
|
const buildResult = await this.options.buildPipeline.build({
|
|
42
47
|
entry: options.entry,
|
|
48
|
+
signal: options.signal,
|
|
43
49
|
});
|
|
44
50
|
if (!buildResult.success) {
|
|
45
51
|
return this.failStart(record, buildResult.diagnostics, "initial-build", buildResult);
|
|
46
52
|
}
|
|
47
|
-
const previewDocument = await this.createPreviewDocument(sessionId, previewUrl, 1, buildResult);
|
|
53
|
+
const previewDocument = await this.createPreviewDocument(sessionId, previewUrl, 1, buildResult, record.adapter);
|
|
54
|
+
record.lastSuccessfulBuildResult = buildResult;
|
|
55
|
+
record.lastSuccessfulPreviewDocument = previewDocument;
|
|
48
56
|
const session = {
|
|
49
57
|
...record.session,
|
|
50
58
|
status: "running",
|
|
59
|
+
adapter: record.adapter,
|
|
51
60
|
revision: 1,
|
|
52
61
|
updatedAt: this.now(),
|
|
53
62
|
diagnostics: buildResult.diagnostics,
|
|
54
63
|
buildResult,
|
|
55
64
|
previewDocument,
|
|
65
|
+
errorOverlayDocument: undefined,
|
|
56
66
|
};
|
|
57
67
|
record.session = session;
|
|
58
68
|
record.unsubscribe = this.options.fileSystem.watch((event) => {
|
|
@@ -72,7 +82,7 @@ class DefaultDevServerPreview {
|
|
|
72
82
|
const diagnostic = (0, diagnostics_1.createSessionNotFoundDiagnostic)(sessionId);
|
|
73
83
|
return { success: false, diagnostics: [diagnostic] };
|
|
74
84
|
}
|
|
75
|
-
return this.performRebuild(record, options.reason ?? "manual", options.entry);
|
|
85
|
+
return this.performRebuild(record, options.reason ?? "manual", options.entry, options.signal);
|
|
76
86
|
}
|
|
77
87
|
async stop(sessionId) {
|
|
78
88
|
const record = this.sessions.get(sessionId);
|
|
@@ -87,6 +97,7 @@ class DefaultDevServerPreview {
|
|
|
87
97
|
status: "stopped",
|
|
88
98
|
updatedAt: this.now(),
|
|
89
99
|
diagnostics: [],
|
|
100
|
+
errorOverlayDocument: undefined,
|
|
90
101
|
};
|
|
91
102
|
record.session = session;
|
|
92
103
|
this.emit({ type: "dev:session-stopped", session });
|
|
@@ -105,21 +116,34 @@ class DefaultDevServerPreview {
|
|
|
105
116
|
this.listeners.delete(listener);
|
|
106
117
|
};
|
|
107
118
|
}
|
|
108
|
-
async
|
|
119
|
+
async readDevManifest() {
|
|
109
120
|
try {
|
|
110
121
|
const manifest = await (0, package_resolution_1.readPackageManifest)(this.options.fileSystem);
|
|
111
122
|
const devScript = manifest.scripts.dev;
|
|
112
123
|
if (devScript === undefined) {
|
|
113
|
-
return
|
|
124
|
+
return {
|
|
125
|
+
success: false,
|
|
126
|
+
diagnostics: [(0, diagnostics_1.createMissingDevScriptDiagnostic)()],
|
|
127
|
+
};
|
|
114
128
|
}
|
|
115
129
|
if (!isSupportedViteDevScript(devScript)) {
|
|
116
|
-
return
|
|
130
|
+
return {
|
|
131
|
+
success: false,
|
|
132
|
+
diagnostics: [(0, diagnostics_1.createUnsupportedDevScriptDiagnostic)(devScript)],
|
|
133
|
+
};
|
|
117
134
|
}
|
|
118
|
-
return
|
|
135
|
+
return {
|
|
136
|
+
success: true,
|
|
137
|
+
diagnostics: [],
|
|
138
|
+
manifest,
|
|
139
|
+
};
|
|
119
140
|
}
|
|
120
141
|
catch (error) {
|
|
121
142
|
if (error instanceof package_resolution_1.ForgePackageResolutionError) {
|
|
122
|
-
return
|
|
143
|
+
return {
|
|
144
|
+
success: false,
|
|
145
|
+
diagnostics: [error.diagnostic],
|
|
146
|
+
};
|
|
123
147
|
}
|
|
124
148
|
throw error;
|
|
125
149
|
}
|
|
@@ -153,13 +177,59 @@ class DefaultDevServerPreview {
|
|
|
153
177
|
return;
|
|
154
178
|
}
|
|
155
179
|
const previous = record.rebuildQueue ?? Promise.resolve(undefined);
|
|
156
|
-
const next = previous.then(() => this.
|
|
180
|
+
const next = previous.then(() => this.handleWorkspaceChange(record, event, change.reason));
|
|
157
181
|
record.rebuildQueue = next;
|
|
158
182
|
void next.catch(() => {
|
|
159
183
|
record.rebuildQueue = undefined;
|
|
160
184
|
});
|
|
161
185
|
}
|
|
162
|
-
async
|
|
186
|
+
async handleWorkspaceChange(record, event, reason) {
|
|
187
|
+
const cssPath = getUpdatedCssPath(event);
|
|
188
|
+
if (cssPath !== undefined &&
|
|
189
|
+
record.session.status === "running" &&
|
|
190
|
+
this.wasCssPathBundled(record, cssPath)) {
|
|
191
|
+
await this.performStyleUpdate(record, cssPath, reason);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const scriptPath = getUpdatedScriptModulePath(event);
|
|
195
|
+
if (scriptPath !== undefined &&
|
|
196
|
+
record.session.status === "running" &&
|
|
197
|
+
this.wasWorkspaceModuleBundled(record, scriptPath)) {
|
|
198
|
+
return this.performModuleUpdate(record, scriptPath, reason);
|
|
199
|
+
}
|
|
200
|
+
return this.performRebuild(record, reason, record.entry);
|
|
201
|
+
}
|
|
202
|
+
wasCssPathBundled(record, path) {
|
|
203
|
+
const styleId = createForgeCssStyleId(path);
|
|
204
|
+
return (record.lastSuccessfulBuildResult?.outputFiles.some((file) => file.contentType === "text/javascript" &&
|
|
205
|
+
file.content.includes(styleId)) ?? false);
|
|
206
|
+
}
|
|
207
|
+
wasWorkspaceModuleBundled(record, path) {
|
|
208
|
+
return (record.lastSuccessfulBuildResult?.browserBundle?.modules.some((moduleRecord) => moduleRecord.origin === "workspace" &&
|
|
209
|
+
moduleRecord.sourcePath === path) ?? false);
|
|
210
|
+
}
|
|
211
|
+
async performStyleUpdate(record, sourcePath, reason) {
|
|
212
|
+
const content = await this.options.fileSystem.readText(sourcePath);
|
|
213
|
+
const session = {
|
|
214
|
+
...record.session,
|
|
215
|
+
status: "running",
|
|
216
|
+
updatedAt: this.now(),
|
|
217
|
+
errorOverlayDocument: undefined,
|
|
218
|
+
};
|
|
219
|
+
record.session = session;
|
|
220
|
+
this.emit({
|
|
221
|
+
type: "preview:style-updated",
|
|
222
|
+
session,
|
|
223
|
+
styleUpdate: {
|
|
224
|
+
content,
|
|
225
|
+
contentType: "text/css",
|
|
226
|
+
sourcePath,
|
|
227
|
+
styleId: createForgeCssStyleId(sourcePath),
|
|
228
|
+
},
|
|
229
|
+
reason,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
async performModuleUpdate(record, sourcePath, reason) {
|
|
163
233
|
if (record.session.status === "stopped") {
|
|
164
234
|
const diagnostic = (0, diagnostics_1.createSessionStoppedDiagnostic)(record.session.id);
|
|
165
235
|
return {
|
|
@@ -168,37 +238,74 @@ class DefaultDevServerPreview {
|
|
|
168
238
|
diagnostics: [diagnostic],
|
|
169
239
|
};
|
|
170
240
|
}
|
|
241
|
+
const previousBuildResult = record.lastSuccessfulBuildResult;
|
|
171
242
|
record.session = {
|
|
172
243
|
...record.session,
|
|
173
244
|
status: "rebuilding",
|
|
174
245
|
updatedAt: this.now(),
|
|
246
|
+
errorOverlayDocument: undefined,
|
|
175
247
|
};
|
|
176
|
-
const buildResult = await this.options.buildPipeline.build({
|
|
248
|
+
const buildResult = await this.options.buildPipeline.build({
|
|
249
|
+
entry: record.entry,
|
|
250
|
+
});
|
|
177
251
|
if (!buildResult.success) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
252
|
+
return this.completeFailedRebuild(record, reason, buildResult);
|
|
253
|
+
}
|
|
254
|
+
const moduleUpdate = createModuleUpdate(previousBuildResult, buildResult, sourcePath);
|
|
255
|
+
if (moduleUpdate === undefined) {
|
|
256
|
+
return this.completeSuccessfulRefresh(record, reason, buildResult);
|
|
257
|
+
}
|
|
258
|
+
record.lastSuccessfulBuildResult = buildResult;
|
|
259
|
+
const session = {
|
|
260
|
+
...record.session,
|
|
261
|
+
status: "running",
|
|
262
|
+
updatedAt: this.now(),
|
|
263
|
+
diagnostics: buildResult.diagnostics,
|
|
264
|
+
buildResult,
|
|
265
|
+
previewDocument: record.lastSuccessfulPreviewDocument ?? record.session.previewDocument,
|
|
266
|
+
errorOverlayDocument: undefined,
|
|
267
|
+
};
|
|
268
|
+
record.session = session;
|
|
269
|
+
this.emit({
|
|
270
|
+
type: "preview:module-updated",
|
|
271
|
+
session,
|
|
272
|
+
moduleUpdate,
|
|
273
|
+
buildResult,
|
|
274
|
+
reason,
|
|
275
|
+
});
|
|
276
|
+
return {
|
|
277
|
+
success: true,
|
|
278
|
+
session,
|
|
279
|
+
diagnostics: buildResult.diagnostics,
|
|
280
|
+
buildResult,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
async performRebuild(record, reason, entry, signal) {
|
|
284
|
+
if (record.session.status === "stopped") {
|
|
285
|
+
const diagnostic = (0, diagnostics_1.createSessionStoppedDiagnostic)(record.session.id);
|
|
193
286
|
return {
|
|
194
287
|
success: false,
|
|
195
|
-
session,
|
|
196
|
-
diagnostics:
|
|
197
|
-
buildResult,
|
|
288
|
+
session: record.session,
|
|
289
|
+
diagnostics: [diagnostic],
|
|
198
290
|
};
|
|
199
291
|
}
|
|
292
|
+
record.session = {
|
|
293
|
+
...record.session,
|
|
294
|
+
status: "rebuilding",
|
|
295
|
+
updatedAt: this.now(),
|
|
296
|
+
errorOverlayDocument: undefined,
|
|
297
|
+
};
|
|
298
|
+
const buildResult = await this.options.buildPipeline.build({ entry, signal });
|
|
299
|
+
if (!buildResult.success) {
|
|
300
|
+
return this.completeFailedRebuild(record, reason, buildResult);
|
|
301
|
+
}
|
|
302
|
+
return this.completeSuccessfulRefresh(record, reason, buildResult);
|
|
303
|
+
}
|
|
304
|
+
async completeSuccessfulRefresh(record, reason, buildResult) {
|
|
200
305
|
const revision = record.session.revision + 1;
|
|
201
|
-
const previewDocument = await this.createPreviewDocument(record.session.id, record.session.previewUrl, revision, buildResult);
|
|
306
|
+
const previewDocument = await this.createPreviewDocument(record.session.id, record.session.previewUrl, revision, buildResult, record.adapter);
|
|
307
|
+
record.lastSuccessfulBuildResult = buildResult;
|
|
308
|
+
record.lastSuccessfulPreviewDocument = previewDocument;
|
|
202
309
|
const session = {
|
|
203
310
|
...record.session,
|
|
204
311
|
status: "running",
|
|
@@ -207,6 +314,7 @@ class DefaultDevServerPreview {
|
|
|
207
314
|
diagnostics: buildResult.diagnostics,
|
|
208
315
|
buildResult,
|
|
209
316
|
previewDocument,
|
|
317
|
+
errorOverlayDocument: undefined,
|
|
210
318
|
};
|
|
211
319
|
record.session = session;
|
|
212
320
|
this.emit({
|
|
@@ -222,11 +330,41 @@ class DefaultDevServerPreview {
|
|
|
222
330
|
buildResult,
|
|
223
331
|
};
|
|
224
332
|
}
|
|
225
|
-
|
|
333
|
+
completeFailedRebuild(record, reason, buildResult) {
|
|
334
|
+
const previousPreviewDocument = record.lastSuccessfulPreviewDocument ?? record.session.previewDocument;
|
|
335
|
+
const errorOverlayDocument = previousPreviewDocument === undefined
|
|
336
|
+
? undefined
|
|
337
|
+
: (0, preview_document_1.createForgePreviewErrorOverlayDocument)(previousPreviewDocument, buildResult.diagnostics);
|
|
338
|
+
const session = {
|
|
339
|
+
...record.session,
|
|
340
|
+
status: "failed",
|
|
341
|
+
updatedAt: this.now(),
|
|
342
|
+
diagnostics: buildResult.diagnostics,
|
|
343
|
+
buildResult,
|
|
344
|
+
previewDocument: previousPreviewDocument,
|
|
345
|
+
errorOverlayDocument,
|
|
346
|
+
};
|
|
347
|
+
record.session = session;
|
|
348
|
+
this.emit({
|
|
349
|
+
type: "dev:session-failed",
|
|
350
|
+
session,
|
|
351
|
+
diagnostics: buildResult.diagnostics,
|
|
352
|
+
buildResult,
|
|
353
|
+
reason,
|
|
354
|
+
});
|
|
355
|
+
return {
|
|
356
|
+
success: false,
|
|
357
|
+
session,
|
|
358
|
+
diagnostics: buildResult.diagnostics,
|
|
359
|
+
buildResult,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
async createPreviewDocument(sessionId, previewUrl, revision, buildResult, adapter) {
|
|
226
363
|
const htmlShell = (await this.options.fileSystem.exists("/index.html"))
|
|
227
364
|
? await this.options.fileSystem.readText("/index.html")
|
|
228
365
|
: undefined;
|
|
229
366
|
return (0, preview_document_1.createForgePreviewDocument)({
|
|
367
|
+
adapter,
|
|
230
368
|
buildResult,
|
|
231
369
|
htmlShell,
|
|
232
370
|
previewUrl,
|
|
@@ -268,6 +406,30 @@ exports.isSupportedViteDevScript = isSupportedViteDevScript;
|
|
|
268
406
|
function createPreviewUrl(sessionId) {
|
|
269
407
|
return `/preview/${encodeURIComponent(sessionId)}/`;
|
|
270
408
|
}
|
|
409
|
+
function selectDevServerAdapter(manifest) {
|
|
410
|
+
const dependencies = {
|
|
411
|
+
...manifest.dependencies,
|
|
412
|
+
...manifest.devDependencies,
|
|
413
|
+
};
|
|
414
|
+
if ("react" in dependencies || "react-dom" in dependencies) {
|
|
415
|
+
return createDevServerAdapterMetadata("vite-react", "react");
|
|
416
|
+
}
|
|
417
|
+
if ("vue" in dependencies) {
|
|
418
|
+
return createDevServerAdapterMetadata("vite-vue", "vue");
|
|
419
|
+
}
|
|
420
|
+
return createDevServerAdapterMetadata("vite-vanilla", "vanilla");
|
|
421
|
+
}
|
|
422
|
+
function createDevServerAdapterMetadata(name, framework) {
|
|
423
|
+
return {
|
|
424
|
+
framework,
|
|
425
|
+
hmrMode: "hmr-lite",
|
|
426
|
+
name,
|
|
427
|
+
preservesState: false,
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
function createForgeCssStyleId(path) {
|
|
431
|
+
return `forge-css:${path}`;
|
|
432
|
+
}
|
|
271
433
|
function describeFileChange(event) {
|
|
272
434
|
const path = event.type === "file:renamed" ? event.to : event.path;
|
|
273
435
|
if (path.startsWith("/.forge/")) {
|
|
@@ -277,3 +439,56 @@ function describeFileChange(event) {
|
|
|
277
439
|
reason: `${event.type}:${path}`,
|
|
278
440
|
};
|
|
279
441
|
}
|
|
442
|
+
function getUpdatedCssPath(event) {
|
|
443
|
+
return event.type === "file:updated" &&
|
|
444
|
+
event.path.endsWith(".css") &&
|
|
445
|
+
!event.path.endsWith(".module.css")
|
|
446
|
+
? event.path
|
|
447
|
+
: undefined;
|
|
448
|
+
}
|
|
449
|
+
function getUpdatedScriptModulePath(event) {
|
|
450
|
+
return event.type === "file:updated" && isHmrScriptPath(event.path)
|
|
451
|
+
? event.path
|
|
452
|
+
: undefined;
|
|
453
|
+
}
|
|
454
|
+
function createModuleUpdate(previousBuildResult, buildResult, sourcePath) {
|
|
455
|
+
const previousBundle = previousBuildResult?.browserBundle;
|
|
456
|
+
const nextBundle = buildResult.browserBundle;
|
|
457
|
+
if (previousBundle === undefined ||
|
|
458
|
+
nextBundle === undefined ||
|
|
459
|
+
previousBundle.entry === sourcePath ||
|
|
460
|
+
nextBundle.entry === sourcePath ||
|
|
461
|
+
!sameOutputPaths(previousBundle.outputPaths, nextBundle.outputPaths)) {
|
|
462
|
+
return undefined;
|
|
463
|
+
}
|
|
464
|
+
const previousModule = findWorkspaceModule(previousBundle.modules, sourcePath);
|
|
465
|
+
const nextModule = findWorkspaceModule(nextBundle.modules, sourcePath);
|
|
466
|
+
if (previousModule === undefined ||
|
|
467
|
+
nextModule === undefined ||
|
|
468
|
+
previousModule.outputPath !== nextModule.outputPath) {
|
|
469
|
+
return undefined;
|
|
470
|
+
}
|
|
471
|
+
const outputFile = buildResult.outputFiles.find((file) => file.path === nextModule.outputPath &&
|
|
472
|
+
file.contentType === "text/javascript");
|
|
473
|
+
return outputFile === undefined
|
|
474
|
+
? undefined
|
|
475
|
+
: {
|
|
476
|
+
content: outputFile.content,
|
|
477
|
+
contentType: "text/javascript",
|
|
478
|
+
outputPath: nextModule.outputPath,
|
|
479
|
+
sourcePath,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
function findWorkspaceModule(modules, sourcePath) {
|
|
483
|
+
return modules.find((moduleRecord) => moduleRecord.origin === "workspace" &&
|
|
484
|
+
moduleRecord.sourcePath === sourcePath);
|
|
485
|
+
}
|
|
486
|
+
function sameOutputPaths(left, right) {
|
|
487
|
+
const sortedLeft = [...left].sort();
|
|
488
|
+
const sortedRight = [...right].sort();
|
|
489
|
+
return (sortedLeft.length === sortedRight.length &&
|
|
490
|
+
sortedLeft.every((path, index) => path === sortedRight[index]));
|
|
491
|
+
}
|
|
492
|
+
function isHmrScriptPath(path) {
|
|
493
|
+
return /\.(?:jsx?|tsx?)$/i.test(path);
|
|
494
|
+
}
|