@markless/bundler 0.3.3 → 0.4.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.
@@ -9,6 +9,9 @@ type BuiltPrerenderRecords = {
9
9
  readonly view: ProtocolViewPayload;
10
10
  };
11
11
  //#endregion
12
+ //#region src/hooks/hook-context.d.ts
13
+ type Environment = MarklessEnvironment | ((context: unknown) => MarklessEnvironment);
14
+ //#endregion
12
15
  //#region src/build/chunking.d.ts
13
16
  declare const MARKLESS_BUILD_PREFIX = "build/";
14
17
  declare const MARKLESS_BUNDLE_GRAPH = "build/bundle-graph.json";
@@ -71,7 +74,6 @@ declare function collectHeadLinkInjections(bundle: HeadLinkBundle, options?: {
71
74
  declare function transformTsrxModule(input: TransformTsrxModuleInput): Promise<TransformTsrxModuleResult>;
72
75
  //#endregion
73
76
  //#region src/rolldown.d.ts
74
- type Environment = MarklessEnvironment | ((context: unknown) => MarklessEnvironment);
75
77
  type MarklessRolldownPlugin = Plugin & {
76
78
  api: MarklessRolldownPluginApi;
77
79
  };
package/dist/rolldown.js CHANGED
@@ -1,3 +1,3 @@
1
- import { l as resumeVirtualModuleId, s as prerenderWakeVirtualModuleId, t as MARKLESS_VIRTUAL_PREFIX } from "./source-module-CZFm1uWH.js";
2
- import { a as createBuildMetadata, c as collectHeadLinkInjections, d as outputDefaults, f as convertManifestToBundleGraph, i as marklessServer, l as MARKLESS_BUILD_PREFIX, n as marklessClient, p as createPreloadGraphAdder, r as marklessLib, s as transformTsrxModule, t as createMarklessRolldownPlugin, u as MARKLESS_BUNDLE_GRAPH } from "./rolldown-FRjFod14.js";
1
+ import { l as resumeVirtualModuleId, s as prerenderWakeVirtualModuleId, t as MARKLESS_VIRTUAL_PREFIX } from "./source-module-DI20i0ZG.js";
2
+ import { a as createBuildMetadata, c as createPreloadGraphAdder, d as MARKLESS_BUILD_PREFIX, f as MARKLESS_BUNDLE_GRAPH, i as marklessServer, n as marklessClient, o as collectHeadLinkInjections, p as outputDefaults, r as marklessLib, s as convertManifestToBundleGraph, t as createMarklessRolldownPlugin, u as transformTsrxModule } from "./rolldown-CB2Xpf-E.js";
3
3
  export { MARKLESS_BUILD_PREFIX, MARKLESS_BUNDLE_GRAPH, MARKLESS_VIRTUAL_PREFIX, collectHeadLinkInjections, convertManifestToBundleGraph, createBuildMetadata, createMarklessRolldownPlugin, createPreloadGraphAdder, marklessClient, marklessLib, marklessServer, outputDefaults, prerenderWakeVirtualModuleId, resumeVirtualModuleId, transformTsrxModule };
@@ -479,14 +479,20 @@ function payloadModule(payload) {
479
479
  ""
480
480
  ].join("\n");
481
481
  }
482
+ /** The module-local name a direct-CSR module's root renders under. */
483
+ function csrRootLocalName(rootExportName) {
484
+ return `marklessCsr${rootExportName}`;
485
+ }
482
486
  function emitPublicRenderModule(source, input) {
483
- if (!input.executionLogEnabled || !input.rootExportName) return source;
487
+ if (!input.rootExportName) return source;
484
488
  const declaration = `export function ${input.rootExportName}()`;
485
- if (!source.includes(declaration)) return source;
489
+ if (!source.includes(declaration)) throw new Error(`MARKLESS_CSR_ROOT_DECLARATION_MISSING: ${input.rootExportName}`);
490
+ const localName = csrRootLocalName(input.rootExportName);
491
+ if (!input.executionLogEnabled) return source.replace(declaration, `function ${localName}()`);
486
492
  const implementationName = `marklessRender${input.rootExportName}`;
487
493
  return [
488
494
  source.replace(declaration, `function ${implementationName}()`),
489
- `export function ${input.rootExportName}() {`,
495
+ `function ${localName}() {`,
490
496
  `\tconst output = ${implementationName}();`,
491
497
  " globalThis.__mxLoadLog().then(log => log.logMarklessRenderSummary());",
492
498
  " return output;",
@@ -501,10 +507,13 @@ function emitSourceModule(input) {
501
507
  const symbolsOnly = input.environment === "client" && input.clientOutput === "symbols-only";
502
508
  const routeSymbols = input.environment === "client" && input.symbolRoutes.length > 0;
503
509
  const canonicalRenderData = input.environment === "client" && !symbolsOnly && !input.prerenderRecords && input.canonicalRenderData === true && input.publicRenderRootExportName === null && !!input.publicSsrModuleSource && !!input.renderDataId;
510
+ const emitsSsrBody = !!input.publicSsrModuleSource && !(input.environment === "client" && (symbolsOnly || !input.prerenderRecords && !input.dev));
511
+ const csrRootName = input.environment !== "server" && !symbolsOnly && !input.prerenderRecords && input.publicRenderRootExportName ? csrRootLocalName(input.publicRenderRootExportName) : null;
504
512
  return [
505
- !input.publicSsrModuleSource && !input.publicRenderModuleSource || symbolsOnly || !input.renderDataId || input.environment === "client" && !input.publicRenderModuleSource && !input.prerenderRecords && !input.dev && !canonicalRenderData ? "" : canonicalRenderData ? `import { marklessPrerenderData } from '${input.renderDataId}';` : `import { marklessRenderData } from '${input.renderDataId}';`,
513
+ !input.publicSsrModuleSource && !input.publicRenderModuleSource || symbolsOnly || !input.renderDataId || input.environment === "client" && !input.publicRenderModuleSource && !input.prerenderRecords && !input.dev && !canonicalRenderData ? "" : canonicalRenderData ? emitsSsrBody ? `import { marklessPrerenderData, marklessRenderData } from '${input.renderDataId}';` : `import { marklessPrerenderData } from '${input.renderDataId}';` : `import { marklessRenderData } from '${input.renderDataId}';`,
506
514
  symbolsOnly ? "" : `import { state as payloadState, view as payloadView, runtimeDemandMap as payloadRuntimeDemandMap } from '${input.payloadId}';`,
507
515
  input.environment === "client" && input.directCsr === true && input.hasComputedState === true ? ["import { installMarklessDerivedReconcile } from '@markless/web/fns/reconcile-plane';", "installMarklessDerivedReconcile();"].join("\n") : null,
516
+ input.environment === "client" && input.hasOverlayMarks === true ? emitOverlayLoaderInstall() : null,
508
517
  "",
509
518
  emitLoadSymbol(input),
510
519
  input.behaviorSymbols?.length ? emitDirectSourceSymbolLoader(input.behaviorSymbols, "readMarklessBehaviorSourceSymbol").replace("function loadSymbol(", "function loadBehaviorSymbol(") : "",
@@ -519,8 +528,9 @@ function emitSourceModule(input) {
519
528
  executionLogEnabled: input.executionLog !== "never",
520
529
  rootExportName: input.publicRenderRootExportName
521
530
  }),
522
- input.environment === "client" && (symbolsOnly || !input.prerenderRecords && !input.dev) ? "" : input.publicSsrModuleSource,
531
+ emitsSsrBody ? input.publicSsrModuleSource : "",
523
532
  routeSymbols ? emitLazySymbolRouteFunction(input.symbolRoutes, "marklessSsrLoadSymbolRoute", "marklessLoadLocalSymbol") : "",
533
+ routeSymbols && !symbolsOnly && input.directCsr === true ? emitComposedArmRecordsInstall() : null,
524
534
  symbolsOnly && routeSymbols ? "export { marklessSsrLoadSymbolRoute as loadSymbol };" : "",
525
535
  symbolsOnly ? "" : emitCompiledAppDefault({
526
536
  environment: input.environment,
@@ -532,15 +542,44 @@ function emitSourceModule(input) {
532
542
  resumeModuleUrl: input.resumeModuleUrl,
533
543
  prerenderWakeModuleUrl: input.prerenderWakeModuleUrl,
534
544
  rootExportName: input.publicRenderRootExportName,
545
+ csrRootName,
535
546
  symbolLoaderName: routeSymbols ? "marklessSsrLoadSymbolRoute" : "loadSymbol",
536
547
  ssrExportName: input.publicRenderSsrExportName,
548
+ ssrComponents: input.publicRenderSsrComponentExports ?? [],
537
549
  canonicalRenderData,
538
550
  prerenderRecords: input.prerenderRecords,
539
551
  dev: input.dev
540
552
  }),
553
+ ...emitSharedDefinitionExports(input.sharedDefinitionExports ?? [], input.publicRenderSsrComponentExports ?? []),
541
554
  ""
542
555
  ].filter((line) => line !== null).join("\n");
543
556
  }
557
+ const MARKLESS_SHARED_CALL_UNCOMPILED = "MARKLESS_SHARED_CALL_UNCOMPILED";
558
+ /**
559
+ * One real ES named export per authored shared definition, bound to a callable
560
+ * that only ever throws.
561
+ *
562
+ * The export exists for two reasons, and neither is "run the definition".
563
+ * Linking first: a `.ts` barrel writing `export { nscShared as state } from
564
+ * './family.tsrx'` is resolved before anything runs, and the emitted module
565
+ * replacing the authored one wholesale made that a SyntaxError. Failing closed
566
+ * second: a compiled consumer's `family.state()` is lowered by the compiler and
567
+ * never reaches this binding, so anything that DOES reach it is an uncompiled
568
+ * call site, which cannot be served correctly — a shared instance resolves
569
+ * against the widget the caller renders under, and an uncompiled call has no
570
+ * such caller. It throws by name rather than quietly becoming a second,
571
+ * unratified runtime API.
572
+ */
573
+ function emitSharedDefinitionExports(sharedExports, ssrComponents) {
574
+ if (sharedExports.length === 0) return [];
575
+ const taken = new Set([...RESERVED_MODULE_BINDINGS, ...ssrComponents.map((component) => component.exportName)]);
576
+ return sharedExports.map((name) => {
577
+ if (taken.has(name)) throw new Error(`MARKLESS_SHARED_EXPORT_NAME_RESERVED: ${name}`);
578
+ taken.add(name);
579
+ const message = `${MARKLESS_SHARED_CALL_UNCOMPILED}: ${name} — a shared definition can only be called from a compiled module; this call site was not compiled.`;
580
+ return `export const ${name} = () => { throw new Error(${JSON.stringify(message)}); };`;
581
+ });
582
+ }
544
583
  /**
545
584
  * Whether this app's serialized state defines any computed node. Computed
546
585
  * definitions are what `createRuntimeGraph` turns into derived graph nodes on
@@ -550,20 +589,64 @@ function payloadHasComputed(payloadState) {
550
589
  const computed = payloadState?.computed;
551
590
  return Array.isArray(computed) && computed.length > 0;
552
591
  }
592
+ /**
593
+ * The one line that makes elevation cost nothing until it is used.
594
+ *
595
+ * Writing the `import()` specifier HERE rather than in the runtime is the whole
596
+ * mechanism: a module every app loads cannot name the behaviour without making
597
+ * every app ship its chunk. The handoff is a global rather than a slot module
598
+ * for the same reason - a slot module is bytes every app pays for a capability
599
+ * most of them do not have. Nothing is fetched until the runtime calls it.
600
+ */
601
+ function emitOverlayLoaderInstall() {
602
+ return [
603
+ "globalThis.__marklessOverlay = (root) =>",
604
+ " root.querySelector('[overlay]')",
605
+ " ? import('@markless/web/fns/overlay').then((m) => m.installOverlayBehavior(root))",
606
+ " : undefined;"
607
+ ].join("\n");
608
+ }
609
+ /**
610
+ * The same one line for the row mint, and for the same reason.
611
+ *
612
+ * `resume-keyed-repeats` is loaded by EVERY repeat, so it cannot name the
613
+ * building half: doing so makes an app whose rows only reorder emit the mint
614
+ * chunk anyway. Written here, the specifier exists only in a build the compiler
615
+ * recorded a mintable repeat for; the repeat runtime reads the loader off the
616
+ * global and refuses to build without one.
617
+ */
618
+ function emitRowMintLoaderInstall() {
619
+ return "globalThis.__marklessRowMint = () => import('@markless/web/fns/row-mint');";
620
+ }
621
+ /**
622
+ * True when the compiler folded the mint into a keyed-repeat record - which it
623
+ * does exactly for a record carrying `rowTemplate` or `emptyArm`, the two fields
624
+ * that make a repeat able to build nodes.
625
+ */
626
+ function demandsRowMint(runtimeDemandMap) {
627
+ const records = runtimeDemandMap?.payloadRecords;
628
+ return Array.isArray(records) && records.some((record) => record.kind === "keyed-repeat" && record.runtimeModuleIds?.includes(ROW_MINT_RUNTIME_MODULE_ID) === true);
629
+ }
630
+ const ROW_MINT_RUNTIME_MODULE_ID = "web/fns/row-mint";
631
+ /** True when the compiler recorded an `overlay` mark for this module. */
632
+ function demandsOverlay(runtimeDemandMap) {
633
+ const records = runtimeDemandMap?.payloadRecords;
634
+ return Array.isArray(records) && records.some((record) => record.kind === "overlay");
635
+ }
553
636
  function emitResumeModule(input) {
554
637
  const routeSymbols = input.symbolRoutes.length > 0;
555
638
  const resumeSymbolLoader = routeSymbols ? "marklessSsrLoadSymbolRoute" : "loadSymbol";
556
639
  const scalarSpecializations = scalarDispatcherSpecializations(input);
557
640
  const stagedPrerender = (input.prerenderTriggerGroups?.length ?? 0) > 0;
558
- const streamedResume = stagedPrerender || (input.payloadView?.asyncBoundaries?.length ?? 0) > 0;
559
- const bareResumeContainerEvent = emitResumeContainerEvent(resumeSymbolLoader, input.needsFullResume ?? false, input.payloadState?.version === ASYNC_PROTOCOL_VERSION, input.symbolRoutes.length > 0 ? "none" : leanResumeMode(input.runtimeDemandMap), scalarSpecializations, input.runtimeDemandMap, input.executionLog !== "never", input.prerenderDataId, stagedPrerender);
560
- const resumeContainerEvent = streamedResume ? emitQueuedResumeContainerEvent(bareResumeContainerEvent) : bareResumeContainerEvent;
641
+ const resumeContainerEvent = emitQueuedResumeContainerEvent(emitResumeContainerEvent(resumeSymbolLoader, input.needsFullResume ?? false, input.payloadState?.version === ASYNC_PROTOCOL_VERSION, input.symbolRoutes.length > 0 ? "none" : leanResumeMode(input.runtimeDemandMap), scalarSpecializations, input.runtimeDemandMap, input.executionLog !== "never", input.prerenderDataId, stagedPrerender));
561
642
  return [
562
643
  input.boundSymbolDescriptors ? `export const ${MARKLESS_BOUND_SYMBOLS_EXPORT} = ${emitBoundSymbolDescriptors(input.boundSymbolDescriptors)};` : null,
563
644
  input.prerenderDataId && !stagedPrerender && resumeContainerEvent.includes("marklessPrerenderData") ? `import { marklessPrerenderData } from '${input.prerenderDataId}';` : null,
564
645
  stagedPrerender && input.prerenderDataId ? `async function marklessLoadPrerenderData() { const module = await import('${input.prerenderDataId}'); return module.marklessPrerenderData; }` : null,
565
646
  `import { runtimeDemandMap as payloadRuntimeDemandMap } from '${input.payloadId}';`,
566
647
  payloadHasComputed(input.payloadState) ? ["import { installMarklessDerivedReconcile } from '@markless/web/fns/reconcile-plane';", "installMarklessDerivedReconcile();"].join("\n") : null,
648
+ demandsOverlay(input.runtimeDemandMap) ? emitOverlayLoaderInstall() : null,
649
+ demandsRowMint(input.runtimeDemandMap) ? emitRowMintLoaderInstall() : null,
567
650
  scalarSpecializations.length > 0 ? [
568
651
  "import { marklessDecodeScalarCell, marklessReadScalarCell, marklessScalarSpecializedError } from '@markless/web/fns/scalar-specialized';",
569
652
  "import { marklessWriteScalar } from '@markless/web/fns/write-scalar';",
@@ -578,6 +661,7 @@ function emitResumeModule(input) {
578
661
  routeSymbols ? "const marklessLoadLocalSymbol = loadSymbol;" : "",
579
662
  routeSymbols ? emitLazySymbolRouteFunction(input.symbolRoutes, "marklessSsrLoadSymbolRoute", "marklessLoadLocalSymbol") : "",
580
663
  routeSymbols ? "export { marklessSsrLoadSymbolRoute as loadSymbol };" : "",
664
+ routeSymbols ? emitComposedArmRecordsInstall() : null,
581
665
  stagedPrerender ? emitPrerenderTriggerGroupLoader(input.prerenderTriggerGroups ?? []) : "",
582
666
  resumeContainerEvent,
583
667
  ""
@@ -612,13 +696,17 @@ function emitQueuedResumeContainerEvent(source) {
612
696
  "}"
613
697
  ].join("\n");
614
698
  }
699
+ function emitComposedArmRecordsInstall() {
700
+ return ["import { installMarklessComposedArmRecords } from '@markless/web/fns/instance-scope';", "installMarklessComposedArmRecords();"].join("\n");
701
+ }
615
702
  function emitLazySymbolRouteFunction(routes, functionName, fallbackName = "loadSymbol") {
616
703
  if (routes.length === 0) return "";
704
+ const ordered = [...routes].sort((left, right) => right.prefix.length - left.prefix.length);
617
705
  return [
618
706
  `function ${functionName}(symbolId) {`,
619
- ...routes.flatMap((route) => [
707
+ ...ordered.flatMap((route) => [
620
708
  ` if (symbolId.startsWith(${JSON.stringify(route.prefix)})) {`,
621
- ` return import(${JSON.stringify(symbolRouteImportSource(route.importSource))}).then((mod) => mod.loadSymbol ? mod.loadSymbol(symbolId.slice(${route.prefix.length})) : Promise.reject(new Error(\`Unknown child async symbol \${symbolId}\`)));`,
709
+ "importSource" in route ? ` return import(${JSON.stringify(symbolRouteImportSource(route.importSource))}).then((mod) => mod.loadSymbol ? mod.loadSymbol(symbolId.slice(${route.prefix.length})) : Promise.reject(new Error(\`Unknown child async symbol \${symbolId}\`)));` : ` return ${route.prefix.length > 0 ? functionName : fallbackName}(symbolId.slice(${route.prefix.length}));`,
622
710
  " }"
623
711
  ]),
624
712
  ` return ${fallbackName}(symbolId);`,
@@ -629,14 +717,16 @@ function symbolRouteImportSource(importSource) {
629
717
  return importSource.includes("?") ? `${importSource}&markless-symbols` : `${importSource}?markless-symbols`;
630
718
  }
631
719
  function emitCompiledAppDefault(input) {
632
- const renderCsrEntry = input.rootExportName && input.environment !== "server" && !input.prerenderRecords ? [` renderCsr: ${input.rootExportName},`] : [];
720
+ const renderCsrEntry = input.csrRootName ? [` renderCsr: ${input.csrRootName},`] : [];
633
721
  const symbolLoaderEntry = input.environment !== "server" && input.rootExportName === null ? [`\tloadSymbol: ${input.symbolLoaderName},`] : input.prerenderRecords === true ? [`\tloadSymbol: ${input.symbolLoaderName},`] : [];
634
722
  const renderDataEntry = input.canonicalRenderData ? [" renderData: marklessPrerenderData,"] : [];
635
- const renderSsrEntry = input.ssrExportName && (input.environment !== "client" || input.prerenderRecords || input.dev) ? [
723
+ const servesSsr = !!input.ssrExportName && (input.environment !== "client" || input.prerenderRecords || input.dev);
724
+ const renderSsrEntry = servesSsr ? [
636
725
  " renderSsr(props, marklessRenderContext) {",
637
726
  ` return ${input.ssrExportName}(props, marklessRenderContext);`,
638
727
  " },"
639
728
  ] : [];
729
+ const renderSsrComponentsEntry = servesSsr && input.ssrComponents.length > 0 ? [` renderSsrComponents: { ${input.ssrComponents.map((component) => `${JSON.stringify(component.exportName)}: { renderSsr: ${component.ssrFunctionName} }`).join(", ")} },`] : [];
640
730
  const resumeModuleEntry = input.resumeModuleUrl && input.environment !== "client" ? [` resumeModuleUrl: ${JSON.stringify(input.resumeModuleUrl)},`] : [];
641
731
  const prerenderWakeModuleEntry = input.prerenderWakeModuleUrl && input.environment !== "client" ? [`\tprerenderWakeModuleUrl: ${JSON.stringify(input.prerenderWakeModuleUrl)},`] : [];
642
732
  const inlineResumerEntry = input.inlineResumerSources && input.environment !== "client" ? [`\tinlineResumerSources: ${JSON.stringify(input.inlineResumerSources)},`] : [];
@@ -661,11 +751,57 @@ function emitCompiledAppDefault(input) {
661
751
  ...renderDataEntry,
662
752
  ...symbolLoaderEntry,
663
753
  ...renderSsrEntry,
754
+ ...renderSsrComponentsEntry,
664
755
  ...metadataEntries,
665
756
  "};",
666
- "export default marklessCompiledApp;"
757
+ "export default marklessCompiledApp;",
758
+ ...emitComponentExports({
759
+ ssrComponents: input.ssrComponents,
760
+ rootExportName: input.ssrComponents.find((component) => component.ssrFunctionName === input.ssrExportName)?.exportName,
761
+ servesSsr: !!servesSsr
762
+ })
667
763
  ].join("\n");
668
764
  }
765
+ const RESERVED_MODULE_BINDINGS = new Set([
766
+ "loadBehaviorSymbol",
767
+ "loadSymbol",
768
+ "marklessCompiledApp",
769
+ "marklessLoadLocalSymbol",
770
+ "marklessPrerenderData",
771
+ "marklessRenderData",
772
+ "marklessRenderSsr",
773
+ "marklessSsrLoadSymbolRoute",
774
+ "payloadRuntimeDemandMap",
775
+ "payloadState",
776
+ "payloadView",
777
+ "readMarklessBehaviorSourceSymbol",
778
+ "resumeContainerEvent"
779
+ ]);
780
+ /**
781
+ * One real ES named export per component the module serves, so plain ESM reaches
782
+ * them: a `.ts` barrel writing `export { CheckboxRoot as root } from
783
+ * './checkbox.tsrx'`, and an app writing `import { Gallery } from
784
+ * './Gallery.tsrx'`, are both named reads the bundler resolves at LINK time.
785
+ * Publishing components only on the default export's map answered neither
786
+ * (MISSING_EXPORT at build, "does not provide an export named" in the browser).
787
+ *
788
+ * The root is published merged - the module surface plus its own part's
789
+ * `renderSsr` - because both consumers reach it by that one name: `render(Gallery,
790
+ * { target })` reads the surface (`renderCsr`/`renderData`/`loadSymbol`), while a
791
+ * barrel re-export composed as a member tag reads the part. Every other component
792
+ * is published as exactly the part `marklessSsrComponentPart` hands a composed
793
+ * child, through the same access path, so a named import and a member tag reach
794
+ * one object.
795
+ */
796
+ function emitComponentExports(input) {
797
+ return input.ssrComponents.map((component) => {
798
+ if (RESERVED_MODULE_BINDINGS.has(component.exportName)) throw new Error(`MARKLESS_COMPONENT_EXPORT_NAME_RESERVED: ${component.exportName}`);
799
+ const part = `marklessCompiledApp.renderSsrComponents[${JSON.stringify(component.exportName)}]`;
800
+ const isRoot = component.exportName === input.rootExportName;
801
+ if (input.servesSsr) return `export const ${component.exportName} = ${isRoot ? `{ ...marklessCompiledApp, renderSsr: ${part}.renderSsr }` : part};`;
802
+ return isRoot ? `export const ${component.exportName} = marklessCompiledApp;` : `export const ${component.exportName} = { marklessCsrOnlyPart: ${JSON.stringify(component.exportName)} };`;
803
+ });
804
+ }
669
805
  function emitResumeContainerEvent(loadSymbolName, needsFullResume, storageFreePayload, leanMode, scalarSpecializations, runtimeDemandMap, executionLogEnabled, prerenderDataId, stagedPrerender = false) {
670
806
  const fullResumeHandoff = stagedPrerender && prerenderDataId ? [
671
807
  "async function marklessFullResumeHandoff(handoff) {",
package/dist/vite.js CHANGED
@@ -1,22 +1,34 @@
1
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-CZFm1uWH.js";
3
- import { d as outputDefaults, o as preflightTsrxModuleDiagnostics, p as createPreloadGraphAdder, t as createMarklessRolldownPlugin } from "./rolldown-FRjFod14.js";
2
+ import { _ as executionLogActivationInjection } from "./source-module-DI20i0ZG.js";
3
+ import { c as createPreloadGraphAdder, l as preflightTsrxModuleDiagnostics, p as outputDefaults, t as createMarklessRolldownPlugin } from "./rolldown-CB2Xpf-E.js";
4
4
  import { joinURL, parsePath, withQuery } from "ufo";
5
5
  import { ASYNC_PROTOCOL_VERSION, STORAGE_SLOT_SYMBOL_KEY, serializeRuntimeAsyncSnapshots, serializeRuntimeStateCells, storageAttributeName } from "@markless/serializer";
6
- import { pathToFileURL } from "node:url";
7
6
  import { join, resolve } from "pathe";
7
+ import { isAbsolute as isAbsolute$1 } from "node:path";
8
+ import { pathToFileURL } from "node:url";
8
9
  import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
9
10
  import { tmpdir } from "node:os";
10
11
  import { PROTOCOL_EVENT_ACTION_KIND as PROTOCOL_EVENT_ACTION_KIND$1, protocolEventDispatchesMarkless } from "@markless/serializer/protocol";
11
12
  import "@markless/serializer/resume-record-delta";
12
13
  import { deserializeGraphValueForClient } from "@markless/serializer/decode-client";
13
14
  import { createServerModuleRunner, isRunnableDevEnvironment } from "vite";
14
- import { isAbsolute as isAbsolute$1 } from "node:path";
15
15
  //#region ../web/src/inline/resumer.ts
16
16
  PROTOCOL_EVENT_ACTION_KIND$1.event, PROTOCOL_EVENT_ACTION_KIND$1.externalDelegate;
17
17
  function createInlineResumerSelfWakeSource(resumeModuleUrl) {
18
18
  return `;(${runInlineResumerSelfWake.toString()})(${JSON.stringify(resumeModuleUrl)});`;
19
19
  }
20
+ /**
21
+ * The Escape primer, appended only to a page whose served HTML carries an
22
+ * `overlay` mark.
23
+ *
24
+ * Appended rather than composed into the resumer body for the same reason the
25
+ * self-wake is: the body is serialized whole, so a flag inside it would put the
26
+ * text on every page in the world to serve the few that elevate anything. A page
27
+ * with no mark ships a byte-identical resumer.
28
+ */
29
+ function createInlineResumerOverlayPrimerSource(resumeModuleUrl) {
30
+ return `;(${runInlineResumerOverlayPrimer.toString()})(${JSON.stringify(resumeModuleUrl)},(url) => import(/* @vite-ignore */ url));`;
31
+ }
20
32
  const PRERENDER_INLINE_EVENT_NAMES_TOKEN = "__MARKLESS_PRERENDER_EVENT_NAMES__";
21
33
  function renderPrerenderInlineResumerSource(compiled, eventNames) {
22
34
  return compiled.replace(PRERENDER_INLINE_EVENT_NAMES_TOKEN, JSON.stringify(eventNames));
@@ -114,9 +126,10 @@ function runPrerenderInlineResumer(eventNames, fallbackResumeModuleUrl, loadModu
114
126
  const root = currentScript?.closest("[data-async-container]");
115
127
  const resumeModuleUrl = currentScript?.getAttribute?.("data-markless-resume-module") ?? fallbackResumeModuleUrl;
116
128
  if (!root || !resumeModuleUrl) return;
129
+ let loaded;
117
130
  for (const eventName of eventNames) root.addEventListener(eventName, (event) => {
118
131
  root.__marklessDelegatedDispatch = true;
119
- return loadModule(resumeModuleUrl).then((module) => module.resumeContainerEvent({
132
+ return (loaded ||= loadModule(resumeModuleUrl)).then((module) => module.resumeContainerEvent({
120
133
  root,
121
134
  event,
122
135
  element: event.target,
@@ -146,6 +159,45 @@ function runInlineResumerSelfWake(fallbackResumeModuleUrl) {
146
159
  if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", wake, { once: true });
147
160
  else wake();
148
161
  }
162
+ /**
163
+ * One window-level listen, from first paint, on a page that elevates something.
164
+ *
165
+ * Two facts make it necessary. A page served with an open overlay is on the
166
+ * stack the moment the behaviour installs, but nothing installs until the
167
+ * runtime wakes; and Escape is the one gesture with no route into the page -
168
+ * every other one reaches an authored handler through the container's own
169
+ * listener, while a dismissal is reported by a document listener that does not
170
+ * exist yet. So the first Escape on such a page would be spent waking and
171
+ * dismiss nothing. This listens above the container, leaves the reason where the
172
+ * behaviour's installer takes it, and lets the wake finish the job.
173
+ *
174
+ * Deliberately one-shot, and deliberately not the waker of first resort. The
175
+ * container's own capture listener runs after this one and wakes the runtime for
176
+ * any event the page has a record for; waking here as well would start a second
177
+ * runtime for one gesture. Deciding a task later is what tells the two apart.
178
+ */
179
+ function runInlineResumerOverlayPrimer(fallbackResumeModuleUrl, loadModule) {
180
+ const currentScript = document.currentScript;
181
+ const root = currentScript?.closest("[data-async-container]");
182
+ const resumeModuleUrl = currentScript?.getAttribute?.("data-markless-resume-module") ?? fallbackResumeModuleUrl;
183
+ if (!root || !resumeModuleUrl) return;
184
+ const prime = (event) => {
185
+ removeEventListener("keydown", prime, true);
186
+ removeEventListener("pointerdown", prime, true);
187
+ if (root.__marklessDelegatedDispatch) return;
188
+ if (event.key === "Escape") globalThis.__marklessOverlayPrimedDismissal = "escape";
189
+ setTimeout(() => {
190
+ if (root.__marklessDelegatedDispatch) return;
191
+ root.__marklessDelegatedDispatch = true;
192
+ loadModule(resumeModuleUrl).then((module) => module.resumeContainerEvent({
193
+ root,
194
+ event: 0
195
+ }));
196
+ });
197
+ };
198
+ addEventListener("keydown", prime, true);
199
+ addEventListener("pointerdown", prime, true);
200
+ }
149
201
  //#endregion
150
202
  //#region ../web/src/repeat-runtime.ts
151
203
  var KeyedRepeatRuntimeError = class extends Error {
@@ -396,8 +448,14 @@ function browserEventNames(view) {
396
448
  function renderContainerAttributes(containerId) {
397
449
  return containerId ? ` data-async-container="${escapeAttribute(containerId)}"` : " data-async-container";
398
450
  }
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>`;
451
+ function renderInlineResumerScript(source, nonce, resumeModuleUrl, selfWake, settleModuleUrl, appendSelfWakeSource = true, overlayPrimer = false) {
452
+ const nonceAttribute = nonce ? ` nonce="${escapeAttribute(nonce)}"` : "";
453
+ const resumeModuleAttribute = resumeModuleUrl ? ` data-markless-resume-module="${escapeAttribute(resumeModuleUrl)}"` : "";
454
+ const settleModuleAttribute = settleModuleUrl ? ` data-markless-settle-module="${escapeAttribute(settleModuleUrl)}"` : "";
455
+ const selfWakeAttribute = selfWake ? " data-markless-self-wake" : "";
456
+ const selfWakeSource = selfWake && appendSelfWakeSource ? createInlineResumerSelfWakeSource(resumeModuleUrl) : "";
457
+ const overlayPrimerSource = overlayPrimer ? createInlineResumerOverlayPrimerSource(resumeModuleUrl) : "";
458
+ return `<script data-async-resumer${nonceAttribute}${resumeModuleAttribute}${settleModuleAttribute}${selfWakeAttribute}>${escapeInlineScript(source + selfWakeSource + overlayPrimerSource)}<\/script>`;
401
459
  }
402
460
  function escapeAttribute(value) {
403
461
  return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markless/bundler",
3
- "version": "0.3.3",
3
+ "version": "0.4.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/compiler": "0.3.3",
43
- "@markless/web": "0.3.3",
44
- "@markless/serializer": "0.3.3"
42
+ "@markless/compiler": "0.4.0",
43
+ "@markless/serializer": "0.4.0",
44
+ "@markless/web": "0.4.0"
45
45
  },
46
46
  "devDependencies": {
47
- "@markless/analyzer": "0.3.3"
47
+ "@markless/analyzer": "0.4.0"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "rolldown": "1.0.3",