@markless/bundler 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/vite.js CHANGED
@@ -1,10 +1,10 @@
1
- import { c as renderMarklessDevErrorDocument, n as MARKLESS_DEV_ERROR_CLIENT_ID, r as MARKLESS_DEV_ERROR_EVENT, s as normalizeMarklessDevError, t as MARKLESS_DEV_ERROR_CLEAR_EVENT } from "./dev-error-DRvO28Fd.js";
2
- import { Et as executionLogActivationInjection, I as sharedReachedRenderDataRequest, at as STORAGE_FREE_RESUME_ENTRY_SPECIFIER, it as RESUME_ENTRY_SPECIFIER, j as pathname, nt as outputDefaults, t as MARKLESS_DEFERRED_PACK } from "./route-pack-groups-BpefyjrE.js";
1
+ import { a as MARKLESS_DEV_RELOADS_ATTRIBUTE, d as renderMarklessDevErrorDocument, i as MARKLESS_DEV_PAGE_EDIT_EVENT, n as MARKLESS_DEV_ERROR_CLIENT_ID, o as MARKLESS_DEV_RELOADS_EVENT, r as MARKLESS_DEV_ERROR_EVENT, t as MARKLESS_DEV_ERROR_CLEAR_EVENT, u as normalizeMarklessDevError } from "./dev-error-BEduK_R3.js";
2
+ import { N as pathname, R as sharedReachedRenderDataRequest, at as outputDefaults, ct as STORAGE_FREE_RESUME_ENTRY_SPECIFIER, kt as executionLogActivationInjection, st as RESUME_ENTRY_SPECIFIER, t as MARKLESS_DEFERRED_PACK } from "./route-pack-groups-BXc755qH.js";
3
3
  import { n as includeOptimizedDeps, t as excludeSourceShippedDeps } from "./optimized-deps-BfzxSm6C.js";
4
- import { A as inlineDeferSource, E as preflightTsrxModuleDiagnostics, O as INLINE_DEFER_LOADER_SOURCE, T as prefetchChunkParses, _ as createPreloadGraphAdder, a as collapseInitFacadeImports, d as nativePackingEnabled, f as nativePackingPlugins, h as withUndemandedReExportDoors, k as createInlineDeferTriggerSource, l as marklessRuntimeSpecifierId, m as undemandedRuntimeModules, o as unwrapAsyncImportWrappers, p as isReExportDoor, t as createMarklessRolldownPlugin, u as DEPRECATED_NATIVE_PACKING_WARNING, v as runtimeModuleIdFromOrigin } from "./rolldown-BciZw5hZ.js";
4
+ import { A as createInlineDeferTriggerSource, D as preflightTsrxModuleDiagnostics, E as prefetchChunkParses, M as moduleIdFor, _ as createPreloadGraphAdder, a as collapseInitFacadeImports, d as nativePackingEnabled, f as nativePackingPlugins, h as withUndemandedReExportDoors, j as inlineDeferSource, k as INLINE_DEFER_LOADER_SOURCE, l as marklessRuntimeSpecifierId, m as undemandedRuntimeModules, o as unwrapAsyncImportWrappers, p as isReExportDoor, t as createMarklessRolldownPlugin, u as DEPRECATED_NATIVE_PACKING_WARNING, v as runtimeModuleIdFromOrigin } from "./rolldown-DpkfQO1h.js";
5
5
  import { n as marklessRosterPositions, r as marklessRosterRenderContext, t as marklessRosterPositionSeeds } from "./shared-seed-slot-ihIVCD2S.js";
6
6
  import { joinURL, parsePath, withQuery } from "ufo";
7
- import { ASYNC_PROTOCOL_VERSION, STORAGE_SLOT_SYMBOL_KEY, escapeScriptJson, serializeRuntimeAsyncSnapshots, serializeRuntimeStateCells, storageAttributeName, storageCookieName, storageSlotEntryKeyFromGraphNodeId } from "@markless/serializer";
7
+ import { ASYNC_PROTOCOL_VERSION, PROTOCOL_CONTAINER_HOST_NODE_ID, STORAGE_SLOT_SYMBOL_KEY, escapeScriptJson, serializeRuntimeAsyncSnapshots, serializeRuntimeStateCells, storageAttributeName, storageCookieName, storageSlotEntryKeyFromGraphNodeId } from "@markless/serializer";
8
8
  import { isAbsolute, join, resolve } from "pathe";
