@markless/bundler 0.3.2 → 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-BCEdLQvG.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-DAsY_7V1.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,9 +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}';`,
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,
507
517
  "",
508
518
  emitLoadSymbol(input),
509
519
  input.behaviorSymbols?.length ? emitDirectSourceSymbolLoader(input.behaviorSymbols, "readMarklessBehaviorSourceSymbol").replace("function loadSymbol(", "function loadBehaviorSymbol(") : "",
@@ -518,8 +528,9 @@ function emitSourceModule(input) {
518
528
  executionLogEnabled: input.executionLog !== "never",
519
529
  rootExportName: input.publicRenderRootExportName
520
530
  }),
521
- input.environment === "client" && (symbolsOnly || !input.prerenderRecords && !input.dev) ? "" : input.publicSsrModuleSource,
531
+ emitsSsrBody ? input.publicSsrModuleSource : "",
522
532
  routeSymbols ? emitLazySymbolRouteFunction(input.symbolRoutes, "marklessSsrLoadSymbolRoute", "marklessLoadLocalSymbol") : "",
533
+ routeSymbols && !symbolsOnly && input.directCsr === true ? emitComposedArmRecordsInstall() : null,
523
534
  symbolsOnly && routeSymbols ? "export { marklessSsrLoadSymbolRoute as loadSymbol };" : "",
524
535
  symbolsOnly ? "" : emitCompiledAppDefault({
525
536
  environment: input.environment,
@@ -531,28 +542,111 @@ function emitSourceModule(input) {
531
542
  resumeModuleUrl: input.resumeModuleUrl,
532
543
  prerenderWakeModuleUrl: input.prerenderWakeModuleUrl,
533
544
  rootExportName: input.publicRenderRootExportName,
545
+ csrRootName,
534
546
  symbolLoaderName: routeSymbols ? "marklessSsrLoadSymbolRoute" : "loadSymbol",
535
547
  ssrExportName: input.publicRenderSsrExportName,
548
+ ssrComponents: input.publicRenderSsrComponentExports ?? [],
536
549
  canonicalRenderData,
537
550
  prerenderRecords: input.prerenderRecords,
538
551
  dev: input.dev
539
552
  }),
553
+ ...emitSharedDefinitionExports(input.sharedDefinitionExports ?? [], input.publicRenderSsrComponentExports ?? []),
540
554
  ""
541
555
  ].filter((line) => line !== null).join("\n");
