@markless/bundler 0.2.2 → 0.3.1
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 +1 -1
- package/dist/preload.d.ts +4 -4
- package/dist/preload.js +2 -2
- package/dist/rolldown-DAsY_7V1.js +4515 -0
- package/dist/rolldown.d.ts +11 -3
- package/dist/rolldown.js +3 -3
- package/dist/{source-module-C8W2THUI.js → source-module-BCEdLQvG.js} +362 -286
- package/dist/source-module-XFgOFX1c.d.ts +8 -0
- package/dist/{types-CzXV_ABj.d.ts → types-BuooJg32.d.ts} +47 -2
- package/dist/vite.d.ts +1 -1
- package/dist/vite.js +1298 -115
- package/package.json +6 -6
- package/dist/rolldown-CpG_S-ZJ.js +0 -2554
- package/dist/source-module-BRb5P7wL.d.ts +0 -6
package/dist/vite.js
CHANGED
|
@@ -1,9 +1,916 @@
|
|
|
1
|
-
import { MARKLESS_DEV_ERROR_CLEAR_EVENT, MARKLESS_DEV_ERROR_CLIENT_ID, MARKLESS_DEV_ERROR_EVENT, normalizeMarklessDevError } from "./dev-error.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { joinURL, parsePath } from "ufo";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { MARKLESS_DEV_ERROR_CLEAR_EVENT, MARKLESS_DEV_ERROR_CLIENT_ID, MARKLESS_DEV_ERROR_EVENT, normalizeMarklessDevError, renderMarklessDevErrorDocument } from "./dev-error.js";
|
|
2
|
+
import { _ as executionLogActivationInjection } from "./source-module-BCEdLQvG.js";
|
|
3
|
+
import { d as outputDefaults, o as preflightTsrxModuleDiagnostics, p as createPreloadGraphAdder, t as createMarklessRolldownPlugin } from "./rolldown-DAsY_7V1.js";
|
|
4
|
+
import { joinURL, parsePath, withQuery } from "ufo";
|
|
5
|
+
import { ASYNC_PROTOCOL_VERSION, STORAGE_SLOT_SYMBOL_KEY, serializeRuntimeAsyncSnapshots, serializeRuntimeStateCells, storageAttributeName } from "@markless/serializer";
|
|
6
|
+
import { pathToFileURL } from "node:url";
|
|
7
|
+
import { join, resolve } from "pathe";
|
|
8
|
+
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { PROTOCOL_EVENT_ACTION_KIND as PROTOCOL_EVENT_ACTION_KIND$1, protocolEventDispatchesMarkless } from "@markless/serializer/protocol";
|
|
11
|
+
import "@markless/serializer/resume-record-delta";
|
|
12
|
+
import { deserializeGraphValueForClient } from "@markless/serializer/decode-client";
|
|
13
|
+
import { createServerModuleRunner, isRunnableDevEnvironment } from "vite";
|
|
14
|
+
import { isAbsolute as isAbsolute$1 } from "node:path";
|
|
15
|
+
//#region ../web/src/inline/resumer.ts
|
|
16
|
+
PROTOCOL_EVENT_ACTION_KIND$1.event, PROTOCOL_EVENT_ACTION_KIND$1.externalDelegate;
|
|
17
|
+
function createInlineResumerSelfWakeSource(resumeModuleUrl) {
|
|
18
|
+
return `;(${runInlineResumerSelfWake.toString()})(${JSON.stringify(resumeModuleUrl)});`;
|
|
19
|
+
}
|
|
20
|
+
const PRERENDER_INLINE_EVENT_NAMES_TOKEN = "__MARKLESS_PRERENDER_EVENT_NAMES__";
|
|
21
|
+
function renderPrerenderInlineResumerSource(compiled, eventNames) {
|
|
22
|
+
return compiled.replace(PRERENDER_INLINE_EVENT_NAMES_TOKEN, JSON.stringify(eventNames));
|
|
23
|
+
}
|
|
24
|
+
function createPrerenderInlineResumerSource(eventNames, resumeModuleUrl, options) {
|
|
25
|
+
const plain = `(${runPrerenderInlineResumer.toString()})(${JSON.stringify(eventNames)},${JSON.stringify(resumeModuleUrl)},u=>import(u));`;
|
|
26
|
+
const logging = options?.executionLog !== void 0 && options.executionLog !== "never";
|
|
27
|
+
if (!options?.debug && !logging) return plain;
|
|
28
|
+
return `{
|
|
29
|
+
${options?.debug ? `const __MARKLESS_PRERENDER_DEBUG_BOOTSTRAP__=${options.debug.bootstrapSource};\n(${runPrerenderInlineResumerDebugSetup.toString()})(${JSON.stringify(eventNames)});` : ""}
|
|
30
|
+
${logging ? `(${runPrerenderInlineResumerLogSummary.toString()})(${JSON.stringify(options.executionLog)});` : ""}
|
|
31
|
+
${plain}
|
|
32
|
+
}`;
|
|
33
|
+
}
|
|
34
|
+
function runPrerenderInlineResumerLogSummary(mode) {
|
|
35
|
+
if (!(() => {
|
|
36
|
+
if (mode === "always") return true;
|
|
37
|
+
const currentLocation = location;
|
|
38
|
+
if (/^https?:\/\/(?:localhost|127\.0\.0\.1|\[::1\])(?::|$)/.test(currentLocation.origin) || new URLSearchParams(currentLocation.search).has("markless-log")) return true;
|
|
39
|
+
try {
|
|
40
|
+
return localStorage.getItem("marklessLog") === "1";
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
})()) return;
|
|
45
|
+
const globalScope = globalThis;
|
|
46
|
+
const ledger = globalScope.__marklessExecutionLedger ||= {
|
|
47
|
+
unit: "chunk-raw-bytes",
|
|
48
|
+
load: {
|
|
49
|
+
app: 0,
|
|
50
|
+
framework: 0,
|
|
51
|
+
instrument: 0,
|
|
52
|
+
inline: 0,
|
|
53
|
+
modules: []
|
|
54
|
+
},
|
|
55
|
+
total: {
|
|
56
|
+
app: 0,
|
|
57
|
+
framework: 0,
|
|
58
|
+
instrument: 0,
|
|
59
|
+
inline: 0
|
|
60
|
+
},
|
|
61
|
+
turns: [],
|
|
62
|
+
incomplete: null
|
|
63
|
+
};
|
|
64
|
+
const ownBytes = document.currentScript?.textContent?.length ?? 0;
|
|
65
|
+
ledger.total.inline += ownBytes;
|
|
66
|
+
if (!ledger.loadClosed) ledger.load.inline += ownBytes;
|
|
67
|
+
for (const type of [
|
|
68
|
+
"pointerdown",
|
|
69
|
+
"touchstart",
|
|
70
|
+
"keydown"
|
|
71
|
+
]) addEventListener(type, (event) => {
|
|
72
|
+
if (event.isTrusted) ledger.loadClosed = true;
|
|
73
|
+
}, {
|
|
74
|
+
capture: true,
|
|
75
|
+
passive: true
|
|
76
|
+
});
|
|
77
|
+
ledger.turns.push({
|
|
78
|
+
kind: "resume",
|
|
79
|
+
delta: {
|
|
80
|
+
app: 0,
|
|
81
|
+
framework: 0,
|
|
82
|
+
instrument: 0
|
|
83
|
+
},
|
|
84
|
+
modules: []
|
|
85
|
+
});
|
|
86
|
+
const executed = [...globalScope.__mxLog ||= /* @__PURE__ */ new Set()];
|
|
87
|
+
const preloaded = document.querySelectorAll("link[rel=modulepreload]").length;
|
|
88
|
+
const loadBytes = ledger.load.app + ledger.load.framework + ledger.load.inline;
|
|
89
|
+
const totalBytes = ledger.total.app + ledger.total.framework + ledger.total.inline;
|
|
90
|
+
const summary = executed.length === 0 ? `markless: resumed — 0.0 KB app executed, ${preloaded} modules preloaded (0 app executed) · 0.0 KB instrument` : `markless: ${(loadBytes / 1024).toFixed(1)} KB executed at load · total ${(totalBytes / 1024).toFixed(1)} KB · ${executed.length} modules pending sizes`;
|
|
91
|
+
console.log(summary);
|
|
92
|
+
const documentElement = document.documentElement;
|
|
93
|
+
if (documentElement) {
|
|
94
|
+
documentElement.setAttribute("data-markless-log-summary", summary);
|
|
95
|
+
documentElement.setAttribute("data-markless-log-inline-bytes", String(ledger.total.inline));
|
|
96
|
+
documentElement.setAttribute("data-markless-log-load-bytes", String(loadBytes));
|
|
97
|
+
if (executed.length === 0) {
|
|
98
|
+
documentElement.setAttribute("data-markless-log-app-bytes", "0");
|
|
99
|
+
documentElement.setAttribute("data-markless-log-instrument-bytes", "0");
|
|
100
|
+
} else documentElement.removeAttribute("data-markless-log-app-bytes");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function runPrerenderInlineResumerDebugSetup(eventNames) {
|
|
104
|
+
const root = document.currentScript?.closest("[data-async-container]");
|
|
105
|
+
if (!root) return;
|
|
106
|
+
try {
|
|
107
|
+
const controls = __MARKLESS_PRERENDER_DEBUG_BOOTSTRAP__?.(root, "ssr-inline", false);
|
|
108
|
+
controls?.delegated?.(eventNames);
|
|
109
|
+
controls?.activate();
|
|
110
|
+
} catch {}
|
|
111
|
+
}
|
|
112
|
+
function runPrerenderInlineResumer(eventNames, fallbackResumeModuleUrl, loadModule) {
|
|
113
|
+
const currentScript = document.currentScript;
|
|
114
|
+
const root = currentScript?.closest("[data-async-container]");
|
|
115
|
+
const resumeModuleUrl = currentScript?.getAttribute?.("data-markless-resume-module") ?? fallbackResumeModuleUrl;
|
|
116
|
+
if (!root || !resumeModuleUrl) return;
|
|
117
|
+
for (const eventName of eventNames) root.addEventListener(eventName, (event) => {
|
|
118
|
+
root.__marklessDelegatedDispatch = true;
|
|
119
|
+
return loadModule(resumeModuleUrl).then((module) => module.resumeContainerEvent({
|
|
120
|
+
root,
|
|
121
|
+
event,
|
|
122
|
+
element: event.target,
|
|
123
|
+
eventRecord: null
|
|
124
|
+
}));
|
|
125
|
+
}, true);
|
|
126
|
+
}
|
|
127
|
+
function runInlineResumerSelfWake(fallbackResumeModuleUrl) {
|
|
128
|
+
const currentScript = document.currentScript;
|
|
129
|
+
const root = currentScript?.closest("[data-async-container]");
|
|
130
|
+
const resumeModuleUrl = currentScript?.getAttribute?.("data-markless-resume-module") ?? fallbackResumeModuleUrl;
|
|
131
|
+
if (!root || !resumeModuleUrl) return;
|
|
132
|
+
const wake = () => requestAnimationFrame(() => {
|
|
133
|
+
queueMicrotask(async () => {
|
|
134
|
+
if (!root.__asyncResumeRuntimeStarted) {
|
|
135
|
+
root.__marklessDelegatedDispatch = true;
|
|
136
|
+
await (await import(
|
|
137
|
+
/* @vite-ignore */
|
|
138
|
+
resumeModuleUrl
|
|
139
|
+
)).resumeContainerEvent({
|
|
140
|
+
root,
|
|
141
|
+
event: 0
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", wake, { once: true });
|
|
147
|
+
else wake();
|
|
148
|
+
}
|
|
149
|
+
//#endregion
|
|
150
|
+
//#region ../web/src/repeat-runtime.ts
|
|
151
|
+
var KeyedRepeatRuntimeError = class extends Error {
|
|
152
|
+
code = "MARKLESS_REPEAT_KEY_DUPLICATE";
|
|
153
|
+
severity = "error";
|
|
154
|
+
phase = "runtime";
|
|
155
|
+
repeatId;
|
|
156
|
+
keyPath;
|
|
157
|
+
collidingValue;
|
|
158
|
+
docsUrl;
|
|
159
|
+
constructor(diagnostic) {
|
|
160
|
+
super(diagnostic.message);
|
|
161
|
+
Object.defineProperty(this, "message", {
|
|
162
|
+
value: diagnostic.message,
|
|
163
|
+
enumerable: true,
|
|
164
|
+
configurable: true
|
|
165
|
+
});
|
|
166
|
+
this.name = "KeyedRepeatRuntimeError";
|
|
167
|
+
this.repeatId = diagnostic.repeatId;
|
|
168
|
+
this.keyPath = diagnostic.keyPath;
|
|
169
|
+
this.collidingValue = diagnostic.collidingValue;
|
|
170
|
+
this.docsUrl = diagnostic.docsUrl;
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
function validateKeyedRepeatGraphKeys(graph, view) {
|
|
174
|
+
for (const repeat of view.keyedRepeats ?? []) assertUniqueRepeatKeys(repeat, readKeyedRepeatCollection(graph, repeat));
|
|
175
|
+
}
|
|
176
|
+
async function validateKeyedRepeatPayloadKeys(input) {
|
|
177
|
+
const entries = await Promise.all(input.state.cells.map(async (cell) => [cell.graphNodeId, cell.value === void 0 ? void 0 : await deserializeGraphValueForClient(cell.value)]));
|
|
178
|
+
const cells = new Map(entries);
|
|
179
|
+
validateKeyedRepeatGraphKeys({ read(graphNodeId, path = []) {
|
|
180
|
+
return readPath(cells.get(graphNodeId), path);
|
|
181
|
+
} }, input.view);
|
|
182
|
+
}
|
|
183
|
+
function readKeyedRepeatCollection(graph, repeat) {
|
|
184
|
+
if (!repeat.collectionGraphNodeId) return [];
|
|
185
|
+
const value = graph.read(repeat.collectionGraphNodeId, repeat.collectionPath);
|
|
186
|
+
if (Array.isArray(value)) return value;
|
|
187
|
+
return Array.from(value ?? []);
|
|
188
|
+
}
|
|
189
|
+
function repeatItemKey(item, repeat) {
|
|
190
|
+
return readPath(item, repeat.keyPath);
|
|
191
|
+
}
|
|
192
|
+
function assertUniqueRepeatKeys(repeat, items) {
|
|
193
|
+
const seen = /* @__PURE__ */ new Map();
|
|
194
|
+
for (const item of items) {
|
|
195
|
+
const key = repeatItemKey(item, repeat);
|
|
196
|
+
if (seen.has(key)) throw duplicateRepeatKeyError(repeat, key);
|
|
197
|
+
seen.set(key, true);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function duplicateRepeatKeyError(repeat, key) {
|
|
201
|
+
const source = `${repeat.itemName}.${repeat.keyPath.join(".")}`;
|
|
202
|
+
return new KeyedRepeatRuntimeError({
|
|
203
|
+
message: `MARKLESS_REPEAT_KEY_DUPLICATE: Duplicate @for key ${JSON.stringify(key)} from ${source}.`,
|
|
204
|
+
repeatId: repeat.id,
|
|
205
|
+
keyPath: repeat.keyPath,
|
|
206
|
+
collidingValue: key,
|
|
207
|
+
docsUrl: "https://markless.dev/errors/MARKLESS_REPEAT_KEY_DUPLICATE"
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
function readPath(value, path) {
|
|
211
|
+
let cursor = value;
|
|
212
|
+
for (const key of path) {
|
|
213
|
+
if (cursor == null) return void 0;
|
|
214
|
+
cursor = cursor[key];
|
|
215
|
+
}
|
|
216
|
+
return cursor;
|
|
217
|
+
}
|
|
218
|
+
//#endregion
|
|
219
|
+
//#region ../web/src/prerender/records.ts
|
|
220
|
+
async function prepareSsrResumeRecords(output) {
|
|
221
|
+
const rawState = output.state ?? emptyStatePayload();
|
|
222
|
+
const state = {
|
|
223
|
+
...rawState,
|
|
224
|
+
cells: serializeRuntimeStateCells(rawState.cells ?? []),
|
|
225
|
+
computed: serializeRuntimeAsyncSnapshots(rawState.computed ?? [])
|
|
226
|
+
};
|
|
227
|
+
const view = containerScopedView(output.view ?? emptyViewPayload());
|
|
228
|
+
await validateKeyedRepeatPayloadKeys({
|
|
229
|
+
state,
|
|
230
|
+
view
|
|
231
|
+
});
|
|
232
|
+
return {
|
|
233
|
+
state,
|
|
234
|
+
view
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function containerScopedView(view) {
|
|
238
|
+
return {
|
|
239
|
+
...view,
|
|
240
|
+
locators: view.locators.map((locator) => ({
|
|
241
|
+
...locator,
|
|
242
|
+
index: locator.index + 1
|
|
243
|
+
}))
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
function emptyStatePayload() {
|
|
247
|
+
return {
|
|
248
|
+
version: ASYNC_PROTOCOL_VERSION,
|
|
249
|
+
cells: [],
|
|
250
|
+
computed: []
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function emptyViewPayload() {
|
|
254
|
+
return {
|
|
255
|
+
version: ASYNC_PROTOCOL_VERSION,
|
|
256
|
+
locators: [],
|
|
257
|
+
events: [],
|
|
258
|
+
domUpdates: [],
|
|
259
|
+
behaviors: [],
|
|
260
|
+
elementHandles: [],
|
|
261
|
+
asyncBoundaries: []
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
//#endregion
|
|
265
|
+
//#region ../web/src/prerender/evaluator.ts
|
|
266
|
+
async function evaluateBuiltPageClosure(page, props) {
|
|
267
|
+
const renderContext = { prerender: true };
|
|
268
|
+
if (typeof page === "function") return page(props, renderContext);
|
|
269
|
+
if (page && typeof page.renderSsr === "function") return page.renderSsr(props, renderContext);
|
|
270
|
+
throw new TypeError("Prerender resume requires a compiled TSRX artifact.");
|
|
271
|
+
}
|
|
272
|
+
//#endregion
|
|
273
|
+
//#region ../web/src/render-to-string.ts
|
|
274
|
+
async function assemblePrerenderPageParts(component, output, options) {
|
|
275
|
+
const { state, view } = await prepareSsrResumeRecords(output);
|
|
276
|
+
const browserTriggers = hasBrowserTriggers(view, state);
|
|
277
|
+
const resumeModuleUrl = options.prerenderWakeModuleUrl ?? options.resumeModuleUrl ?? artifactPrerenderWakeModuleUrl(component) ?? artifactResumeModuleUrl(component);
|
|
278
|
+
const modulePreloads = (typeof options.modulePreloads === "function" ? options.modulePreloads(output.html) : options.modulePreloads) ?? (browserTriggers ? artifactModulePreloads(component) : void 0);
|
|
279
|
+
const eventNames = browserEventNames(view);
|
|
280
|
+
const selfWake = hasUnsettledAsyncBoundaryRunner(view, state);
|
|
281
|
+
const boot = artifactPrerenderBoot(component);
|
|
282
|
+
const settle = options.armEmission && selfWake ? boot?.settle : void 0;
|
|
283
|
+
const resumerScript = browserTriggers && resumeModuleUrl ? renderInlineResumerScript(settle ? renderPrerenderInlineResumerSource(settle.boot, eventNames) : boot ? renderPrerenderInlineResumerSource(selfWake ? boot.prerenderSelfWake : boot.prerender, eventNames) : createPrerenderInlineResumerSource(eventNames, resumeModuleUrl), options.nonce, resumeModuleUrl, selfWake && !settle, settle?.moduleUrl, !boot) : "";
|
|
284
|
+
return {
|
|
285
|
+
head: [
|
|
286
|
+
renderStorageSeedScript(artifactStorageSeeds(component), options.nonce),
|
|
287
|
+
renderHeadInjections(artifactHeadInjections(component), options.nonce),
|
|
288
|
+
renderModulePreloadLinks(modulePreloads, options.nonce)
|
|
289
|
+
].filter(Boolean).join(""),
|
|
290
|
+
container: [
|
|
291
|
+
`<div${renderContainerAttributes(options.containerId)}>`,
|
|
292
|
+
output.html,
|
|
293
|
+
options.armEmission ?? "",
|
|
294
|
+
resumerScript,
|
|
295
|
+
"</div>"
|
|
296
|
+
].filter(Boolean).join("")
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
async function renderSsrOutput(component, props, renderContext) {
|
|
300
|
+
if (typeof component === "function") return component(props, renderContext);
|
|
301
|
+
if (component && typeof component.renderSsr === "function") return component.renderSsr(props, renderContext);
|
|
302
|
+
throw new TypeError("renderToString(App) requires a compiled TSRX artifact.");
|
|
303
|
+
}
|
|
304
|
+
function artifactResumeModuleUrl(component) {
|
|
305
|
+
return typeof component === "object" ? component.resumeModuleUrl : void 0;
|
|
306
|
+
}
|
|
307
|
+
function artifactPrerenderBoot(component) {
|
|
308
|
+
return typeof component === "object" ? component.prerenderBoot : void 0;
|
|
309
|
+
}
|
|
310
|
+
function artifactPrerenderWakeModuleUrl(component) {
|
|
311
|
+
return typeof component === "object" ? component.prerenderWakeModuleUrl : void 0;
|
|
312
|
+
}
|
|
313
|
+
function artifactModulePreloads(component) {
|
|
314
|
+
return typeof component === "object" ? component.modulePreloads : void 0;
|
|
315
|
+
}
|
|
316
|
+
function artifactHeadInjections(component) {
|
|
317
|
+
return typeof component === "object" ? component.headInjections : void 0;
|
|
318
|
+
}
|
|
319
|
+
function artifactStorageSeeds(component) {
|
|
320
|
+
return typeof component === "object" ? component.storageSeeds : void 0;
|
|
321
|
+
}
|
|
322
|
+
function renderHeadInjections(injections, nonce) {
|
|
323
|
+
if (!injections?.length) return "";
|
|
324
|
+
return injections.filter((injection) => injection.location === "head").map((injection) => renderHeadInjection(injection, nonce)).join("");
|
|
325
|
+
}
|
|
326
|
+
function renderStorageSeedScript(seeds, nonce) {
|
|
327
|
+
if (!seeds?.length) return "";
|
|
328
|
+
return `<script${nonce ? ` nonce="${escapeAttribute(nonce)}"` : ""}>${escapeInlineScript(`(()=>{const s=globalThis[Symbol.for(${JSON.stringify(STORAGE_SLOT_SYMBOL_KEY)})]||={};for(const[k,d,a,f]of ${JSON.stringify(seeds.map((seed) => [
|
|
329
|
+
seed.slotKey,
|
|
330
|
+
seed.driverKey,
|
|
331
|
+
storageAttributeName(seed.driverKey),
|
|
332
|
+
seed.fallback
|
|
333
|
+
]))}){let v=f;try{v=localStorage.getItem(d)??f}catch{}s[k]=v;document.documentElement.setAttribute(a,v)}})()`)}<\/script>`;
|
|
334
|
+
}
|
|
335
|
+
function renderHeadInjection(injection, nonce) {
|
|
336
|
+
const attributes = { ...injection.attributes };
|
|
337
|
+
if (nonce && injection.tag === "script" && !attributes.nonce) attributes.nonce = nonce;
|
|
338
|
+
const renderedAttributes = Object.entries(attributes).map(([name, value]) => `${name}="${escapeAttribute(value)}"`).join(" ");
|
|
339
|
+
const suffix = renderedAttributes ? ` ${renderedAttributes}` : "";
|
|
340
|
+
return injection.tag === "link" ? `<${injection.tag}${suffix}>` : `<${injection.tag}${suffix}>${escapeInlineScript(injection.children ?? "")}</${injection.tag}>`;
|
|
341
|
+
}
|
|
342
|
+
function renderModulePreloadLinks(preloads, nonce) {
|
|
343
|
+
if (!preloads?.length) return "";
|
|
344
|
+
const seen = /* @__PURE__ */ new Set();
|
|
345
|
+
const links = [];
|
|
346
|
+
for (const preload of preloads) {
|
|
347
|
+
const entry = typeof preload === "string" ? { href: preload } : preload;
|
|
348
|
+
if (!entry.href || seen.has(entry.href)) continue;
|
|
349
|
+
seen.add(entry.href);
|
|
350
|
+
const attributes = [
|
|
351
|
+
"rel=\"modulepreload\"",
|
|
352
|
+
`href="${escapeAttribute(entry.href)}"`,
|
|
353
|
+
`crossorigin="${escapeAttribute(entry.crossOrigin ?? "anonymous")}"`,
|
|
354
|
+
entry.fetchPriority ? `fetchpriority="${entry.fetchPriority}"` : "",
|
|
355
|
+
nonce ? `nonce="${escapeAttribute(nonce)}"` : ""
|
|
356
|
+
].filter(Boolean);
|
|
357
|
+
links.push(`<link ${attributes.join(" ")}>`);
|
|
358
|
+
}
|
|
359
|
+
return links.join("");
|
|
360
|
+
}
|
|
361
|
+
function hasBrowserTriggers(view, state) {
|
|
362
|
+
return (state.storage?.length ?? 0) > 0 || view.events.some(protocolEventDispatchesMarkless) || state.computed.some((computed) => computed.async === false && typeof computed.deriveSymbolId === "string") || view.behaviors.some((behavior) => !!behavior.symbolId) || Object.keys(view.asyncRunners ?? {}).length > 0 || view.asyncBoundaries.some((boundary) => boundary.asyncReads.some((read) => !!read.runnerSymbolId)) || (view.keyedRepeats ?? []).some((repeat) => repeat.rowEvents.length > 0) || (view.branches ?? []).some((branch) => (branch.armRecords ?? []).some((arm) => arm.events.some(protocolEventDispatchesMarkless))) || view.asyncBoundaries.some((boundary) => boundaryArmEventNames(boundary).length > 0);
|
|
363
|
+
}
|
|
364
|
+
function hasUnsettledAsyncBoundaryRunner(view, state) {
|
|
365
|
+
const runners = { ...view.asyncRunners };
|
|
366
|
+
const reachable = /* @__PURE__ */ new Set();
|
|
367
|
+
for (const boundary of view.asyncBoundaries) for (const read of boundary.asyncReads) {
|
|
368
|
+
reachable.add(read.graphNodeId);
|
|
369
|
+
if (read.runnerSymbolId) runners[read.graphNodeId] ??= read.runnerSymbolId;
|
|
370
|
+
}
|
|
371
|
+
const computedByGraphNode = new Map(state.computed.map((computed) => [computed.graphNodeId, computed]));
|
|
372
|
+
for (const graphNodeId of reachable) {
|
|
373
|
+
const computed = computedByGraphNode.get(graphNodeId);
|
|
374
|
+
if (!computed) continue;
|
|
375
|
+
if (runners[graphNodeId]) {
|
|
376
|
+
const status = computed.snapshot?.status;
|
|
377
|
+
if (status !== "fulfilled" && status !== "rejected") return true;
|
|
378
|
+
}
|
|
379
|
+
for (const dependency of computed.dependencies ?? []) reachable.add(dependency.graphNodeId);
|
|
380
|
+
}
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
function boundaryArmEventNames(boundary) {
|
|
384
|
+
const armRecords = boundary.armRecords;
|
|
385
|
+
if (!armRecords || Array.isArray(armRecords)) return [];
|
|
386
|
+
return [...(armRecords.events ?? []).filter(protocolEventDispatchesMarkless).map((event) => event.eventName), ...(armRecords.keyedRepeats ?? []).flatMap((repeat) => repeat.rowEvents.map((event) => event.eventName))];
|
|
387
|
+
}
|
|
388
|
+
function browserEventNames(view) {
|
|
389
|
+
return [...new Set([
|
|
390
|
+
...view.events.filter(protocolEventDispatchesMarkless).map((event) => event.eventName),
|
|
391
|
+
...(view.keyedRepeats ?? []).flatMap((repeat) => repeat.rowEvents.map((event) => event.eventName)),
|
|
392
|
+
...(view.branches ?? []).flatMap((branch) => (branch.armRecords ?? []).flatMap((arm) => arm.events.filter(protocolEventDispatchesMarkless).map((event) => event.eventName))),
|
|
393
|
+
...view.asyncBoundaries.flatMap(boundaryArmEventNames)
|
|
394
|
+
])].filter((eventName) => eventName !== "visible");
|
|
395
|
+
}
|
|
396
|
+
function renderContainerAttributes(containerId) {
|
|
397
|
+
return containerId ? ` data-async-container="${escapeAttribute(containerId)}"` : " data-async-container";
|
|
398
|
+
}
|
|
399
|
+
function renderInlineResumerScript(source, nonce, resumeModuleUrl, selfWake, settleModuleUrl, appendSelfWakeSource = true) {
|
|
400
|
+
return `<script data-async-resumer${nonce ? ` nonce="${escapeAttribute(nonce)}"` : ""}${resumeModuleUrl ? ` data-markless-resume-module="${escapeAttribute(resumeModuleUrl)}"` : ""}${settleModuleUrl ? ` data-markless-settle-module="${escapeAttribute(settleModuleUrl)}"` : ""}${selfWake ? " data-markless-self-wake" : ""}>${escapeInlineScript(source + (selfWake && appendSelfWakeSource ? createInlineResumerSelfWakeSource(resumeModuleUrl) : ""))}<\/script>`;
|
|
401
|
+
}
|
|
402
|
+
function escapeAttribute(value) {
|
|
403
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
|
404
|
+
}
|
|
405
|
+
function escapeInlineScript(value) {
|
|
406
|
+
return value.replace(/<\/script/gi, "<\\/script");
|
|
407
|
+
}
|
|
408
|
+
//#endregion
|
|
409
|
+
//#region src/build/prerender.ts
|
|
410
|
+
async function deriveBuiltPrerenderRecords(input) {
|
|
411
|
+
return withBuiltPrerenderPage(input, async (page) => prepareSsrResumeRecords(await evaluateBuiltPageClosure(page)));
|
|
412
|
+
}
|
|
413
|
+
async function emitPrerenderedPage(input) {
|
|
414
|
+
const page = await withBuiltPrerenderPage(input, async (built) => {
|
|
415
|
+
const output = await evaluateBuiltPageClosure(built);
|
|
416
|
+
const emission = await deriveArmFillPlan(built, {
|
|
417
|
+
state: output.state,
|
|
418
|
+
bound: artifactSettleBound(built)
|
|
419
|
+
});
|
|
420
|
+
return assemblePrerenderPageParts(built, output, {
|
|
421
|
+
resumeModuleUrl: input.resumeModuleUrl,
|
|
422
|
+
prerenderWakeModuleUrl: input.prerenderWakeModuleUrl,
|
|
423
|
+
armEmission: emission?.html
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
const htmlFile = resolve(input.root, input.outDir, "index.html");
|
|
427
|
+
const html = await readFile(htmlFile, "utf8");
|
|
428
|
+
const placeholder = "<div id=\"app\"></div>";
|
|
429
|
+
if (!html.includes(placeholder)) throw new Error("MARKLESS_PRERENDER_CONTAINER_MISSING: expected exact #app build placeholder");
|
|
430
|
+
await writeFile(htmlFile, (page.head ? html.replace("</head>", `${page.head}</head>`) : html).replace(placeholder, page.container));
|
|
431
|
+
}
|
|
432
|
+
const SENTINEL_OPEN = "";
|
|
433
|
+
const SENTINEL_CLOSE = "";
|
|
434
|
+
const SENTINEL_PATTERN = /mh(\d+)/g;
|
|
435
|
+
const PROBE_TAGS = [1000003, 8000017];
|
|
436
|
+
var ArmPlanUnsupported = class extends Error {};
|
|
437
|
+
function unsupported(reason) {
|
|
438
|
+
throw new ArmPlanUnsupported(reason);
|
|
439
|
+
}
|
|
440
|
+
function createHoleMinter() {
|
|
441
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
442
|
+
const paths = [];
|
|
443
|
+
return {
|
|
444
|
+
paths,
|
|
445
|
+
sentinel(path) {
|
|
446
|
+
const key = path.join("\0");
|
|
447
|
+
let index = byKey.get(key);
|
|
448
|
+
if (index === void 0) {
|
|
449
|
+
index = paths.length;
|
|
450
|
+
byKey.set(key, index);
|
|
451
|
+
paths.push(path);
|
|
452
|
+
}
|
|
453
|
+
return `${SENTINEL_OPEN}mh${index}${SENTINEL_CLOSE}`;
|
|
454
|
+
},
|
|
455
|
+
index(path) {
|
|
456
|
+
const key = path.join("\0");
|
|
457
|
+
const found = byKey.get(key);
|
|
458
|
+
if (found === void 0) unsupported(`unminted path: ${path.join(".")}`);
|
|
459
|
+
return found;
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
function holeProxy(minter, path, options) {
|
|
464
|
+
const target = options.rows === null ? {} : Array.from({ length: options.rows });
|
|
465
|
+
return new Proxy(target, {
|
|
466
|
+
get(_target, key) {
|
|
467
|
+
if (typeof key === "symbol") return void 0;
|
|
468
|
+
if (key === "then") return void 0;
|
|
469
|
+
if (key === "toString" || key === "valueOf" || key === "toJSON") return () => minter.sentinel(path);
|
|
470
|
+
if (key === "length") {
|
|
471
|
+
const sentinel = minter.sentinel([...path, "length"]);
|
|
472
|
+
const tag = options.tag + minter.index([...path, "length"]);
|
|
473
|
+
return {
|
|
474
|
+
toString: () => sentinel,
|
|
475
|
+
valueOf: () => tag
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
if (key === "map") return (visit) => Array.from({ length: options.rows ?? 0 }, (_unused, index) => visit(holeProxy(minter, [...path, "[]"], options), index));
|
|
479
|
+
if (/^\d+$/.test(String(key))) return holeProxy(minter, [...path, "[]"], options);
|
|
480
|
+
return holeProxy(minter, [...path, String(key)], options);
|
|
481
|
+
},
|
|
482
|
+
has() {
|
|
483
|
+
return true;
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
async function renderForcedArm(page, rows, tag) {
|
|
488
|
+
const minter = createHoleMinter();
|
|
489
|
+
return {
|
|
490
|
+
output: await renderSsrOutput(page, void 0, { prerenderSettle: { graph: { read: () => ({
|
|
491
|
+
status: "fulfilled",
|
|
492
|
+
version: 1,
|
|
493
|
+
key: null,
|
|
494
|
+
value: holeProxy(minter, [], {
|
|
495
|
+
rows,
|
|
496
|
+
tag
|
|
497
|
+
})
|
|
498
|
+
}) } } }),
|
|
499
|
+
minter
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
async function deriveArmFillPlan(page, context = {}) {
|
|
503
|
+
try {
|
|
504
|
+
return buildArmFillEmission(await Promise.all(PROBE_TAGS.map(async (tag) => ({
|
|
505
|
+
two: await renderForcedArm(page, 2, tag),
|
|
506
|
+
one: await renderForcedArm(page, 1, tag),
|
|
507
|
+
none: await renderForcedArm(page, null, tag)
|
|
508
|
+
}))), context);
|
|
509
|
+
} catch (error) {
|
|
510
|
+
if (process.env.MARKLESS_ARM_PLAN_DEBUG) throw error;
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
function buildArmFillEmission(probes, context = {}) {
|
|
515
|
+
return planArmEmission(probes, context).emission;
|
|
516
|
+
}
|
|
517
|
+
function planArmEmission(probes, context = {}) {
|
|
518
|
+
const first = probes[0];
|
|
519
|
+
if (!first) return { skipped: [] };
|
|
520
|
+
const boundaryIds = (first.one.output.structure?.anchors ?? []).filter((anchor) => anchor.kind === "async").map((anchor) => anchor.id);
|
|
521
|
+
const skipped = [];
|
|
522
|
+
const plans = [];
|
|
523
|
+
const markup = [];
|
|
524
|
+
const bounds = {};
|
|
525
|
+
const initials = {};
|
|
526
|
+
for (const id of boundaryIds) {
|
|
527
|
+
const [primary, ...rest] = probes.map((probe, index) => {
|
|
528
|
+
try {
|
|
529
|
+
return buildBoundary(probe, probes[(index + 1) % probes.length], id);
|
|
530
|
+
} catch (error) {
|
|
531
|
+
skipped.push({
|
|
532
|
+
id,
|
|
533
|
+
reason: error instanceof ArmPlanUnsupported ? error.message : "render failed"
|
|
534
|
+
});
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
if (!primary) continue;
|
|
539
|
+
if (rest.some((other) => !other || other.markup !== primary.markup)) {
|
|
540
|
+
skipped.push({
|
|
541
|
+
id,
|
|
542
|
+
reason: "probe-dependent residue survived in the template"
|
|
543
|
+
});
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
if (rest.some((other) => JSON.stringify(other.plan) !== JSON.stringify(primary.plan))) {
|
|
547
|
+
skipped.push({
|
|
548
|
+
id,
|
|
549
|
+
reason: "probe-dependent plan"
|
|
550
|
+
});
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
const initial = initialScalarsFor(primary.plan, context.state);
|
|
554
|
+
const bound = context.bound?.[id];
|
|
555
|
+
if (!initial) {
|
|
556
|
+
skipped.push({
|
|
557
|
+
id,
|
|
558
|
+
reason: "derived argument is not a build-known scalar"
|
|
559
|
+
});
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
if (derivedSymbolIds(primary.plan).some((symbolId) => !bound?.[symbolId])) {
|
|
563
|
+
skipped.push({
|
|
564
|
+
id,
|
|
565
|
+
reason: "derived symbol has no bound-symbol descriptor"
|
|
566
|
+
});
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
for (const [node, value] of Object.entries(initial)) initials[node] = value;
|
|
570
|
+
if (bound) bounds[id] = bound;
|
|
571
|
+
plans.push({
|
|
572
|
+
...primary.plan,
|
|
573
|
+
...runnerSnapshot(primary.plan.node, context.state)
|
|
574
|
+
});
|
|
575
|
+
markup.push(primary.markup);
|
|
576
|
+
}
|
|
577
|
+
if (plans.length === 0) return { skipped };
|
|
578
|
+
const plan = {
|
|
579
|
+
version: 1,
|
|
580
|
+
boundaries: plans,
|
|
581
|
+
...Object.keys(bounds).length > 0 ? { bound: bounds } : {},
|
|
582
|
+
...Object.keys(initials).length > 0 ? { initial: initials } : {}
|
|
583
|
+
};
|
|
584
|
+
const planJson = JSON.stringify(plan);
|
|
585
|
+
const templateHtml = markup.join("");
|
|
586
|
+
const planScript = `<script type="markless/fill-plan">${planJson.replace(/<\/script/gi, "<\\/script")}<\/script>`;
|
|
587
|
+
return {
|
|
588
|
+
emission: {
|
|
589
|
+
html: `${templateHtml}${planScript}`,
|
|
590
|
+
planBytes: Buffer.byteLength(planScript, "utf8"),
|
|
591
|
+
templateBytes: Buffer.byteLength(templateHtml, "utf8"),
|
|
592
|
+
plan,
|
|
593
|
+
skipped
|
|
594
|
+
},
|
|
595
|
+
skipped
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function runnerSnapshot(node, state) {
|
|
599
|
+
const snapshot = (state?.computed)?.find((entry) => entry.graphNodeId === node)?.snapshot;
|
|
600
|
+
if (!snapshot) return {};
|
|
601
|
+
return {
|
|
602
|
+
..."key" in snapshot ? { key: snapshot.key } : {},
|
|
603
|
+
...typeof snapshot.version === "number" ? { version: snapshot.version } : {}
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
function derivedSymbolIds(plan) {
|
|
607
|
+
return [
|
|
608
|
+
...plan.holes,
|
|
609
|
+
...plan.repeat?.holes ?? [],
|
|
610
|
+
...plan.repeat?.emptyHoles ?? []
|
|
611
|
+
].flatMap((hole) => Array.isArray(hole.from) ? [] : [hole.from.symbolId]);
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* The scalar cells a derived hole reads besides the settled value. Returns
|
|
615
|
+
* undefined when any of them is not a build-known scalar, which fails the whole
|
|
616
|
+
* boundary closed: an unresolvable argument would fill the hole from nothing.
|
|
617
|
+
*/
|
|
618
|
+
function initialScalarsFor(plan, state) {
|
|
619
|
+
const cells = state?.cells;
|
|
620
|
+
const wanted = new Set([
|
|
621
|
+
...plan.holes,
|
|
622
|
+
...plan.repeat?.holes ?? [],
|
|
623
|
+
...plan.repeat?.emptyHoles ?? []
|
|
624
|
+
].flatMap((hole) => Array.isArray(hole.from) ? [] : hole.from.args ?? []).map((argument) => argument.node).filter((node) => node !== plan.node));
|
|
625
|
+
const initial = {};
|
|
626
|
+
for (const node of wanted) {
|
|
627
|
+
const envelope = (cells?.find((candidate) => candidate.graphNodeId === node))?.value;
|
|
628
|
+
if (!envelope || (envelope.records?.length ?? 0) > 0) return void 0;
|
|
629
|
+
const root = envelope.root;
|
|
630
|
+
if (root !== null && ![
|
|
631
|
+
"string",
|
|
632
|
+
"number",
|
|
633
|
+
"boolean"
|
|
634
|
+
].includes(typeof root)) return void 0;
|
|
635
|
+
initial[node] = root;
|
|
636
|
+
}
|
|
637
|
+
return initial;
|
|
638
|
+
}
|
|
639
|
+
function artifactSettleBound(page) {
|
|
640
|
+
return typeof page === "object" ? page.prerenderBoot?.settle?.bound : void 0;
|
|
641
|
+
}
|
|
642
|
+
function boundaryRecords(output, boundaryId) {
|
|
643
|
+
const view = output.view;
|
|
644
|
+
const state = output.state;
|
|
645
|
+
const boundary = (view?.asyncBoundaries ?? []).find((entry) => entry.id === boundaryId);
|
|
646
|
+
if (!boundary) unsupported(`boundary records missing: ${boundaryId}`);
|
|
647
|
+
const armRecords = boundary.armRecords;
|
|
648
|
+
if (!armRecords || Array.isArray(armRecords)) unsupported(`boundary is not composed-settled: ${boundaryId}`);
|
|
649
|
+
const armLocatorIndex = /* @__PURE__ */ new Map();
|
|
650
|
+
for (const locator of armRecords.locators ?? []) if (!armLocatorIndex.has(locator.hostNodeId)) armLocatorIndex.set(locator.hostNodeId, locator.index);
|
|
651
|
+
const pageUpdates = (view?.domUpdates ?? []).filter((update) => armLocatorIndex.has(update.hostNodeId));
|
|
652
|
+
return {
|
|
653
|
+
runnerGraphNodeId: boundary.runnerGraphNodeId,
|
|
654
|
+
armLocatorIndex,
|
|
655
|
+
domUpdates: [...pageUpdates, ...armRecords.domUpdates ?? []],
|
|
656
|
+
keyedRepeats: armRecords.keyedRepeats ?? [],
|
|
657
|
+
branches: armRecords.branches ?? [],
|
|
658
|
+
computedByNode: new Map((state?.computed ?? []).map((entry) => [entry.graphNodeId, entry]))
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
function armHtml(output, boundaryId) {
|
|
662
|
+
const anchor = (output.structure?.anchors ?? []).find((candidate) => candidate.kind === "async" && candidate.id === boundaryId);
|
|
663
|
+
if (!anchor) unsupported(`arm markup missing: ${boundaryId}`);
|
|
664
|
+
return anchor.html;
|
|
665
|
+
}
|
|
666
|
+
function buildBoundary(probe, sibling, boundaryId) {
|
|
667
|
+
const records = boundaryRecords(probe.one.output, boundaryId);
|
|
668
|
+
if (records.branches.length > 0) unsupported(`arm structure depends on data (branch in arm): ${boundaryId}`);
|
|
669
|
+
if (records.keyedRepeats.length > 1) unsupported("more than one repeat in the arm");
|
|
670
|
+
const htmlTwo = armHtml(probe.two.output, boundaryId);
|
|
671
|
+
const htmlOne = armHtml(probe.one.output, boundaryId);
|
|
672
|
+
const htmlNone = armHtml(probe.none.output, boundaryId);
|
|
673
|
+
const repeat = records.keyedRepeats[0];
|
|
674
|
+
const parts = repeat ? reconstruct(htmlTwo, htmlOne, htmlNone, Number(repeat.rowElementCount ?? 0)) : {
|
|
675
|
+
prefix: htmlOne,
|
|
676
|
+
row: "",
|
|
677
|
+
suffix: "",
|
|
678
|
+
empty: ""
|
|
679
|
+
};
|
|
680
|
+
if (!repeat && (htmlTwo !== htmlOne || htmlNone !== htmlOne)) unsupported("arm markup varies by row count without a repeat record");
|
|
681
|
+
const derived = derivedHoleSites(probe, sibling, boundaryId, records, parts);
|
|
682
|
+
const counter = { next: 0 };
|
|
683
|
+
const armHoles = [];
|
|
684
|
+
const prefix = substituteHoles(parts.prefix, probe.one.minter, armHoles, counter, derived, 0);
|
|
685
|
+
const rowAnchorCoordinate = repeat ? counter.next++ : -1;
|
|
686
|
+
const suffix = substituteHoles(parts.suffix, probe.one.minter, armHoles, counter, derived, parts.prefix.length + parts.row.length);
|
|
687
|
+
const rowHoles = [];
|
|
688
|
+
const row = repeat ? substituteHoles(parts.row, probe.one.minter, rowHoles, counter, [], -1) : "";
|
|
689
|
+
const emptyHoles = [];
|
|
690
|
+
const empty = repeat ? substituteHoles(parts.empty, probe.one.minter, emptyHoles, counter, [], -1) : "";
|
|
691
|
+
const collectionPath = repeat ? repeat.collectionPath ?? [] : [];
|
|
692
|
+
if (repeat && repeat.collectionGraphNodeId !== records.runnerGraphNodeId) unsupported("repeat collection is not the settled value");
|
|
693
|
+
for (const hole of rowHoles) rebaseRowHole(hole, collectionPath);
|
|
694
|
+
assertCoverage(records, [
|
|
695
|
+
...armHoles,
|
|
696
|
+
...rowHoles,
|
|
697
|
+
...emptyHoles
|
|
698
|
+
], repeat);
|
|
699
|
+
const plan = {
|
|
700
|
+
id: boundaryId,
|
|
701
|
+
arm: "try",
|
|
702
|
+
node: records.runnerGraphNodeId,
|
|
703
|
+
holes: armHoles,
|
|
704
|
+
...repeat ? { repeat: {
|
|
705
|
+
id: String(repeat.id),
|
|
706
|
+
coordinate: rowAnchorCoordinate,
|
|
707
|
+
from: collectionPath,
|
|
708
|
+
holes: rowHoles,
|
|
709
|
+
emptyHoles
|
|
710
|
+
} } : {}
|
|
711
|
+
};
|
|
712
|
+
const escaped = `${escapeArmId(boundaryId)}`;
|
|
713
|
+
const markup = [
|
|
714
|
+
`<template m:arm="${escaped}">${prefix}${repeat ? holeAnchor(rowAnchorCoordinate) : ""}${suffix}</template>`,
|
|
715
|
+
repeat ? `<template m:row="${escaped}">${row}</template>` : "",
|
|
716
|
+
repeat ? `<template m:empty="${escaped}">${empty}</template>` : ""
|
|
717
|
+
].join("");
|
|
718
|
+
if (markup.includes(SENTINEL_OPEN)) unsupported("sentinel leaked into emitted markup");
|
|
719
|
+
return {
|
|
720
|
+
plan,
|
|
721
|
+
markup
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
function escapeArmId(value) {
|
|
725
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
|
726
|
+
}
|
|
727
|
+
function holeAnchor(coordinate) {
|
|
728
|
+
return `<!--mh:${coordinate}-->`;
|
|
729
|
+
}
|
|
730
|
+
function reconstruct(htmlTwo, htmlOne, htmlNone, rowElementCount) {
|
|
731
|
+
const rowLength = htmlTwo.length - htmlOne.length;
|
|
732
|
+
if (rowLength <= 0) unsupported("two-row markup is not longer than one-row markup");
|
|
733
|
+
const candidates = [];
|
|
734
|
+
for (let split = 0; split + rowLength <= htmlOne.length; split++) {
|
|
735
|
+
const prefix = htmlOne.slice(0, split);
|
|
736
|
+
const row = htmlOne.slice(split, split + rowLength);
|
|
737
|
+
const suffix = htmlOne.slice(split + rowLength);
|
|
738
|
+
if (`${prefix}${row}${row}${suffix}` !== htmlTwo) continue;
|
|
739
|
+
if (!row.startsWith("<")) continue;
|
|
740
|
+
if ((row.match(/<[A-Za-z]/g) ?? []).length !== rowElementCount) continue;
|
|
741
|
+
candidates.push({
|
|
742
|
+
prefix,
|
|
743
|
+
row,
|
|
744
|
+
suffix
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
if (candidates.length !== 1) unsupported(`row reconstruction is not unique (${candidates.length} splits)`);
|
|
748
|
+
const { prefix, row, suffix } = candidates[0];
|
|
749
|
+
if (!htmlNone.startsWith(prefix) || !htmlNone.endsWith(suffix)) unsupported("empty markup does not share the reconstructed prefix and suffix");
|
|
750
|
+
return {
|
|
751
|
+
prefix,
|
|
752
|
+
row,
|
|
753
|
+
suffix,
|
|
754
|
+
empty: htmlNone.slice(prefix.length, htmlNone.length - suffix.length)
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
function derivedHoleSites(probe, sibling, boundaryId, records, parts) {
|
|
758
|
+
const derivedUpdates = records.domUpdates.filter((update) => {
|
|
759
|
+
return !!records.computedByNode.get(update.graphNodeId)?.deriveSymbolId;
|
|
760
|
+
});
|
|
761
|
+
if (derivedUpdates.length === 0) return [];
|
|
762
|
+
if (derivedUpdates.length > 1) unsupported("more than one derived residue in the arm");
|
|
763
|
+
const update = derivedUpdates[0];
|
|
764
|
+
const computed = records.computedByNode.get(update.graphNodeId);
|
|
765
|
+
const other = armHtml(sibling.one.output, boundaryId);
|
|
766
|
+
const html = armHtml(probe.one.output, boundaryId);
|
|
767
|
+
if (html === other) unsupported("derived residue did not move between probes");
|
|
768
|
+
let head = 0;
|
|
769
|
+
while (head < html.length && html[head] === other[head]) head++;
|
|
770
|
+
let tail = 0;
|
|
771
|
+
while (tail < html.length - head && tail < other.length - head && html[html.length - 1 - tail] === other[other.length - 1 - tail]) tail++;
|
|
772
|
+
const region = expandRegion(html, head, html.length - tail);
|
|
773
|
+
const ordinal = elementOrdinalAt(html, region.start);
|
|
774
|
+
const expected = records.armLocatorIndex.get(update.hostNodeId);
|
|
775
|
+
if (expected === void 0 || expected !== ordinal) unsupported("derived residue is not inside its recorded host element");
|
|
776
|
+
const target = update.target ?? {};
|
|
777
|
+
if (target.kind !== "text") unsupported(`derived residue target is not expressible: ${String(target.kind)}`);
|
|
778
|
+
const prefixText = String(target.prefix ?? "");
|
|
779
|
+
if (!html.slice(region.start, region.end).startsWith(prefixText)) unsupported("derived text residue does not follow its recorded prefix");
|
|
780
|
+
const start = region.start + prefixText.length;
|
|
781
|
+
if (start >= parts.prefix.length && start < parts.prefix.length + parts.row.length) unsupported("derived residue lands inside a repeat row");
|
|
782
|
+
return [{
|
|
783
|
+
start,
|
|
784
|
+
end: region.end,
|
|
785
|
+
hole: {
|
|
786
|
+
kind: "text",
|
|
787
|
+
from: {
|
|
788
|
+
symbolId: String(computed.deriveSymbolId),
|
|
789
|
+
args: (computed.dependencies ?? []).map((dependency) => ({
|
|
790
|
+
node: String(dependency.graphNodeId),
|
|
791
|
+
path: dependency.path ?? []
|
|
792
|
+
}))
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}];
|
|
796
|
+
}
|
|
797
|
+
function expandRegion(html, start, end) {
|
|
798
|
+
let left = start;
|
|
799
|
+
while (left > 0 && html[left - 1] !== ">" && html[left - 1] !== "\"") left--;
|
|
800
|
+
let right = end;
|
|
801
|
+
while (right < html.length && html[right] !== "<" && html[right] !== "\"") right++;
|
|
802
|
+
if (html[left - 1] === "\"") {
|
|
803
|
+
const name = /([A-Za-z_:][-A-Za-z0-9_:.]*)="$/.exec(html.slice(0, left))?.[1];
|
|
804
|
+
if (!name) unsupported("attribute residue has no attribute name");
|
|
805
|
+
return {
|
|
806
|
+
start: left,
|
|
807
|
+
end: right,
|
|
808
|
+
attributeName: name
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
return {
|
|
812
|
+
start: left,
|
|
813
|
+
end: right
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
function elementOrdinalAt(html, index) {
|
|
817
|
+
return (html.slice(0, index).match(/<[A-Za-z]/g) ?? []).length - 1;
|
|
818
|
+
}
|
|
819
|
+
function substituteHoles(source, minter, holes, counter, derived, offset) {
|
|
820
|
+
SENTINEL_PATTERN.lastIndex = 0;
|
|
821
|
+
const sites = [];
|
|
822
|
+
for (const match of source.matchAll(SENTINEL_PATTERN)) {
|
|
823
|
+
const index = match.index;
|
|
824
|
+
const path = minter.paths[Number(match[1])];
|
|
825
|
+
if (!path) unsupported("unknown sentinel");
|
|
826
|
+
const attribute = /([A-Za-z_:][-A-Za-z0-9_:.]*)="$/.exec(source.slice(0, index))?.[1];
|
|
827
|
+
sites.push({
|
|
828
|
+
start: index,
|
|
829
|
+
end: index + match[0].length,
|
|
830
|
+
hole: {
|
|
831
|
+
kind: attribute ? "attribute" : "text",
|
|
832
|
+
...attribute ? { name: attribute } : {},
|
|
833
|
+
from: path
|
|
834
|
+
}
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
for (const site of derived) {
|
|
838
|
+
if (offset < 0) continue;
|
|
839
|
+
const start = site.start - offset;
|
|
840
|
+
const end = site.end - offset;
|
|
841
|
+
if (start < 0 || end > source.length) continue;
|
|
842
|
+
sites.push({
|
|
843
|
+
start,
|
|
844
|
+
end,
|
|
845
|
+
hole: site.hole
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
sites.sort((left, right) => left.start - right.start);
|
|
849
|
+
let result = "";
|
|
850
|
+
let cursor = 0;
|
|
851
|
+
for (const site of sites) {
|
|
852
|
+
const coordinate = counter.next++;
|
|
853
|
+
holes.push({
|
|
854
|
+
...site.hole,
|
|
855
|
+
coordinate
|
|
856
|
+
});
|
|
857
|
+
if (site.hole.kind === "attribute") {
|
|
858
|
+
const elementStart = source.lastIndexOf("<", site.start);
|
|
859
|
+
if (elementStart < cursor) unsupported("attribute hole outside the working segment");
|
|
860
|
+
result += `${source.slice(cursor, elementStart)}${holeAnchor(coordinate)}${source.slice(elementStart, site.start)}`;
|
|
861
|
+
} else result += `${source.slice(cursor, site.start)}${holeAnchor(coordinate)}`;
|
|
862
|
+
cursor = site.end;
|
|
863
|
+
}
|
|
864
|
+
return `${result}${source.slice(cursor)}`;
|
|
865
|
+
}
|
|
866
|
+
function rebaseRowHole(hole, collectionPath) {
|
|
867
|
+
if (!Array.isArray(hole.from)) unsupported("derived residue inside a repeat row");
|
|
868
|
+
const path = hole.from;
|
|
869
|
+
const marker = [...collectionPath, "[]"];
|
|
870
|
+
for (let index = 0; index < marker.length; index++) if (path[index] !== marker[index]) unsupported("row hole is not item-relative");
|
|
871
|
+
hole.from = path.slice(marker.length);
|
|
872
|
+
}
|
|
873
|
+
function assertCoverage(records, holes, repeat) {
|
|
874
|
+
const pathHoles = new Set(holes.filter((hole) => Array.isArray(hole.from)).map((hole) => hole.from.join(".")));
|
|
875
|
+
const derivedHoles = new Set(holes.filter((hole) => !Array.isArray(hole.from)).map((hole) => hole.from.symbolId));
|
|
876
|
+
for (const update of records.domUpdates) {
|
|
877
|
+
const computed = records.computedByNode.get(update.graphNodeId);
|
|
878
|
+
if (computed?.deriveSymbolId) {
|
|
879
|
+
if (!derivedHoles.has(String(computed.deriveSymbolId))) unsupported(`derived residue without a hole: ${update.graphNodeId}`);
|
|
880
|
+
continue;
|
|
881
|
+
}
|
|
882
|
+
if (update.graphNodeId !== records.runnerGraphNodeId) continue;
|
|
883
|
+
const path = update.path ?? [];
|
|
884
|
+
if (!pathHoles.has(path.join(".")) && !pathHoles.has(path.slice(-1).join("."))) unsupported(`settled residue without a hole: ${update.source}`);
|
|
885
|
+
}
|
|
886
|
+
if (repeat && !repeat.collectionPath) unsupported("repeat without a collection path");
|
|
887
|
+
}
|
|
888
|
+
async function withBuiltPrerenderPage(input, read) {
|
|
889
|
+
const temporaryDirectory = await mkdtemp(join(tmpdir(), "markless-prerender-"));
|
|
890
|
+
try {
|
|
891
|
+
const { rolldown } = await import("rolldown");
|
|
892
|
+
const build = await rolldown({
|
|
893
|
+
input: { prerender: input.entry },
|
|
894
|
+
plugins: [input.serverPlugin]
|
|
895
|
+
});
|
|
896
|
+
await build.write({
|
|
897
|
+
dir: temporaryDirectory,
|
|
898
|
+
format: "esm",
|
|
899
|
+
entryFileNames: "[name].js",
|
|
900
|
+
chunkFileNames: "chunk-[hash].js"
|
|
901
|
+
});
|
|
902
|
+
await build.close();
|
|
903
|
+
const built = await import(`${pathToFileURL(join(temporaryDirectory, "prerender.js")).href}?build=${Date.now()}`);
|
|
904
|
+
if (!built.default) throw new Error("MARKLESS_PRERENDER_ENTRY_MISSING");
|
|
905
|
+
return await read(built.default);
|
|
906
|
+
} finally {
|
|
907
|
+
await rm(temporaryDirectory, {
|
|
908
|
+
force: true,
|
|
909
|
+
recursive: true
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
//#endregion
|
|
7
914
|
//#region src/vite/dev-tags.ts
|
|
8
915
|
function createDevTags() {
|
|
9
916
|
const tags = [];
|
|
@@ -31,39 +938,6 @@ function headScript(base, src) {
|
|
|
31
938
|
};
|
|
32
939
|
}
|
|
33
940
|
//#endregion
|
|
34
|
-
//#region src/vite/environment.ts
|
|
35
|
-
const UNMANAGED_VITE_ENVIRONMENTS = new Set(["nitro"]);
|
|
36
|
-
function viteEnvironmentName(environment, options = {}) {
|
|
37
|
-
if (environment === "client") return options.clientEnvironment ?? "client";
|
|
38
|
-
if (environment === "server") return options.serverEnvironment ?? "ssr";
|
|
39
|
-
return environment;
|
|
40
|
-
}
|
|
41
|
-
function marklessEnvironment(environment) {
|
|
42
|
-
const config = environment?.config;
|
|
43
|
-
if (isUnmanagedViteEnvironment(environment)) return "lib";
|
|
44
|
-
if (!config) return "client";
|
|
45
|
-
if (config.build?.lib) return "lib";
|
|
46
|
-
if (isServerViteEnvironment(environment)) return "server";
|
|
47
|
-
return "client";
|
|
48
|
-
}
|
|
49
|
-
function isServerViteEnvironment(environment) {
|
|
50
|
-
if (isUnmanagedViteEnvironment(environment)) return false;
|
|
51
|
-
const consumer = environment?.config?.consumer;
|
|
52
|
-
if (consumer) return consumer === "server";
|
|
53
|
-
return environment?.name !== void 0 && environment.name !== "client";
|
|
54
|
-
}
|
|
55
|
-
function isUnmanagedViteEnvironment(environment) {
|
|
56
|
-
return !!environment?.name && UNMANAGED_VITE_ENVIRONMENTS.has(environment.name);
|
|
57
|
-
}
|
|
58
|
-
function transformMarklessRequest(server, url, environment, options) {
|
|
59
|
-
return server.environments[viteEnvironmentName(environment, options)]?.transformRequest(url);
|
|
60
|
-
}
|
|
61
|
-
function fetchableDevEnvironment(environment) {
|
|
62
|
-
if (!environment) return;
|
|
63
|
-
const maybeFetchable = environment;
|
|
64
|
-
if (typeof maybeFetchable.dispatchFetch === "function") return maybeFetchable;
|
|
65
|
-
}
|
|
66
|
-
//#endregion
|
|
67
941
|
//#region src/dev-error/client-asset.ts
|
|
68
942
|
function createDevErrorClientAsset(viteClientUrl) {
|
|
69
943
|
const base = viteClientUrl.endsWith("/@vite/client") ? viteClientUrl.slice(0, -13) : "";
|
|
@@ -147,15 +1021,112 @@ if (window.__MARKLESS_DEV_ERROR__) show(window.__MARKLESS_DEV_ERROR__);
|
|
|
147
1021
|
`;
|
|
148
1022
|
}
|
|
149
1023
|
//#endregion
|
|
1024
|
+
//#region src/vite/environment.ts
|
|
1025
|
+
const UNMANAGED_VITE_ENVIRONMENTS = new Set(["nitro"]);
|
|
1026
|
+
function viteEnvironmentName(environment, options = {}) {
|
|
1027
|
+
if (environment === "client") return options.clientEnvironment ?? "client";
|
|
1028
|
+
if (environment === "server") return options.serverEnvironment ?? "ssr";
|
|
1029
|
+
return environment;
|
|
1030
|
+
}
|
|
1031
|
+
function marklessEnvironment(environment) {
|
|
1032
|
+
const config = environment?.config;
|
|
1033
|
+
if (isUnmanagedViteEnvironment(environment)) return "lib";
|
|
1034
|
+
if (!config) return "client";
|
|
1035
|
+
if (config.build?.lib) return "lib";
|
|
1036
|
+
if (isServerViteEnvironment(environment)) return "server";
|
|
1037
|
+
return "client";
|
|
1038
|
+
}
|
|
1039
|
+
function isServerViteEnvironment(environment) {
|
|
1040
|
+
if (isUnmanagedViteEnvironment(environment)) return false;
|
|
1041
|
+
const consumer = environment?.config?.consumer;
|
|
1042
|
+
if (consumer) return consumer === "server";
|
|
1043
|
+
return environment?.name !== void 0 && environment.name !== "client";
|
|
1044
|
+
}
|
|
1045
|
+
function isUnmanagedViteEnvironment(environment) {
|
|
1046
|
+
return !!environment?.name && UNMANAGED_VITE_ENVIRONMENTS.has(environment.name);
|
|
1047
|
+
}
|
|
1048
|
+
function transformMarklessRequest(server, url, environment, options) {
|
|
1049
|
+
return server.environments[viteEnvironmentName(environment, options)]?.transformRequest(url);
|
|
1050
|
+
}
|
|
1051
|
+
function hasInProcessModuleRunner(environment) {
|
|
1052
|
+
return !!environment && isRunnableDevEnvironment(environment);
|
|
1053
|
+
}
|
|
1054
|
+
function fetchableDevEnvironment(environment) {
|
|
1055
|
+
if (!environment) return;
|
|
1056
|
+
const maybeFetchable = environment;
|
|
1057
|
+
if (typeof maybeFetchable.dispatchFetch === "function") return maybeFetchable;
|
|
1058
|
+
}
|
|
1059
|
+
//#endregion
|
|
150
1060
|
//#region src/vite/hmr.ts
|
|
151
1061
|
const SOURCE_FILE_EXTENSION = /\.tsrx(?:[?#].*)?$/;
|
|
152
1062
|
const RESOLVED_DEV_ERROR_CLIENT_ID = `\0${MARKLESS_DEV_ERROR_CLIENT_ID}`;
|
|
1063
|
+
const VITE_ID_PREFIX = "/@id/";
|
|
1064
|
+
const VITE_NULL_BYTE = "__x00__";
|
|
153
1065
|
function createViteHmr(options) {
|
|
154
1066
|
let server;
|
|
155
1067
|
const invalidSources = /* @__PURE__ */ new Set();
|
|
156
1068
|
let invalidSourceRecheck;
|
|
157
1069
|
let recheckingInvalidSources = false;
|
|
158
1070
|
const preflightByFile = /* @__PURE__ */ new Map();
|
|
1071
|
+
const unreportedInvalidations = /* @__PURE__ */ new Map();
|
|
1072
|
+
let hotUpdatePasses = 0;
|
|
1073
|
+
let hotUpdateCycle;
|
|
1074
|
+
function beginHotUpdatePass() {
|
|
1075
|
+
hotUpdatePasses += 1;
|
|
1076
|
+
if (hotUpdateCycle) return;
|
|
1077
|
+
let finish;
|
|
1078
|
+
hotUpdateCycle = {
|
|
1079
|
+
settled: new Promise((resolve) => {
|
|
1080
|
+
finish = resolve;
|
|
1081
|
+
}),
|
|
1082
|
+
finish
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
function endHotUpdatePass() {
|
|
1086
|
+
hotUpdatePasses -= 1;
|
|
1087
|
+
if (hotUpdatePasses > 0) return;
|
|
1088
|
+
const cycle = hotUpdateCycle;
|
|
1089
|
+
hotUpdateCycle = void 0;
|
|
1090
|
+
cycle?.finish();
|
|
1091
|
+
}
|
|
1092
|
+
function settledHotUpdate() {
|
|
1093
|
+
if (process.env.MARKLESS_NO_BARRIER) return void 0;
|
|
1094
|
+
return hotUpdateCycle?.settled;
|
|
1095
|
+
}
|
|
1096
|
+
function recordUnreportedInvalidation(environment, modules) {
|
|
1097
|
+
const pending = unreportedInvalidations.get(environment.name);
|
|
1098
|
+
if (!pending) return;
|
|
1099
|
+
for (const module of modules) if (module.id) pending.add(module.id);
|
|
1100
|
+
}
|
|
1101
|
+
function installRunnerInvalidationGate(nextServer) {
|
|
1102
|
+
for (const [name, environment] of Object.entries(nextServer.environments ?? {})) {
|
|
1103
|
+
if (name === options.clientEnvironment || hasInProcessModuleRunner(environment)) continue;
|
|
1104
|
+
const fetchModule = typeof environment.fetchModule === "function" ? environment.fetchModule.bind(environment) : void 0;
|
|
1105
|
+
if (!fetchModule) continue;
|
|
1106
|
+
unreportedInvalidations.set(name, /* @__PURE__ */ new Set());
|
|
1107
|
+
environment.fetchModule = async (id, importer, fetchOptions) => {
|
|
1108
|
+
const pending = unreportedInvalidations.get(name);
|
|
1109
|
+
const module = pending?.size ? await fetchedModuleNode(environment, id) : void 0;
|
|
1110
|
+
if (!module?.id || !pending?.delete(module.id)) return fetchModule(id, importer, fetchOptions);
|
|
1111
|
+
environment.moduleGraph.invalidateModule(module, /* @__PURE__ */ new Set(), Date.now(), true, true);
|
|
1112
|
+
return fetchModule(id, importer, {
|
|
1113
|
+
...fetchOptions,
|
|
1114
|
+
cached: false
|
|
1115
|
+
});
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
function installServerRenderBarrier(nextServer) {
|
|
1120
|
+
for (const environment of Object.values(nextServer.environments ?? {})) {
|
|
1121
|
+
const fetchEnv = fetchableDevEnvironment(environment);
|
|
1122
|
+
if (!fetchEnv) continue;
|
|
1123
|
+
const dispatchFetch = fetchEnv.dispatchFetch.bind(fetchEnv);
|
|
1124
|
+
fetchEnv.dispatchFetch = async (request) => {
|
|
1125
|
+
await settledHotUpdate();
|
|
1126
|
+
return dispatchFetch(request);
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
159
1130
|
function preflight(sourceId, source) {
|
|
160
1131
|
const cached = preflightByFile.get(sourceId);
|
|
161
1132
|
if (cached?.source === source) return cached.verdict;
|
|
@@ -185,7 +1156,7 @@ function createViteHmr(options) {
|
|
|
185
1156
|
invalidSourceRecheck = void 0;
|
|
186
1157
|
}
|
|
187
1158
|
function addInvalidSource(id) {
|
|
188
|
-
if (!isAbsolute(id) || !SOURCE_FILE_EXTENSION.test(id)) return;
|
|
1159
|
+
if (!isAbsolute$1(id) || !SOURCE_FILE_EXTENSION.test(id)) return;
|
|
189
1160
|
invalidSources.add(id);
|
|
190
1161
|
startInvalidSourceRecheck();
|
|
191
1162
|
}
|
|
@@ -242,6 +1213,8 @@ function createViteHmr(options) {
|
|
|
242
1213
|
return {
|
|
243
1214
|
configureServer(nextServer) {
|
|
244
1215
|
server = nextServer;
|
|
1216
|
+
installRunnerInvalidationGate(nextServer);
|
|
1217
|
+
installServerRenderBarrier(nextServer);
|
|
245
1218
|
nextServer.httpServer?.once("close", cleanup);
|
|
246
1219
|
nextServer.watcher?.once?.("close", cleanup);
|
|
247
1220
|
if (options.enabled) installFetchViteClient(nextServer, options);
|
|
@@ -265,65 +1238,104 @@ function createViteHmr(options) {
|
|
|
265
1238
|
});
|
|
266
1239
|
},
|
|
267
1240
|
async hotUpdate(environment, ctx) {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
if (!options.enabled) {
|
|
274
|
-
hot.send({ type: "full-reload" });
|
|
275
|
-
return [];
|
|
276
|
-
}
|
|
277
|
-
const files = changedFiles(ctx.modules ?? []);
|
|
278
|
-
const root = server?.config?.root;
|
|
279
|
-
if (ctx.file && SOURCE_FILE_EXTENSION.test(ctx.file)) {
|
|
280
|
-
const prefix = root && `${root}/`;
|
|
281
|
-
files.add(prefix && ctx.file.startsWith(prefix) ? `/${ctx.file.slice(prefix.length)}` : ctx.file);
|
|
1241
|
+
beginHotUpdatePass();
|
|
1242
|
+
try {
|
|
1243
|
+
return await runHotUpdate(environment, ctx);
|
|
1244
|
+
} finally {
|
|
1245
|
+
endHotUpdatePass();
|
|
282
1246
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
1247
|
+
}
|
|
1248
|
+
};
|
|
1249
|
+
async function runHotUpdate(environment, ctx) {
|
|
1250
|
+
if (!environment) return;
|
|
1251
|
+
const env = marklessEnvironment(environment);
|
|
1252
|
+
if (env === "lib") return;
|
|
1253
|
+
const hot = clientHot(server, options, environment);
|
|
1254
|
+
if (!hot?.send) return;
|
|
1255
|
+
const targets = invalidationTargets(server, options, environment, env);
|
|
1256
|
+
const runnerHots = moduleRunnerHots(targets, hot);
|
|
1257
|
+
const reloadModuleRunner = () => {
|
|
1258
|
+
for (const runnerHot of runnerHots) runnerHot.send?.({ type: "full-reload" });
|
|
1259
|
+
};
|
|
1260
|
+
if (!options.enabled) {
|
|
1261
|
+
hot.send({ type: "full-reload" });
|
|
1262
|
+
reloadModuleRunner();
|
|
1263
|
+
return [];
|
|
1264
|
+
}
|
|
1265
|
+
const files = changedFiles(ctx.modules ?? []);
|
|
1266
|
+
const root = server?.config?.root;
|
|
1267
|
+
if (ctx.file && SOURCE_FILE_EXTENSION.test(ctx.file)) {
|
|
1268
|
+
const prefix = root && `${root}/`;
|
|
1269
|
+
files.add(prefix && ctx.file.startsWith(prefix) ? `/${ctx.file.slice(prefix.length)}` : ctx.file);
|
|
1270
|
+
}
|
|
1271
|
+
if (!files.size) return;
|
|
1272
|
+
let sendMessages = true;
|
|
1273
|
+
let editedSource;
|
|
1274
|
+
if (ctx.file && SOURCE_FILE_EXTENSION.test(ctx.file) && ctx.read) {
|
|
1275
|
+
const sourceId = parsePath(ctx.file).pathname;
|
|
1276
|
+
try {
|
|
1277
|
+
editedSource = await ctx.read();
|
|
1278
|
+
} catch {}
|
|
1279
|
+
if (editedSource !== void 0) {
|
|
1280
|
+
const verdict = await preflight(sourceId, editedSource);
|
|
1281
|
+
sendMessages = !verdict.messagesSent;
|
|
1282
|
+
verdict.messagesSent = true;
|
|
1283
|
+
if (verdict.error) {
|
|
1284
|
+
if (sendMessages) {
|
|
1285
|
+
addInvalidSource(verdict.error.id);
|
|
1286
|
+
hot.send({
|
|
1287
|
+
type: "custom",
|
|
1288
|
+
event: MARKLESS_DEV_ERROR_EVENT,
|
|
1289
|
+
data: verdict.error
|
|
1290
|
+
});
|
|
305
1291
|
}
|
|
1292
|
+
return [];
|
|
306
1293
|
}
|
|
307
|
-
if (editedSource !== void 0 && sendMessages && deleteInvalidSource(sourceId)) hot.send({
|
|
308
|
-
type: "custom",
|
|
309
|
-
event: MARKLESS_DEV_ERROR_CLEAR_EVENT,
|
|
310
|
-
data: { id: sourceId }
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
const invalidationEnvironments = moduleGraphEnvironments(environment, env === "server" ? server?.environments?.[options.clientEnvironment] : void 0);
|
|
314
|
-
for (const file of files) for (const candidate of hmrCandidates(file, ctx.file)) for (const id of options.invalidateGeneratedModules?.(candidate, env) ?? []) for (const targetEnvironment of invalidationEnvironments) {
|
|
315
|
-
const module = targetEnvironment.moduleGraph?.getModuleById?.(id);
|
|
316
|
-
if (!module) continue;
|
|
317
|
-
targetEnvironment.moduleGraph?.invalidateModule?.(module, /* @__PURE__ */ new Set(), ctx.timestamp, true);
|
|
318
1294
|
}
|
|
319
|
-
if (sendMessages) hot.send({
|
|
320
|
-
type: "
|
|
321
|
-
|
|
322
|
-
|
|
1295
|
+
if (editedSource !== void 0 && sendMessages && deleteInvalidSource(sourceId)) hot.send({
|
|
1296
|
+
type: "custom",
|
|
1297
|
+
event: MARKLESS_DEV_ERROR_CLEAR_EVENT,
|
|
1298
|
+
data: { id: sourceId }
|
|
323
1299
|
});
|
|
324
|
-
return [];
|
|
325
1300
|
}
|
|
326
|
-
|
|
1301
|
+
if (!sendMessages) return [];
|
|
1302
|
+
const invalidationEnvironments = [...targets.keys()];
|
|
1303
|
+
const invalidationScopes = generatedModuleScopes(targets);
|
|
1304
|
+
const invalidatedGeneratedIds = /* @__PURE__ */ new Set();
|
|
1305
|
+
for (const file of files) {
|
|
1306
|
+
const candidates = editedSource !== void 0 && ctx.file ? [ctx.file] : hmrCandidates(file, ctx.file);
|
|
1307
|
+
for (const candidate of candidates) for (const scope of invalidationScopes) {
|
|
1308
|
+
const invalidatedIds = await options.invalidateGeneratedModules?.(candidate, scope, candidate === ctx.file ? editedSource : void 0);
|
|
1309
|
+
for (const id of invalidatedIds ?? []) {
|
|
1310
|
+
invalidatedGeneratedIds.add(id);
|
|
1311
|
+
for (const targetEnvironment of invalidationEnvironments) {
|
|
1312
|
+
const module = targetEnvironment.moduleGraph?.getModuleById?.(id);
|
|
1313
|
+
if (!module) continue;
|
|
1314
|
+
const invalidated = /* @__PURE__ */ new Set();
|
|
1315
|
+
targetEnvironment.moduleGraph?.invalidateModule?.(module, invalidated, ctx.timestamp, true);
|
|
1316
|
+
recordUnreportedInvalidation(targetEnvironment, invalidated);
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
if (invalidatedGeneratedIds.size > 0) options.invalidatePrerenderSnapshots?.([...invalidatedGeneratedIds].sort());
|
|
1322
|
+
if (ctx.file) for (const targetEnvironment of invalidationEnvironments) for (const module of targetEnvironment.moduleGraph?.getModulesByFile?.(ctx.file) ?? []) {
|
|
1323
|
+
const invalidated = /* @__PURE__ */ new Set();
|
|
1324
|
+
targetEnvironment.moduleGraph?.invalidateModule?.(module, invalidated, ctx.timestamp, true, true);
|
|
1325
|
+
recordUnreportedInvalidation(targetEnvironment, invalidated);
|
|
1326
|
+
}
|
|
1327
|
+
reloadModuleRunner();
|
|
1328
|
+
hot.send({
|
|
1329
|
+
type: "full-reload",
|
|
1330
|
+
path: firstChangedFile(files),
|
|
1331
|
+
triggeredBy: ctx.file
|
|
1332
|
+
});
|
|
1333
|
+
return [];
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
async function fetchedModuleNode(environment, id) {
|
|
1337
|
+
const resolved = id.startsWith(VITE_ID_PREFIX) ? id.slice(5).replace(VITE_NULL_BYTE, "\0") : id;
|
|
1338
|
+
return environment.moduleGraph.getModuleById(resolved) ?? await environment.moduleGraph.getModuleByUrl(id).catch(() => void 0);
|
|
327
1339
|
}
|
|
328
1340
|
function hasErrorCode(error, code) {
|
|
329
1341
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
@@ -357,7 +1369,31 @@ function injectDevClients(html, base) {
|
|
|
357
1369
|
return `${tags}${html}`;
|
|
358
1370
|
}
|
|
359
1371
|
function clientHot(server, options, environment) {
|
|
360
|
-
|
|
1372
|
+
if (!environment || marklessEnvironment(environment) === "server") return server?.environments?.[options.clientEnvironment]?.hot;
|
|
1373
|
+
return environment.hot;
|
|
1374
|
+
}
|
|
1375
|
+
function invalidationTargets(server, options, environment, env) {
|
|
1376
|
+
const targets = new Map([[environment, env]]);
|
|
1377
|
+
const clientEnvironment = server?.environments?.[options.clientEnvironment];
|
|
1378
|
+
if (clientEnvironment) targets.set(clientEnvironment, "client");
|
|
1379
|
+
for (const [name, candidate] of Object.entries(server?.environments ?? {})) {
|
|
1380
|
+
if (name === options.clientEnvironment || targets.has(candidate)) continue;
|
|
1381
|
+
if (!(candidate.config?.consumer === "server" && !isServerViteEnvironment(candidate)) && marklessEnvironment(candidate) !== "server") continue;
|
|
1382
|
+
targets.set(candidate, "server");
|
|
1383
|
+
}
|
|
1384
|
+
return targets;
|
|
1385
|
+
}
|
|
1386
|
+
function generatedModuleScopes(targets) {
|
|
1387
|
+
const scopes = new Set(targets.values());
|
|
1388
|
+
return ["client", "server"].filter((scope) => scopes.has(scope));
|
|
1389
|
+
}
|
|
1390
|
+
function moduleRunnerHots(targets, clientChannel) {
|
|
1391
|
+
const hots = /* @__PURE__ */ new Set();
|
|
1392
|
+
for (const [environment, scope] of targets) {
|
|
1393
|
+
if (scope !== "server" || !environment.hot || environment.hot === clientChannel) continue;
|
|
1394
|
+
hots.add(environment.hot);
|
|
1395
|
+
}
|
|
1396
|
+
return [...hots];
|
|
361
1397
|
}
|
|
362
1398
|
function changedFiles(modules) {
|
|
363
1399
|
const files = /* @__PURE__ */ new Set();
|
|
@@ -377,14 +1413,79 @@ function hmrCandidates(file, absoluteFile) {
|
|
|
377
1413
|
if (absoluteFile) candidates.add(absoluteFile);
|
|
378
1414
|
return candidates;
|
|
379
1415
|
}
|
|
380
|
-
function moduleGraphEnvironments(environment, clientEnvironment) {
|
|
381
|
-
if (!clientEnvironment || clientEnvironment === environment) return [environment];
|
|
382
|
-
return [environment, clientEnvironment];
|
|
383
|
-
}
|
|
384
1416
|
function firstChangedFile(files) {
|
|
385
1417
|
for (const file of files) return file;
|
|
386
1418
|
}
|
|
387
1419
|
//#endregion
|
|
1420
|
+
//#region src/vite/dev-prerender.ts
|
|
1421
|
+
const APP_PLACEHOLDER = "<div id=\"app\"></div>";
|
|
1422
|
+
function createDevPrerender(input) {
|
|
1423
|
+
let server;
|
|
1424
|
+
let runner;
|
|
1425
|
+
let renderDataHashKey = "initial";
|
|
1426
|
+
let snapshot;
|
|
1427
|
+
const renderDataHashes = /* @__PURE__ */ new Map();
|
|
1428
|
+
let invalidationGeneration = 0;
|
|
1429
|
+
function updateHashes(hashes) {
|
|
1430
|
+
let changed = false;
|
|
1431
|
+
for (const [id, hash] of hashes) {
|
|
1432
|
+
if (renderDataHashes.get(id) === hash) continue;
|
|
1433
|
+
renderDataHashes.set(id, hash);
|
|
1434
|
+
changed = true;
|
|
1435
|
+
}
|
|
1436
|
+
if (changed) updateHashKey();
|
|
1437
|
+
}
|
|
1438
|
+
function invalidate(renderDataIds) {
|
|
1439
|
+
invalidationGeneration += 1;
|
|
1440
|
+
for (const id of renderDataIds) renderDataHashes.set(id, `invalidated-${invalidationGeneration}`);
|
|
1441
|
+
updateHashKey();
|
|
1442
|
+
}
|
|
1443
|
+
function updateHashKey() {
|
|
1444
|
+
renderDataHashKey = [...renderDataHashes].sort(([left], [right]) => left.localeCompare(right)).map(([id, hash]) => `${encodeURIComponent(id)}:${hash}`).join("|");
|
|
1445
|
+
snapshot = void 0;
|
|
1446
|
+
}
|
|
1447
|
+
async function renderSnapshot() {
|
|
1448
|
+
const key = renderDataHashKey;
|
|
1449
|
+
if (snapshot?.key === key) return snapshot.value;
|
|
1450
|
+
const value = renderPage();
|
|
1451
|
+
snapshot = {
|
|
1452
|
+
key,
|
|
1453
|
+
value
|
|
1454
|
+
};
|
|
1455
|
+
const rendered = await value;
|
|
1456
|
+
return key === renderDataHashKey ? rendered : renderSnapshot();
|
|
1457
|
+
}
|
|
1458
|
+
async function renderPage() {
|
|
1459
|
+
const environment = server?.environments[input.serverEnvironment];
|
|
1460
|
+
if (!environment) throw new Error(`MARKLESS_DEV_PRERENDER_ENVIRONMENT_MISSING: ${input.serverEnvironment}`);
|
|
1461
|
+
runner ??= createServerModuleRunner(environment);
|
|
1462
|
+
const built = await runner.import(input.entry);
|
|
1463
|
+
if (!built.default) throw new Error("MARKLESS_PRERENDER_ENTRY_MISSING");
|
|
1464
|
+
const output = await evaluateBuiltPageClosure(built.default);
|
|
1465
|
+
return assemblePrerenderPageParts(built.default, output, {});
|
|
1466
|
+
}
|
|
1467
|
+
return {
|
|
1468
|
+
invalidate,
|
|
1469
|
+
updateHashes,
|
|
1470
|
+
async renderHtml(html) {
|
|
1471
|
+
try {
|
|
1472
|
+
if (!html.includes(APP_PLACEHOLDER)) throw new Error("MARKLESS_PRERENDER_CONTAINER_MISSING: expected exact #app dev placeholder");
|
|
1473
|
+
const page = await renderSnapshot();
|
|
1474
|
+
return (page.head ? html.replace("</head>", `${page.head}</head>`) : html).replace(APP_PLACEHOLDER, page.container);
|
|
1475
|
+
} catch (error) {
|
|
1476
|
+
const environment = server?.environments[input.serverEnvironment];
|
|
1477
|
+
input.reportError(environment, error);
|
|
1478
|
+
return injectDevClients(renderMarklessDevErrorDocument(normalizeMarklessDevError(error, { id: input.entry }), { base: input.base }), input.base);
|
|
1479
|
+
}
|
|
1480
|
+
},
|
|
1481
|
+
configureServer(nextServer) {
|
|
1482
|
+
server = nextServer;
|
|
1483
|
+
nextServer.httpServer?.once("close", () => void runner?.close());
|
|
1484
|
+
nextServer.watcher?.once?.("close", () => void runner?.close());
|
|
1485
|
+
}
|
|
1486
|
+
};
|
|
1487
|
+
}
|
|
1488
|
+
//#endregion
|
|
388
1489
|
//#region src/vite/index.ts
|
|
389
1490
|
const MARKLESS_SKIP_DUPLICATE_BUILDS = Symbol("markless-skip-duplicate-builds");
|
|
390
1491
|
const TSRX_INPUT_FILE = /\.tsrx(?:[?#].*)?$/;
|
|
@@ -392,19 +1493,33 @@ function markless(options = {}) {
|
|
|
392
1493
|
let command = "build";
|
|
393
1494
|
const bundleGraphAdders = /* @__PURE__ */ new Set();
|
|
394
1495
|
const transformedTsrxSources = /* @__PURE__ */ new Map();
|
|
1496
|
+
const prerenderRecordsBySource = /* @__PURE__ */ new Map();
|
|
395
1497
|
const rolldownOptions = { ...options };
|
|
1498
|
+
const prerender = process.env.MARKLESS_PRERENDER === "1";
|
|
1499
|
+
const explicitPrerenderWake = process.env.MARKLESS_PRERENDER_WAKE;
|
|
1500
|
+
rolldownOptions.prerender = false;
|
|
1501
|
+
rolldownOptions.prerenderWakeChannel = prerender ? explicitPrerenderWake !== "0" : explicitPrerenderWake === "1";
|
|
1502
|
+
const stagePrerenderWake = rolldownOptions.prerenderWakeChannel;
|
|
1503
|
+
rolldownOptions.productionResumeModuleUrls = /* @__PURE__ */ new Map();
|
|
1504
|
+
rolldownOptions.productionPrerenderWakeModuleUrls = /* @__PURE__ */ new Map();
|
|
1505
|
+
let prerenderEntry = null;
|
|
1506
|
+
let resolvedRoot = "";
|
|
1507
|
+
let clientOutDir = "dist";
|
|
396
1508
|
rolldownOptions.bundleGraphAdders = bundleGraphAdders;
|
|
397
1509
|
const hmrOptions = {
|
|
398
1510
|
base: "/",
|
|
399
1511
|
clientEnvironment: viteEnvironmentName("client", options),
|
|
400
1512
|
enabled: false,
|
|
401
|
-
invalidateGeneratedModules: (parent, environment) => marklessPlugin.api.invalidateGeneratedModules(parent, environment)
|
|
1513
|
+
invalidateGeneratedModules: (parent, environment, nextSource) => marklessPlugin.api.invalidateGeneratedModules(parent, environment, nextSource),
|
|
1514
|
+
invalidatePrerenderSnapshots: void 0
|
|
402
1515
|
};
|
|
1516
|
+
let devPrerender;
|
|
403
1517
|
const devTags = createDevTags();
|
|
404
1518
|
rolldownOptions.devInjections = devTags.tags;
|
|
405
1519
|
const basePlugin = createMarklessRolldownPlugin({
|
|
406
1520
|
environment: getBuildEnvironment,
|
|
407
|
-
options: rolldownOptions
|
|
1521
|
+
options: rolldownOptions,
|
|
1522
|
+
prerenderRecordsBySource
|
|
408
1523
|
});
|
|
409
1524
|
const hmr = createViteHmr(hmrOptions);
|
|
410
1525
|
const marklessPlugin = {
|
|
@@ -436,15 +1551,33 @@ function markless(options = {}) {
|
|
|
436
1551
|
__MARKLESS_DEV_ENABLED__: devDefine
|
|
437
1552
|
};
|
|
438
1553
|
configDefaults(config, options, rolldownOptions);
|
|
1554
|
+
if (prerender || stagePrerenderWake) {
|
|
1555
|
+
prerenderEntry = ssrTsrxInput(config, options);
|
|
1556
|
+
rolldownOptions.prerender = prerender && prerenderEntry !== null;
|
|
1557
|
+
}
|
|
439
1558
|
},
|
|
440
1559
|
configResolved(resolvedConfig) {
|
|
1560
|
+
resolvedRoot = resolvedConfig.root;
|
|
1561
|
+
clientOutDir = resolvedConfig.build?.outDir ?? "dist";
|
|
441
1562
|
const serve = resolvedConfig.command === "serve";
|
|
442
1563
|
hmrOptions.base = resolvedConfig.base;
|
|
443
1564
|
hmrOptions.enabled = serve && options.hmr !== false;
|
|
444
1565
|
rolldownOptions.dev = serve;
|
|
445
1566
|
rolldownOptions.rootDir = resolvedConfig.root;
|
|
446
1567
|
rolldownOptions.publicPath = (fileName) => joinURL(resolvedConfig.base, fileName);
|
|
447
|
-
if (serve)
|
|
1568
|
+
if (serve) {
|
|
1569
|
+
devTags.registerViteTags(resolvedConfig.base);
|
|
1570
|
+
if (prerender && prerenderEntry) {
|
|
1571
|
+
devPrerender = createDevPrerender({
|
|
1572
|
+
base: resolvedConfig.base,
|
|
1573
|
+
entry: resolve(resolvedConfig.root, prerenderEntry),
|
|
1574
|
+
serverEnvironment: viteEnvironmentName("server", options),
|
|
1575
|
+
reportError: (environment, error) => hmr.reportError(environment, error)
|
|
1576
|
+
});
|
|
1577
|
+
rolldownOptions.updateDevPrerenderHashes = devPrerender.updateHashes;
|
|
1578
|
+
hmrOptions.invalidatePrerenderSnapshots = devPrerender.invalidate;
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
448
1581
|
},
|
|
449
1582
|
configEnvironment(name, config) {
|
|
450
1583
|
if (command === "serve") return;
|
|
@@ -465,26 +1598,72 @@ function markless(options = {}) {
|
|
|
465
1598
|
},
|
|
466
1599
|
buildApp: {
|
|
467
1600
|
order: "pre",
|
|
468
|
-
handler(builder) {
|
|
469
|
-
|
|
1601
|
+
async handler(builder) {
|
|
1602
|
+
if ((prerender || stagePrerenderWake) && prerenderEntry) {
|
|
1603
|
+
const source = resolve(resolvedRoot, prerenderEntry);
|
|
1604
|
+
prerenderRecordsBySource.set(source, await deriveBuiltPrerenderRecords({
|
|
1605
|
+
entry: source,
|
|
1606
|
+
serverPlugin: createMarklessRolldownPlugin({
|
|
1607
|
+
environment: "server",
|
|
1608
|
+
options: {
|
|
1609
|
+
...rolldownOptions,
|
|
1610
|
+
rootDir: resolvedRoot,
|
|
1611
|
+
dev: false
|
|
1612
|
+
}
|
|
1613
|
+
})
|
|
1614
|
+
}));
|
|
1615
|
+
}
|
|
1616
|
+
await buildMarklessEnvironments(builder, options);
|
|
1617
|
+
if (prerender && prerenderEntry) {
|
|
1618
|
+
const source = resolve(resolvedRoot, prerenderEntry);
|
|
1619
|
+
const clientEnvironment = builder.environments[viteEnvironmentName("client", options)];
|
|
1620
|
+
await emitPrerenderedPage({
|
|
1621
|
+
root: clientEnvironment?.config.root ?? resolvedRoot,
|
|
1622
|
+
entry: resolve(resolvedRoot, prerenderEntry),
|
|
1623
|
+
outDir: clientEnvironment?.config.build?.outDir ?? clientOutDir,
|
|
1624
|
+
resumeModuleUrl: rolldownOptions.productionResumeModuleUrls?.get(source),
|
|
1625
|
+
prerenderWakeModuleUrl: rolldownOptions.productionPrerenderWakeModuleUrls?.get(source),
|
|
1626
|
+
serverPlugin: createMarklessRolldownPlugin({
|
|
1627
|
+
environment: "server",
|
|
1628
|
+
options: {
|
|
1629
|
+
...rolldownOptions,
|
|
1630
|
+
rootDir: resolvedRoot,
|
|
1631
|
+
dev: false
|
|
1632
|
+
}
|
|
1633
|
+
})
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
470
1636
|
}
|
|
471
1637
|
},
|
|
472
1638
|
configureServer(server) {
|
|
473
1639
|
server.config.logger?.info("markless diagnostics available - window.__MARKLESS_DEBUG__ records containers, lifecycles, and event routing; markless debugging playbook: run pnpm doctor, or read agent/markless.md in the installed @markless/core package.");
|
|
474
|
-
rolldownOptions.devServer = {
|
|
1640
|
+
rolldownOptions.devServer = {
|
|
1641
|
+
transformRequest: (url, environment) => transformMarklessRequest(server, url, environment, options),
|
|
1642
|
+
invalidateModule: (id, environment) => {
|
|
1643
|
+
const target = server.environments[viteEnvironmentName(environment, options)];
|
|
1644
|
+
const module = target?.moduleGraph?.getModuleById?.(id);
|
|
1645
|
+
if (!module) return false;
|
|
1646
|
+
target.moduleGraph.invalidateModule(module, /* @__PURE__ */ new Set(), Date.now(), true);
|
|
1647
|
+
return true;
|
|
1648
|
+
}
|
|
1649
|
+
};
|
|
475
1650
|
hmr.configureServer(server);
|
|
1651
|
+
devPrerender?.configureServer(server);
|
|
476
1652
|
},
|
|
477
|
-
transformIndexHtml() {
|
|
1653
|
+
transformIndexHtml(html) {
|
|
478
1654
|
const hmrResult = hmr.transformIndexHtml();
|
|
479
1655
|
const injection = executionLogActivationInjection(rolldownOptions.executionLog);
|
|
480
|
-
|
|
481
|
-
const tag = {
|
|
1656
|
+
const tags = [...Array.isArray(hmrResult) ? hmrResult : [], ...injection ? [{
|
|
482
1657
|
tag: injection.tag,
|
|
483
1658
|
injectTo: injection.location,
|
|
484
1659
|
attrs: injection.attributes,
|
|
485
1660
|
children: injection.children
|
|
486
|
-
};
|
|
487
|
-
|
|
1661
|
+
}] : []];
|
|
1662
|
+
if (!devPrerender) return tags.length > 0 ? tags : void 0;
|
|
1663
|
+
return devPrerender.renderHtml(html).then((renderedHtml) => ({
|
|
1664
|
+
html: renderedHtml,
|
|
1665
|
+
tags
|
|
1666
|
+
}));
|
|
488
1667
|
},
|
|
489
1668
|
resolveId: {
|
|
490
1669
|
order: "pre",
|
|
@@ -502,7 +1681,10 @@ function markless(options = {}) {
|
|
|
502
1681
|
try {
|
|
503
1682
|
return await runHook(basePlugin.transform, this, code, id, transformOptions);
|
|
504
1683
|
} catch (error) {
|
|
505
|
-
if (rolldownOptions.dev === true && TSRX_INPUT_FILE.test(id))
|
|
1684
|
+
if (rolldownOptions.dev === true && TSRX_INPUT_FILE.test(id)) {
|
|
1685
|
+
const environment = this.environment;
|
|
1686
|
+
hmr.reportError(environment.mode === "dev" ? environment : void 0, error);
|
|
1687
|
+
}
|
|
506
1688
|
throw error;
|
|
507
1689
|
}
|
|
508
1690
|
} },
|
|
@@ -572,22 +1754,23 @@ function firstTsrxInput(input) {
|
|
|
572
1754
|
return null;
|
|
573
1755
|
}
|
|
574
1756
|
function withSsrSymbolInput(input, ssrSymbolInput) {
|
|
1757
|
+
const symbolInput = withQuery(ssrSymbolInput, { "markless-symbols": null });
|
|
575
1758
|
if (isRolldownInputRecord(input)) {
|
|
576
1759
|
if (Object.keys(input).some((name) => /symbol/i.test(name))) return input;
|
|
577
1760
|
return {
|
|
578
1761
|
...input,
|
|
579
|
-
symbols:
|
|
1762
|
+
symbols: symbolInput
|
|
580
1763
|
};
|
|
581
1764
|
}
|
|
582
1765
|
if (typeof input === "string") {
|
|
583
1766
|
const name = input.endsWith(".html") ? "index" : "app";
|
|
584
|
-
return input === ssrSymbolInput ? { symbols:
|
|
1767
|
+
return input === ssrSymbolInput ? { symbols: symbolInput } : {
|
|
585
1768
|
[name]: input,
|
|
586
|
-
symbols:
|
|
1769
|
+
symbols: symbolInput
|
|
587
1770
|
};
|
|
588
1771
|
}
|
|
589
|
-
if (Array.isArray(input)) return Object.fromEntries([...input.map((entry, index) => [`input${index}`, entry]), ["symbols",
|
|
590
|
-
return { symbols:
|
|
1772
|
+
if (Array.isArray(input)) return Object.fromEntries([...input.map((entry, index) => [`input${index}`, entry]), ["symbols", symbolInput]]);
|
|
1773
|
+
return { symbols: symbolInput };
|
|
591
1774
|
}
|
|
592
1775
|
function isRolldownInputRecord(input) {
|
|
593
1776
|
return !!input && typeof input === "object" && !Array.isArray(input);
|