9
9
  import { DEBUGGING_PLAYBOOK_HINT } from "@markless/compiler";
10
10
  import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
@@ -12,26 +12,30 @@ import { isAbsolute as isAbsolute$1 } from "node:path";
12
12
  import { pathToFileURL } from "node:url";
13
13
  import { createServerModuleRunner, isRunnableDevEnvironment } from "vite";
14
14
  import { tmpdir } from "node:os";
15
- import { PROTOCOL_EVENT_ACTION_KIND as PROTOCOL_EVENT_ACTION_KIND$1, PROTOCOL_IDLE_EVENT_NAME as PROTOCOL_IDLE_EVENT_NAME$1, PROTOCOL_LOCATOR_PATHS_ATTRIBUTE, PROTOCOL_VISIBLE_EVENT_NAME, protocolEventDispatchesMarkless } from "@markless/serializer/protocol";
15
+ import { PROTOCOL_CONTAINER_END_COMMENT, PROTOCOL_CONTAINER_START_COMMENT, PROTOCOL_EVENT_ACTION_KIND as PROTOCOL_EVENT_ACTION_KIND$1, PROTOCOL_IDLE_EVENT_NAME as PROTOCOL_IDLE_EVENT_NAME$1, PROTOCOL_LOCATOR_PATHS_ATTRIBUTE, PROTOCOL_VISIBLE_EVENT_NAME, protocolEventDispatchesMarkless } from "@markless/serializer/protocol";
16
16
  import "@markless/serializer/resume-record-delta";
17
17
  //#region ../web/src/inline/served-census.ts
18
18
  /**
19
- * Pins the container's elements in DOM order as its census and returns them.
20
- * When the count differs from the one the server measured, a script outside
21
- * Markless changed the page first: the root is flagged for the resume module
22
- * to renumber the census, and no elements are returned, so nothing is matched
23
- * by a shifted index.
19
+ * Pins the container's elements in DOM order as its census and returns them,
20
+ * between the page start and end markers an author's element carries. When the
21
+ * count differs from the one the server measured, a script outside Markless
22
+ * changed the page first: the root is flagged for the resume module to renumber
23
+ * the census, and no elements are returned, so nothing is matched by a shifted index.
24
24
  */
25
25
  function marklessServedCensus(root, attribute) {
26
26
  const live = [root];
27
- const walker = document.createTreeWalker(root, 1);
28
- for (let next = walker.nextNode(); next; next = walker.nextNode()) live.push(next);
27
+ const walker = document.createTreeWalker(root, 129);
28
+ for (let next = walker.nextNode(); next; next = walker.nextNode()) if (next.nodeType !== 8) live.push(next);
29
+ else if (next.data === __MARKLESS_PAGE_START__) live.length = 1;
30
+ else if (next.data === __MARKLESS_PAGE_END__) break;
29
31
  root.__marklessCensus ||= live;
30
32
  const value = root.getAttribute?.(attribute);
31
33
  return value && parseInt(value) !== live.length ? (root.__marklessShifted = 1, []) : live;
32
34
  }
33
35
  //#endregion
34
36
  //#region ../web/src/inline/resumer.ts
37
+ const withPageMarkers = (source) => source.replaceAll("__MARKLESS_PAGE_START__", JSON.stringify(PROTOCOL_CONTAINER_START_COMMENT)).replaceAll("__MARKLESS_PAGE_END__", JSON.stringify(PROTOCOL_CONTAINER_END_COMMENT));
38
+ const servedCensusSource = () => withPageMarkers(marklessServedCensus.toString());
35
39
  PROTOCOL_EVENT_ACTION_KIND$1.event, PROTOCOL_EVENT_ACTION_KIND$1.externalDelegate;
36
40
  const INLINE_MODULE_LOADER_SOURCE = INLINE_DEFER_LOADER_SOURCE;