542
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
+ }
583
+ /**
584
+ * Whether this app's serialized state defines any computed node. Computed
585
+ * definitions are what `createRuntimeGraph` turns into derived graph nodes on
586
+ * resume, so they are exactly the apps derived reconciliation can act on.
587
+ */
588
+ function payloadHasComputed(payloadState) {
589
+ const computed = payloadState?.computed;
590
+ return Array.isArray(computed) && computed.length > 0;
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
+ }
543
636
  function emitResumeModule(input) {
544
637
  const routeSymbols = input.symbolRoutes.length > 0;
545
638
  const resumeSymbolLoader = routeSymbols ? "marklessSsrLoadSymbolRoute" : "loadSymbol";
546
639
  const scalarSpecializations = scalarDispatcherSpecializations(input);
547
640
  const stagedPrerender = (input.prerenderTriggerGroups?.length ?? 0) > 0;
548
- const streamedResume = stagedPrerender || (input.payloadView?.asyncBoundaries?.length ?? 0) > 0;
549
- 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);
550
- 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));
551
642
  return [
552
643
  input.boundSymbolDescriptors ? `export const ${MARKLESS_BOUND_SYMBOLS_EXPORT} = ${emitBoundSymbolDescriptors(input.boundSymbolDescriptors)};` : null,
553
644
  input.prerenderDataId && !stagedPrerender && resumeContainerEvent.includes("marklessPrerenderData") ? `import { marklessPrerenderData } from '${input.prerenderDataId}';` : null,
554
645
  stagedPrerender && input.prerenderDataId ? `async function marklessLoadPrerenderData() { const module = await import('${input.prerenderDataId}'); return module.marklessPrerenderData; }` : null,
555
646
  `import { runtimeDemandMap as payloadRuntimeDemandMap } from '${input.payloadId}';`,
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,
556
650
  scalarSpecializations.length > 0 ? [
557
651
  "import { marklessDecodeScalarCell, marklessReadScalarCell, marklessScalarSpecializedError } from '@markless/web/fns/scalar-specialized';",
558
652
  "import { marklessWriteScalar } from '@markless/web/fns/write-scalar';",
@@ -567,6 +661,7 @@ function emitResumeModule(input) {
567
661
  routeSymbols ? "const marklessLoadLocalSymbol = loadSymbol;" : "",
568
662
  routeSymbols ? emitLazySymbolRouteFunction(input.symbolRoutes, "marklessSsrLoadSymbolRoute", "marklessLoadLocalSymbol") : "",
569
663
  routeSymbols ? "export { marklessSsrLoadSymbolRoute as loadSymbol };" : "",
664
+ routeSymbols ? emitComposedArmRecordsInstall() : null,
570
665
  stagedPrerender ? emitPrerenderTriggerGroupLoader(input.prerenderTriggerGroups ?? []) : "",
571
666
  resumeContainerEvent,
572
667
  ""
@@ -601,13 +696,17 @@ function emitQueuedResumeContainerEvent(source) {
601
696
  "}"
602
697
  ].join("\n");
603
698
  }
699
+ function emitComposedArmRecordsInstall() {
700
+ return ["import { installMarklessComposedArmRecords } from '@markless/web/fns/instance-scope';", "installMarklessComposedArmRecords();"].join("\n");
701
+ }
604
702
  function emitLazySymbolRouteFunction(routes, functionName, fallbackName = "loadSymbol") {
605
703
  if (routes.length === 0) return "";
704
+ const ordered = [...routes].sort((left, right) => right.prefix.length - left.prefix.length);
606
705
  return [
607
706
  `function ${functionName}(symbolId) {`,
608
- ...routes.flatMap((route) => [
707
+ ...ordered.flatMap((route) => [
609
708
  ` if (symbolId.startsWith(${JSON.stringify(route.prefix)})) {`,
610
- ` 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}));`,
611
710
  " }"
612
711
  ]),
613
712
  ` return ${fallbackName}(symbolId);`,
@@ -618,14 +717,16 @@ function symbolRouteImportSource(importSource) {
618
717
  return importSource.includes("?") ? `${importSource}&markless-symbols` : `${importSource}?markless-symbols`;
619
718
  }
620
719
  function emitCompiledAppDefault(input) {
621
- const renderCsrEntry = input.rootExportName && input.environment !== "server" && !input.prerenderRecords ? [` renderCsr: ${input.rootExportName},`] : [];
720
+ const renderCsrEntry = input.csrRootName ? [` renderCsr: ${input.csrRootName},`] : [];
622
721
  const symbolLoaderEntry = input.environment !== "server" && input.rootExportName === null ? [`\tloadSymbol: ${input.symbolLoaderName},`] : input.prerenderRecords === true ? [`\tloadSymbol: ${input.symbolLoaderName},`] : [];
623
722
  const renderDataEntry = input.canonicalRenderData ? [" renderData: marklessPrerenderData,"] : [];
624
- 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 ? [
625
725
  " renderSsr(props, marklessRenderContext) {",
626
726
  ` return ${input.ssrExportName}(props, marklessRenderContext);`,
627
727
  " },"
628
728
  ] : [];
729
+ const renderSsrComponentsEntry = servesSsr && input.ssrComponents.length > 0 ? [` renderSsrComponents: { ${input.ssrComponents.map((component) => `${JSON.stringify(component.exportName)}: { renderSsr: ${component.ssrFunctionName} }`).join(", ")} },`] : [];
629
730
  const resumeModuleEntry = input.resumeModuleUrl && input.environment !== "client" ? [` resumeModuleUrl: ${JSON.stringify(input.resumeModuleUrl)},`] : [];
630
731
  const prerenderWakeModuleEntry = input.prerenderWakeModuleUrl && input.environment !== "client" ? [`\tprerenderWakeModuleUrl: ${JSON.stringify(input.prerenderWakeModuleUrl)},`] : [];
631
732
  const inlineResumerEntry = input.inlineResumerSources && input.environment !== "client" ? [`\tinlineResumerSources: ${JSON.stringify(input.inlineResumerSources)},`] : [];
@@ -650,11 +751,57 @@ function emitCompiledAppDefault(input) {
650
751
  ...renderDataEntry,
651
752
  ...symbolLoaderEntry,
652
753
  ...renderSsrEntry,
754
+ ...renderSsrComponentsEntry,
653
755
  ...metadataEntries,
654
756
  "};",
655
- "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
+ })
656
763
  ].join("\n");
657
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
+ }
658
805
  function emitResumeContainerEvent(loadSymbolName, needsFullResume, storageFreePayload, leanMode, scalarSpecializations, runtimeDemandMap, executionLogEnabled, prerenderDataId, stagedPrerender = false) {
659
806
  const fullResumeHandoff = stagedPrerender && prerenderDataId ? [
660
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-BCEdLQvG.js";
3
- import { d as outputDefaults, o as preflightTsrxModuleDiagnostics, p as createPreloadGraphAdder, t as createMarklessRolldownPlugin } from "./rolldown-DAsY_7V1.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.2",
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.2",
43
- "@markless/web": "0.3.2",
44
- "@markless/serializer": "0.3.2"
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.2"
47
+ "@markless/analyzer": "0.4.0"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "rolldown": "1.0.3",