37
41
  function createInlineResumerSelfWakeSource(resumeModuleUrl) {
@@ -54,14 +58,14 @@ function runInlineResumerStorageWake(slotSymbolKey, rendered, wake) {
54
58
  * with no mark ships a byte-identical resumer.
55
59
  */
56
60
  function createInlineResumerOverlayPrimerSource(resumeModuleUrl) {
57
- return `;(${runInlineResumerOverlayPrimer.toString()})(${JSON.stringify(resumeModuleUrl)},${INLINE_MODULE_LOADER_SOURCE});`;
61
+ return `;(${withPageMarkers(runInlineResumerOverlayPrimer.toString())})(${JSON.stringify(resumeModuleUrl)},${INLINE_MODULE_LOADER_SOURCE});`;
58
62
  }
59
63
  const PRERENDER_INLINE_EVENT_NAMES_TOKEN = "__MARKLESS_PRERENDER_EVENT_NAMES__";
60
64
  function renderPrerenderInlineResumerSource(compiled, eventNames) {
61
65
  return compiled.replace(PRERENDER_INLINE_EVENT_NAMES_TOKEN, JSON.stringify(eventNames));
62
66
  }
63
67
  function createPrerenderInlineResumerSource(eventNames, resumeModuleUrl, options) {
64
- const plain = `(${runPrerenderInlineResumer.toString()})(${JSON.stringify(eventNames)},${JSON.stringify(resumeModuleUrl)},u=>import(u),${marklessServedCensus.toString()},${JSON.stringify(PROTOCOL_LOCATOR_PATHS_ATTRIBUTE)});`;
68
+ const plain = `(${runPrerenderInlineResumer.toString()})(${JSON.stringify(eventNames)},${JSON.stringify(resumeModuleUrl)},u=>import(u),${servedCensusSource()},${JSON.stringify(PROTOCOL_LOCATOR_PATHS_ATTRIBUTE)});`;
65
69
  const logging = options?.executionLog !== void 0 && options.executionLog !== "never";
66
70
  if (!options?.debug && !logging) return plain;
67
71
  return `{
@@ -174,7 +178,7 @@ function runInlineResumerSelfWake(fallbackResumeModuleUrl, loadModule, defer) {
174
178
  defer.idle(root, async () => {
175
179
  if (root.__asyncResumeRuntimeStarted) return;
176
180
  root.__marklessDelegatedDispatch = true;
177
- await (await loadModule(resumeModuleUrl)).resumeContainerEvent({
181
+ await (await (root.__mResume ||= loadModule(resumeModuleUrl))).resumeContainerEvent({
178
182
  root,
179
183
  event: 0
180
184
  });
@@ -196,8 +200,10 @@ function runInlineResumerOverlayPrimer(fallbackResumeModuleUrl, loadModule) {
196
200
  if (root.__marklessDelegatedDispatch === true) return;
197
201
  if (!root.__marklessCensus) {
198
202
  const census = [root];
199
- const all = root.getElementsByTagName?.("*") ?? [];
200
- for (let index = 0; index < all.length; index++) census.push(all[index]);
203
+ const walker = document.createTreeWalker(root, 129);
204
+ for (let next = walker.nextNode(); next; next = walker.nextNode()) if (next.nodeType !== 8) census.push(next);
205
+ else if (next.data === __MARKLESS_PAGE_START__) census.length = 1;
206
+ else if (next.data === __MARKLESS_PAGE_END__) break;
201
207
  root.__marklessCensus = census;
202
208
  }
203
209
  setTimeout(() => {
@@ -232,12 +238,19 @@ async function prepareSsrResumeRecords(output) {
232
238
  };
233
239
  }
234
240
  function containerScopedView(view) {
241
+ const locators = view.locators.map((locator) => ({
242
+ ...locator,
243
+ index: locator.index + 1
244
+ }));
245
+ if (view.keyedRepeats?.some((repeat) => repeat.parentHostNodeId === PROTOCOL_CONTAINER_HOST_NODE_ID)) locators.unshift({
246
+ hostNodeId: PROTOCOL_CONTAINER_HOST_NODE_ID,
247
+ strategy: "dom-order",
248
+ index: 0,
249
+ tagName: "*"
250
+ });
235
251
  return {
236
252
  ...view,
237
- locators: view.locators.map((locator) => ({
238
- ...locator,
239
- index: locator.index + 1
240
- }))
253
+ locators
241
254
  };
242
255
  }
243
256
  function emptyStatePayload() {
@@ -259,6 +272,9 @@ function emptyViewPayload() {
259
272
  };
260
273
  }
261
274
  //#endregion
275
+ //#region ../web/src/fns/container-region.ts
276
+ const CONTAINER_ATTRIBUTE = "data-async-container";
277
+ //#endregion
262
278
  //#region ../web/src/fns/roster-position.ts
263
279
  /**
264
280
  * The server render context with the roster channels on it. One counter per
@@ -485,7 +501,7 @@ async function assemblePrerenderPageParts(component, output, options) {
485
501
  const { state, view } = await prepareSsrResumeRecords(output);
486
502
  const browserTriggers = hasBrowserTriggers(view, state);
487
503
  const resumeModuleUrl = options.prerenderWakeModuleUrl ?? options.resumeModuleUrl ?? artifactPrerenderWakeModuleUrl(component) ?? artifactResumeModuleUrl(component);
488
- const modulePreloads = (typeof options.modulePreloads === "function" ? options.modulePreloads(output.html) : options.modulePreloads) ?? (browserTriggers ? artifactModulePreloads(component) : void 0);
504
+ const modulePreloads = (typeof options.modulePreloads === "function" ? options.modulePreloads(output.html, browserTriggers && resumeModuleUrl !== void 0) : options.modulePreloads) ?? (browserTriggers ? artifactModulePreloads(component) : void 0);
489
505
  const eventNames = browserEventNames(view);
490
506
  const selfWake = hasUnsettledAsyncBoundaryRunner(view, state);
491
507
  const boot = artifactPrerenderBoot(component);
@@ -656,25 +672,34 @@ function eventHostIndexes(view, eventName) {
656
672
  function deferredTriggerSource(view) {
657
673
  const visible = eventHostIndexes(view, PROTOCOL_VISIBLE_EVENT_NAME);
658
674
  const idle = eventHostIndexes(view, PROTOCOL_IDLE_EVENT_NAME$1);
659
- if (browserEventNames({
675
+ const nested = deferredEventNames({
660
676
  ...view,
661
677
  events: []
662
- }).includes(PROTOCOL_IDLE_EVENT_NAME$1)) idle.push(0);
678
+ });
679
+ if (nested.includes(PROTOCOL_VISIBLE_EVENT_NAME)) visible.push(0);
680
+ if (nested.includes(PROTOCOL_IDLE_EVENT_NAME$1)) idle.push(0);
663
681
  return (visible.length > 0 ? createInlineDeferTriggerSource("visible", visible) : "") + (idle.length > 0 ? createInlineDeferTriggerSource("idle", idle) : "");
664
682
  }
665
683
  function browserEventNames(view) {
684
+ return deferredEventNames(view).filter((eventName) => eventName !== PROTOCOL_VISIBLE_EVENT_NAME);
685
+ }
686
+ function deferredEventNames(view) {
666
687
  return [.../* @__PURE__ */ new Set([
667
688
  ...view.events.filter(protocolEventDispatchesMarkless).map((event) => event.eventName),
668
689
  ...(view.keyedRepeats ?? []).flatMap((repeat) => repeat.rowEvents.map((event) => event.eventName)),
669
690
  ...(view.branches ?? []).flatMap((branch) => [...(branch.armRecords ?? []).flatMap((arm) => arm.events.filter(protocolEventDispatchesMarkless).map((event) => event.eventName)), ...branchServedArmEventNames(branch)]),
670
691
  ...view.asyncBoundaries.flatMap(boundaryArmEventNames)
671
- ])].filter((eventName) => eventName !== PROTOCOL_VISIBLE_EVENT_NAME);
692
+ ])];
672
693
  }
673
694
  function renderContainer(containerId, parts, locators) {
674
695
  const inner = parts.filter(Boolean).join("");
675
696
  const paths = servedLocatorPaths(inner, locators);
676
- return `<div data-async-container${containerId ? `="${escapeAttribute(containerId)}"` : ""}${paths ? ` ${PROTOCOL_LOCATOR_PATHS_ATTRIBUTE}="${paths}"` : ""}>${inner}</div>`;
697
+ const id = containerId ? `="${escapeAttribute(containerId)}"` : "";
698
+ const located = paths ? ` ${PROTOCOL_LOCATOR_PATHS_ATTRIBUTE}="${paths}"` : "";
699
+ return `${CONTAINER_OPEN}${id}${located}>${inner}${CONTAINER_CLOSE}`;
677
700
  }
701
+ const CONTAINER_OPEN = `<div ${CONTAINER_ATTRIBUTE}`;
702
+ const CONTAINER_CLOSE = "</div>";
678
703
  function renderInlineResumerScript(source, nonce, resumeModuleUrl, selfWake, settleModuleUrl, appendSelfWakeSource = true, overlayPrimer = false, appendedSource = "", deferFactory) {
679
704
  const nonceAttribute = nonce ? ` nonce="${escapeAttribute(nonce)}"` : "";
680
705
  const resumeModuleAttribute = resumeModuleUrl ? ` data-markless-resume-module="${escapeAttribute(resumeModuleUrl)}"` : "";
@@ -1327,6 +1352,9 @@ function createDevErrorClientAsset(viteClientUrl) {
1327
1352
  const hot = createHotContext(${JSON.stringify(MARKLESS_DEV_ERROR_CLIENT_ID)});
1328
1353
  const ERROR_EVENT = ${JSON.stringify(MARKLESS_DEV_ERROR_EVENT)};
1329
1354
  const CLEAR_EVENT = ${JSON.stringify(MARKLESS_DEV_ERROR_CLEAR_EVENT)};
1355
+ const RELOADS_EVENT = ${JSON.stringify(MARKLESS_DEV_RELOADS_EVENT)};
1356
+ const RELOADS_ATTRIBUTE = ${JSON.stringify(MARKLESS_DEV_RELOADS_ATTRIBUTE)};
1357
+ const PAGE_EDIT_EVENT = ${JSON.stringify(MARKLESS_DEV_PAGE_EDIT_EVENT)};
1330
1358
  const ELEMENT = 'markless-dev-error-overlay';
1331
1359
  const SUPPRESSION = 'data-markless-vite-overlay-suppression';
1332
1360
  const OPEN_IN_EDITOR = ${JSON.stringify(`${base}/__open-in-editor`)};
@@ -1400,6 +1428,10 @@ hot.on(CLEAR_EVENT, ({ id }) => { if (currentPayload?.id === id) reset(); });
1400
1428
  hot.on('vite:beforeFullReload', reset);
1401
1429
  addEventListener('keydown', (event) => { if (event.key === 'Escape') document.querySelector(ELEMENT)?.remove(); });
1402
1430
  if (window.__MARKLESS_DEV_ERROR__) show(window.__MARKLESS_DEV_ERROR__);
1431
+ const servedAfter = document.querySelector('script[' + RELOADS_ATTRIBUTE + ']')?.getAttribute(RELOADS_ATTRIBUTE);
1432
+ const report = (edit) => hot.send(RELOADS_EVENT, { reloads: servedAfter != null ? Number(servedAfter) : edit, href: location.href });
1433
+ if (servedAfter != null) report();
1434
+ hot.on(PAGE_EDIT_EVENT, ({ edit }) => report(edit));
1403
1435
  `;
1404
1436
  }
1405
1437
  //#endregion
@@ -1464,6 +1496,20 @@ function createViteHmr(options) {
1464
1496
  let recheckingInvalidSources = false;
1465
1497
  const preflightByFile = /* @__PURE__ */ new Map();
1466
1498
  const unreportedInvalidations = /* @__PURE__ */ new Map();
1499
+ const edits = [];
1500
+ let lastReloadedEdit;
1501
+ function recordEdit(edit, pages) {
1502
+ if (edit === lastReloadedEdit) return void 0;
1503
+ lastReloadedEdit = edit;
1504
+ return edits.push(pages) - 1;
1505
+ }
1506
+ function servedPageIsStale(data) {
1507
+ const since = typeof data?.reloads === "number" ? data.reloads : edits.length;
1508
+ const later = edits.slice(Math.max(0, since));
1509
+ if (later.length === 0) return false;
1510
+ const page = typeof data?.href === "string" && options.pageForUrl ? options.pageForUrl(data.href) : void 0;
1511
+ return later.some((pages) => pages === "all" || page === void 0 || pages.has(page));
1512
+ }
1467
1513
  let hotUpdatePasses = 0;
1468
1514
  let hotUpdateCycle;
1469
1515
  function beginHotUpdatePass() {
@@ -1587,7 +1633,10 @@ function createViteHmr(options) {
1587
1633
  sendClear(id);
1588
1634
  recovered = true;
1589
1635
  }
1590
- if (recovered) server?.environments?.[options.clientEnvironment]?.hot?.send?.({ type: "full-reload" });
1636
+ if (recovered) {
1637
+ recordEdit(`recovered\0${Date.now()}`, "all");
1638
+ server?.environments?.[options.clientEnvironment]?.hot?.send?.({ type: "full-reload" });
1639
+ }
1591
1640
  } finally {
1592
1641
  recheckingInvalidSources = false;
1593
1642
  stopInvalidSourceRecheckIfIdle();
@@ -1612,7 +1661,15 @@ function createViteHmr(options) {
1612
1661
  installServerRenderBarrier(nextServer);
1613
1662
  nextServer.httpServer?.once("close", cleanup);
1614
1663
  nextServer.watcher?.once?.("close", cleanup);
1615
- if (options.enabled) installFetchViteClient(nextServer, options);
1664
+ if (options.enabled) {
1665
+ installFetchViteClient(nextServer, options, async () => {
1666
+ await settledHotUpdate();
1667
+ return edits.length;
1668
+ });
1669
+ nextServer.environments?.[options.clientEnvironment]?.hot?.on?.(MARKLESS_DEV_RELOADS_EVENT, (data, client) => {
1670
+ if (servedPageIsStale(data)) client.send({ type: "full-reload" });
1671
+ });
1672
+ }
1616
1673
  },
1617
1674
  transformIndexHtml() {},
1618
1675
  resolveId(id) {
@@ -1641,6 +1698,49 @@ function createViteHmr(options) {
1641
1698
  }
1642
1699
  }
1643
1700
  };
1701
+ function loadedOrInvalid(environments, file) {
1702
+ const path = parsePath(file).pathname;
1703
+ if (invalidSources.has(path)) return true;
1704
+ let answered = false;
1705
+ for (const environment of environments) {
1706
+ if (typeof environment.moduleGraph?.getModulesByFile !== "function") continue;
1707
+ answered = true;
1708
+ if (environment.moduleGraph.getModulesByFile(path)?.size) return true;
1709
+ }
1710
+ return !answered;
1711
+ }
1712
+ function editedPages(targets, file) {
1713
+ const isPage = options.isPage;
1714
+ const root = server?.config?.root;
1715
+ if (!isPage || !options.pageForUrl || !root) return "all";
1716
+ const pageOf = (module) => {
1717
+ const id = module.file ? moduleIdFor(module.file, root) : void 0;
1718
+ return id !== void 0 && isPage(id) ? id : void 0;
1719
+ };
1720
+ const pending = [];
1721
+ for (const [target, scope] of targets) {
1722
+ if (scope !== "server") continue;
1723
+ pending.push(...target.moduleGraph?.getModulesByFile?.(parsePath(file).pathname) ?? []);
1724
+ }
1725
+ if (pending.length === 0) return "all";
1726
+ const pages = /* @__PURE__ */ new Set();
1727
+ const seen = /* @__PURE__ */ new Set();
1728
+ while (pending.length > 0) {
1729
+ const module = pending.pop();
1730
+ if (seen.has(module)) continue;
1731
+ seen.add(module);
1732
+ const page = pageOf(module);
1733
+ const importers = [...module.importers ?? []];
1734
+ if (page) {
1735
+ pages.add(page);
1736
+ pending.push(...importers.filter((importer) => pageOf(importer)));
1737
+ continue;
1738
+ }
1739
+ if (importers.length === 0) return "all";
1740
+ pending.push(...importers);
1741
+ }
1742
+ return pages;
1743
+ }
1644
1744
  async function runHotUpdate(environment, ctx) {
1645
1745
  if (!environment) return;
1646
1746
  const env = marklessEnvironment(environment);
@@ -1659,6 +1759,22 @@ function createViteHmr(options) {
1659
1759
  }
1660
1760
  const modules = new Set(ctx.modules ?? []);
1661
1761
  if (ctx.file) for (const target of targets.keys()) for (const module of target.moduleGraph?.getModulesByFile?.(ctx.file) ?? []) modules.add(module);
1762
+ const listingModules = /* @__PURE__ */ new Map();
1763
+ if (ctx.type !== "update") {
1764
+ listingModules.set(environment, new Set(ctx.modules ?? []));
1765
+ for (const target of targets.keys()) for (const module of await globImporters(target, ctx)) {
1766
+ const listed = listingModules.get(target) ?? /* @__PURE__ */ new Set();
1767
+ listed.add(module);
1768
+ listingModules.set(target, listed);
1769
+ modules.add(module);
1770
+ }
1771
+ }
1772
+ if (modules.size === 0 && ctx.file && !loadedOrInvalid(targets.keys(), ctx.file)) return [];
1773
+ for (const [target, listed] of listingModules) for (const module of listed) {
1774
+ const invalidated = /* @__PURE__ */ new Set();
1775
+ target.moduleGraph?.invalidateModule?.(module, invalidated, ctx.timestamp, true);
1776
+ recordUnreportedInvalidation(target, invalidated);
1777
+ }
1662
1778
  const files = changedFiles([...modules]);
1663
1779
  const root = server?.config?.root;
1664
1780
  if (ctx.file && SOURCE_FILE_EXTENSION.test(ctx.file)) {
@@ -1722,14 +1838,31 @@ function createViteHmr(options) {
1722
1838
  recordUnreportedInvalidation(targetEnvironment, invalidated);
1723
1839
  }
1724
1840
  reloadModuleRunner();
1725
- hot.send({
1841
+ const pages = ctx.type === "update" && ctx.file ? editedPages(targets, ctx.file) : "all";
1842
+ const edit = recordEdit(`${ctx.file}\0${ctx.timestamp}`, pages);
1843
+ if (pages === "all") hot.send({
1726
1844
  type: "full-reload",
1727
1845
  path: firstChangedFile(files),
1728
1846
  triggeredBy: ctx.file
1729
1847
  });
1848
+ else if (edit !== void 0) hot.send({
1849
+ type: "custom",
1850
+ event: MARKLESS_DEV_PAGE_EDIT_EVENT,
1851
+ data: { edit }
1852
+ });
1730
1853
  return [];
1731
1854
  }
1732
1855
  }
1856
+ async function globImporters(environment, ctx) {
1857
+ const hook = (environment.plugins?.find((candidate) => candidate.name === "vite:import-glob"))?.hotUpdate;
1858
+ const handler = typeof hook === "function" ? hook : hook?.handler;
1859
+ if (!handler) return [];
1860
+ const modules = await handler.call({ environment }, {
1861
+ ...ctx,
1862
+ modules: []
1863
+ });
1864
+ return Array.isArray(modules) ? modules : [];
1865
+ }
1733
1866
  async function fetchedModuleNode(environment, id) {
1734
1867
  const resolved = id.startsWith(VITE_ID_PREFIX) ? id.slice(5).replace(VITE_NULL_BYTE, "\0") : id;
1735
1868
  return environment.moduleGraph.getModuleById(resolved) ?? await environment.moduleGraph.getModuleByUrl(id).catch(() => void 0);
@@ -1737,16 +1870,17 @@ async function fetchedModuleNode(environment, id) {
1737
1870
  function hasErrorCode(error, code) {
1738
1871
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
1739
1872
  }
1740
- function installFetchViteClient(server, options) {
1873
+ function installFetchViteClient(server, options, servedAfterReloads) {
1741
1874
  for (const environment of Object.values(server.environments)) {
1742
1875
  const fetchEnv = fetchableDevEnvironment(environment);
1743
1876
  if (!fetchEnv) continue;
1744
1877
  const dispatchFetch = fetchEnv.dispatchFetch.bind(fetchEnv);
1745
1878
  fetchEnv.dispatchFetch = async (request) => {
1879
+ const reloads = await servedAfterReloads();
1746
1880
  const response = await dispatchFetch(request);
1747
1881
  if (!response.headers.get("content-type")?.includes("text/html")) return response;
1748
1882
  const html = await response.text();
1749
- const nextHtml = injectDevClients(html, options.base);
1883
+ const nextHtml = injectDevClients(html, options.base, reloads);
1750
1884
  const headers = new Headers(response.headers);
1751
1885
  if (nextHtml !== html) headers.delete("content-length");
1752
1886
  return new Response(nextHtml, {
@@ -1757,9 +1891,9 @@ function installFetchViteClient(server, options) {
1757
1891
  };
1758
1892
  }
1759
1893
  }
1760
- function injectDevClients(html, base) {
1894
+ function injectDevClients(html, base, reloads) {
1761
1895
  if (!html) return html;
1762
- const tags = [...html.includes("/@vite/client") ? [] : [`<script type="module" src="${joinURL(base, "/@vite/client")}"><\/script>`], ...html.includes("virtual:markless:dev-error-client") ? [] : [`<script type="module" src="${joinURL(base, `/@id/__x00__${MARKLESS_DEV_ERROR_CLIENT_ID}`)}"><\/script>`]].join("");
1896
+ const tags = [...html.includes("/@vite/client") ? [] : [`<script type="module" src="${joinURL(base, "/@vite/client")}"><\/script>`], ...html.includes("virtual:markless:dev-error-client") ? [] : [`<script type="module" src="${joinURL(base, `/@id/__x00__${MARKLESS_DEV_ERROR_CLIENT_ID}`)}"${reloads === void 0 ? "" : ` ${MARKLESS_DEV_RELOADS_ATTRIBUTE}="${reloads}"`}><\/script>`]].join("");
1763
1897
  if (!tags) return html;
1764
1898
  if (html.includes("</head>")) return html.replace("</head>", `${tags}</head>`);
1765
1899
  if (html.includes("<head>")) return html.replace("<head>", `<head>${tags}`);
@@ -1957,7 +2091,9 @@ function markless(options = {}) {
1957
2091
  clientEnvironment: viteEnvironmentName("client", options),
1958
2092
  enabled: false,
1959
2093
  invalidateGeneratedModules: (parent, environment, nextSource) => marklessPlugin.api.invalidateGeneratedModules(parent, environment, nextSource),
1960
- invalidatePrerenderSnapshots: void 0
2094
+ invalidatePrerenderSnapshots: void 0,
2095
+ isPage: void 0,
2096
+ pageForUrl: void 0
1961
2097
  };
1962
2098
  let devPrerender;
1963
2099
  const devTags = createDevTags();
@@ -1975,6 +2111,13 @@ function markless(options = {}) {
1975
2111
  sharedDuringBuild: true,
1976
2112
  api: {
1977
2113
  ...basePlugin.api,
2114
+ declareRouteModules: (isRoute) => {
2115
+ basePlugin.api.declareRouteModules(isRoute);
2116
+ hmrOptions.isPage = isRoute;
2117
+ },
2118
+ declarePageForUrl: (pageForUrl) => {
2119
+ hmrOptions.pageForUrl = pageForUrl;
2120
+ },
1978
2121
  registerBundleGraphAdder: (adder) => bundleGraphAdders.add(adder),
1979
2122
  registerDevInjection: (injection) => devTags.register(injection),
1980
2123
  registerPreloadGraphEntries: (adder) => bundleGraphAdders.add(createPreloadGraphAdder(adder))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markless/bundler",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Markless build plugins for Rolldown and Vite.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -39,12 +39,12 @@
39
39
  "@thi.ng/dgraph": "^2.1.205",
40
40
  "pathe": "^2.0.3",
41
41
  "ufo": "^1.6.4",
42
- "@markless/serializer": "0.5.0",
43
- "@markless/compiler": "0.5.0",
44
- "@markless/web": "0.5.0"
42
+ "@markless/compiler": "0.7.0",
43
+ "@markless/web": "0.7.0",
44
+ "@markless/serializer": "0.7.0"
45
45
  },
46
46
  "devDependencies": {
47
- "@markless/analyzer": "0.5.0"
47
+ "@markless/analyzer": "0.7.0"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "rolldown": "1.2.7",