@reckona/mreact-router 0.0.195 → 0.0.196

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/client.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { readFile, stat } from "node:fs/promises";
3
3
  import { builtinModules } from "node:module";
4
- import { dirname, extname, join, relative, sep } from "node:path";
4
+ import { basename, dirname, extname, isAbsolute, join, relative, sep } from "node:path";
5
5
  import { analyzeBoundaryGraph, collectClientRouteModuleAnalysis, formatDiagnostic, } from "@reckona/mreact-compiler";
6
6
  import { collectClientRouteModuleAnalysisFromContext, createCompilerModuleContext, hasUnguardedBrowserGlobalReference, readTopLevelBooleanExport, readTopLevelBooleanExportFromContext, stripTypeScriptWithOxc, transformCompilerModuleContext, } from "@reckona/mreact-compiler/internal";
7
7
  import { assetPath } from "./assets.js";
@@ -1318,6 +1318,12 @@ export async function buildNavigationRuntimeBundle(options = {}) {
1318
1318
  export async function buildClientRouteOutput(options) {
1319
1319
  const entry = await buildClientRouteEntrySource(options);
1320
1320
  const dropConsoleFunctions = options.dropConsoleFunctions ?? resolveClientConsolePureFunctions(options.dropClientConsole);
1321
+ const sourceRegionModulePaths = options.debugLabels === true ? undefined : new Set([options.filename]);
1322
+ const runtimePlugin = workspaceRuntimePlugin({
1323
+ debugLabels: options.debugLabels === true,
1324
+ routeFiles: [options.filename],
1325
+ sourceRegionModulePaths,
1326
+ });
1321
1327
  const bundled = await bundleRouterModule({
1322
1328
  code: entry.code,
1323
1329
  cacheDir: options.cacheDir,
@@ -1329,7 +1335,8 @@ export async function buildClientRouteOutput(options) {
1329
1335
  minify: options.minify === true,
1330
1336
  platform: "browser",
1331
1337
  preserveExports: true,
1332
- plugins: [workspaceRuntimePlugin({ routeFiles: [options.filename] })],
1338
+ sourceRegionModulePaths,
1339
+ plugins: [runtimePlugin],
1333
1340
  sourceMap: options.sourceMap,
1334
1341
  vitePlugins: options.vitePlugins,
1335
1342
  });
@@ -1350,6 +1357,15 @@ export async function buildClientRouteBatchOutput(options) {
1350
1357
  vitePlugins: options.vitePlugins ?? route.vitePlugins,
1351
1358
  }),
1352
1359
  })));
1360
+ const debugLabels = options.routes.some((route) => route.debugLabels === true);
1361
+ const sourceRegionModulePaths = debugLabels
1362
+ ? undefined
1363
+ : new Set(entries.map((entry) => entry.filename));
1364
+ const runtimePlugin = workspaceRuntimePlugin({
1365
+ debugLabels,
1366
+ routeFiles: entries.map((entry) => entry.filename),
1367
+ sourceRegionModulePaths,
1368
+ });
1353
1369
  const bundled = await bundleRouterModules({
1354
1370
  base: options.assetBaseUrl ?? "/_mreact/client/",
1355
1371
  cacheDir: options.cacheDir,
@@ -1363,7 +1379,8 @@ export async function buildClientRouteBatchOutput(options) {
1363
1379
  })),
1364
1380
  minify: options.minify === true,
1365
1381
  platform: "browser",
1366
- plugins: [workspaceRuntimePlugin({ routeFiles: entries.map((entry) => entry.filename) })],
1382
+ sourceRegionModulePaths,
1383
+ plugins: [runtimePlugin],
1367
1384
  root: options.projectRoot,
1368
1385
  sourceMap: options.sourceMap,
1369
1386
  dropConsoleFunctions: options.dropConsoleFunctions,
@@ -1392,13 +1409,20 @@ export async function buildClientRouteEntrySource(options) {
1392
1409
  filename: options.filename,
1393
1410
  });
1394
1411
  const routeSourceAnalysis = collectClientRouteModuleAnalysisFromContext(moduleContext);
1412
+ const compilerFilename = options.debugLabels === true ? basename(options.filename) : options.filename;
1413
+ const compilerModuleContext = compilerFilename === options.filename
1414
+ ? moduleContext
1415
+ : createCompilerModuleContext({
1416
+ code: options.code,
1417
+ filename: compilerFilename,
1418
+ });
1395
1419
  const compiled = transformCompilerModuleContext({
1396
1420
  code: options.code,
1397
1421
  clientBoundaryImports: options.clientBoundaryImports ?? [],
1398
- filename: options.filename,
1399
- moduleContext,
1422
+ filename: compilerFilename,
1423
+ moduleContext: compilerModuleContext,
1400
1424
  target: "client",
1401
- dev: options.minify !== true,
1425
+ dev: options.debugLabels === true,
1402
1426
  });
1403
1427
  if (compiled.diagnostics.length > 0) {
1404
1428
  throw new Error(compiled.diagnostics
@@ -1416,7 +1440,9 @@ export async function buildClientRouteEntrySource(options) {
1416
1440
  const routeId = routeIdForPath(options.routePath);
1417
1441
  const routeUsesCells = detectRouteCellStateHint(compiled.code);
1418
1442
  const routeUsesReactiveEffect = detectRouteReactiveEffectHint(compiled.code);
1419
- const routeUsesCleanupScope = routeUsesCells || routeUsesReactiveEffect;
1443
+ const routeUsesDomRefs = compiled.metadata.imports.some((entry) => entry.source === "@reckona/mreact-reactive-dom" &&
1444
+ entry.specifiers.includes("bindDomRef"));
1445
+ const routeUsesCleanupScope = routeUsesCells || routeUsesReactiveEffect || routeUsesDomRefs;
1420
1446
  const routeExplicitlyRequiresHydration = isExplicitClientRouteSource(routeSourceAnalysis, options.filename);
1421
1447
  const routeHasEventBindings = (compiled.metadata.eventHydrationManifest?.events.length ?? 0) > 0;
1422
1448
  const routeCapturesEventBindings = compiled.code.includes("__mreactEventBindings");
@@ -1427,6 +1453,7 @@ export async function buildClientRouteEntrySource(options) {
1427
1453
  const routeRequiresFullHydration = routeExplicitlyRequiresHydration ||
1428
1454
  routeUsesCells ||
1429
1455
  routeUsesReactiveEffect ||
1456
+ routeUsesDomRefs ||
1430
1457
  routeHasEventBindings;
1431
1458
  const routeUsesOnlyClientReferenceBoundaries = !routeRequiresFullHydration &&
1432
1459
  clientReferenceManifest.length > 0 &&
@@ -1447,7 +1474,7 @@ export async function buildClientRouteEntrySource(options) {
1447
1474
  ? `import { bindCapturedEvent as __mreactBindCapturedEvent } from "@reckona/mreact-reactive-dom/internal";\n`
1448
1475
  : "";
1449
1476
  const routeReactiveDomMetadataImport = !routeUsesOnlyClientReferenceBoundaries
1450
- ? `${routeCapturedEventImport}import { withEventBindingMetadata as __mreactWithEventBindingMetadata, withPropBindingMetadata as __mreactWithPropBindingMetadata } from "@reckona/mreact-reactive-dom";\n`
1477
+ ? `${routeCapturedEventImport}import { ${routeUsesDomRefs ? "getDomRefBindings as __mreactGetDomRefBindings, " : ""}withEventBindingMetadata as __mreactWithEventBindingMetadata, withPropBindingMetadata as __mreactWithPropBindingMetadata } from "@reckona/mreact-reactive-dom";\n`
1451
1478
  : "";
1452
1479
  const navigationStateDeclaration = inlineClientNavigation
1453
1480
  ? `const __mreactNavigationState = __mreactGlobal.__mreactNavigationState ??= {
@@ -1723,9 +1750,10 @@ __mreactGlobal.__mreactRouteCell = (nativeCell, initial) => {
1723
1750
  __mreactActiveCellIndex = 0;
1724
1751
  }
1725
1752
  return () => {
1726
- for (const __mreactDispose of Array.from(__mreactRouteEffectDisposers)) {
1727
- __mreactDispose();
1728
- }
1753
+ __mreactRunLifecycleTasks(
1754
+ Array.from(__mreactRouteEffectDisposers),
1755
+ (__mreactDispose) => __mreactDispose(),
1756
+ );
1729
1757
  __mreactRouteEffectDisposers.clear();
1730
1758
  };
1731
1759
  });
@@ -1736,13 +1764,33 @@ __mreactGlobal.__mreactRouteCell = (nativeCell, initial) => {
1736
1764
  ? ` __mreactDisposeRoute(__mreactRouteId);
1737
1765
  const __mreactRouteEffectDisposers = new Set();
1738
1766
  __mreactRouteDisposers.set(__mreactRouteId, () => {
1739
- for (const __mreactDispose of Array.from(__mreactRouteEffectDisposers)) {
1740
- __mreactDispose();
1741
- }
1767
+ __mreactRunLifecycleTasks(
1768
+ Array.from(__mreactRouteEffectDisposers),
1769
+ (__mreactDispose) => __mreactDispose(),
1770
+ );
1742
1771
  __mreactRouteEffectDisposers.clear();
1743
1772
  });
1744
1773
  `
1745
1774
  : "";
1775
+ const routeLifecycleFunctions = `
1776
+ function __mreactRunLifecycleTasks(values, run) {
1777
+ let firstError;
1778
+
1779
+ for (const value of values) {
1780
+ try {
1781
+ run(value);
1782
+ } catch (error) {
1783
+ firstError ??= error;
1784
+ }
1785
+ }
1786
+
1787
+ if (firstError !== undefined) {
1788
+ queueMicrotask(() => {
1789
+ throw firstError;
1790
+ });
1791
+ }
1792
+ }
1793
+ `;
1746
1794
  const routeCellDropFunction = routeUsesCells
1747
1795
  ? `
1748
1796
  function __mreactDropMismatchedRouteState(previousState, nextState) {
@@ -1768,12 +1816,18 @@ function __mreactDisposeRoute(routeId) {
1768
1816
  }
1769
1817
  `
1770
1818
  : "";
1771
- const routeCleanupNavigationDispose = routeUsesCleanupScope
1772
- ? ` if (currentRouteId !== nextRouteId) {
1773
- __mreactDisposeRoute(currentRouteId);
1819
+ const routeCleanupNavigationDispose = ` if (currentRouteId !== nextRouteId) {
1820
+ const __mreactRegisteredRouteDisposers = __mreactGlobal.__mreactRouteDisposers;
1821
+ const __mreactRegisteredRouteDispose = __mreactRegisteredRouteDisposers?.get(currentRouteId);
1822
+ if (__mreactRegisteredRouteDispose !== undefined) {
1823
+ __mreactRegisteredRouteDisposers.delete(currentRouteId);
1824
+ __mreactRunLifecycleTasks(
1825
+ [__mreactRegisteredRouteDispose],
1826
+ (__mreactDispose) => __mreactDispose(),
1827
+ );
1828
+ }
1774
1829
  }
1775
- `
1776
- : "";
1830
+ `;
1777
1831
  const routeNodeResolver = routeUsesCells
1778
1832
  ? `
1779
1833
  function __mreactResolveRouteNode(value) {
@@ -1812,9 +1866,7 @@ function __mreactResolveRouteNode(value) {
1812
1866
  const previousDisposers = current.__mreactEventDisposers;
1813
1867
 
1814
1868
  if (Array.isArray(previousDisposers)) {
1815
- for (const dispose of previousDisposers) {
1816
- dispose();
1817
- }
1869
+ __mreactRunLifecycleTasks(previousDisposers, (dispose) => dispose());
1818
1870
  }
1819
1871
 
1820
1872
  const rawBindings = next.__mreactEventBindings;
@@ -1843,14 +1895,26 @@ function __mreactResolveRouteNode(value) {
1843
1895
  const previousDisposers = current.__mreactEventDisposers;
1844
1896
 
1845
1897
  if (Array.isArray(previousDisposers)) {
1846
- for (const dispose of previousDisposers) {
1847
- dispose();
1848
- }
1898
+ __mreactRunLifecycleTasks(previousDisposers, (dispose) => dispose());
1849
1899
  }
1850
1900
 
1851
1901
  current.__mreactEventDisposers = [];
1852
1902
  current.__mreactHasEvents = false;
1853
1903
  }
1904
+ `;
1905
+ const routeDomRefBindingSyncFunction = routeUsesDomRefs
1906
+ ? `function __mreactSyncDomRefBindings(current, next) {
1907
+ __mreactRunLifecycleTasks(
1908
+ Array.from(__mreactGetDomRefBindings(current)),
1909
+ (binding) => binding.dispose(),
1910
+ );
1911
+ __mreactRunLifecycleTasks(
1912
+ Array.from(__mreactGetDomRefBindings(next)),
1913
+ (binding) => binding.retarget(current),
1914
+ );
1915
+ }
1916
+ `
1917
+ : `function __mreactSyncDomRefBindings() {}
1854
1918
  `;
1855
1919
  const boundaryOnlyHydrationBlock = routeRequiresFullHydration
1856
1920
  ? ""
@@ -1909,6 +1973,7 @@ ${routeCellHydrationIndent}__mreactMarker.setAttribute(__mreactRouteHydratedAttr
1909
1973
  ${routeCellHydrationIndent}__mreactMarkRouteHydrated();
1910
1974
  ${routeCellHydrationEnd}}
1911
1975
  ${routeCellDropFunction}
1976
+ ${routeLifecycleFunctions}
1912
1977
  ${routeCleanupFunction}
1913
1978
 
1914
1979
  function __mreactMarkRouteHydrated() {
@@ -2495,13 +2560,14 @@ function __mreactApplyNavigationHtml(html, url) {
2495
2560
  const currentRouteId = currentMarker.getAttribute("${routeHydrationContract.routeMarkerAttribute}");
2496
2561
  const nextRouteId = nextMarker.getAttribute("${routeHydrationContract.routeMarkerAttribute}");
2497
2562
 
2563
+ ${routeCleanupNavigationDispose}
2498
2564
  __mreactMarkRouteHydrating();
2499
2565
  __mreactSyncHeadMetadata(template.content, html);
2500
2566
  if (!__mreactApplyNavigationShellHtml(currentMarker, nextMarker)) {
2501
2567
  __mreactUnmountCompatBoundaries(currentMarker);
2502
2568
  __mreactResumeNode(currentMarker, nextMarker);
2503
2569
  }
2504
- ${routeCleanupNavigationDispose} __mreactSyncRouteDataScripts(template.content, currentRouteId, nextRouteId);
2570
+ __mreactSyncRouteDataScripts(template.content, currentRouteId, nextRouteId);
2505
2571
 
2506
2572
  const script = template.content.querySelector('script[type="module"][src]')?.getAttribute("src");
2507
2573
  if (script !== null && script !== undefined) {
@@ -3658,6 +3724,7 @@ function __mreactResumeNode(current, next) {
3658
3724
  }
3659
3725
 
3660
3726
  __mreactSyncEventBindings(current, next);
3727
+ __mreactSyncDomRefBindings(current, next);
3661
3728
  __mreactSyncAttributes(current, next);
3662
3729
  __mreactSyncPropBindings(current, next);
3663
3730
  __mreactResumeChildren(current, next);
@@ -3680,6 +3747,7 @@ function __mreactShouldReplaceNode(current, next) {
3680
3747
  }
3681
3748
 
3682
3749
  ${routeEventBindingSyncFunction}
3750
+ ${routeDomRefBindingSyncFunction}
3683
3751
 
3684
3752
  function __mreactSyncAttributes(current, next) {
3685
3753
  for (const attribute of Array.from(current.attributes)) {
@@ -3699,9 +3767,7 @@ function __mreactSyncPropBindings(current, next) {
3699
3767
  const previousBindings = current.__mreactPropBindings;
3700
3768
 
3701
3769
  if (Array.isArray(previousBindings)) {
3702
- for (const binding of previousBindings) {
3703
- binding.dispose?.();
3704
- }
3770
+ __mreactRunLifecycleTasks(previousBindings, (binding) => binding.dispose?.());
3705
3771
  }
3706
3772
 
3707
3773
  const bindings = next.__mreactPropBindings;
@@ -3717,9 +3783,7 @@ function __mreactSyncPropBindings(current, next) {
3717
3783
  next.__mreactPropBindings = [];
3718
3784
  next.__mreactHasReactiveProps = false;
3719
3785
 
3720
- for (const binding of bindings) {
3721
- binding.retarget?.(current);
3722
- }
3786
+ __mreactRunLifecycleTasks(bindings, (binding) => binding.retarget?.(current));
3723
3787
  }
3724
3788
 
3725
3789
  function __mreactResumeChildren(current, next) {
@@ -3885,22 +3949,33 @@ export function invalidateReactiveDevtoolsCache() {}
3885
3949
  export function prepareReactiveEffectRunDevtoolsEvent() { return undefined; }`,
3886
3950
  loader: "ts",
3887
3951
  }));
3952
+ if (options.sourceRegionModulePaths !== undefined) {
3953
+ buildApi.onLoad({ filter: /.*/ }, (args) => {
3954
+ if (isAbsolute(args.path) &&
3955
+ isRouteClientDependencySourcePath(args.path, routeFiles) &&
3956
+ !runtimePackageDirs.some((runtimePackageDir) => args.path.startsWith(`${runtimePackageDir}${sep}`))) {
3957
+ options.sourceRegionModulePaths?.add(args.path);
3958
+ }
3959
+ return undefined;
3960
+ });
3961
+ }
3888
3962
  buildApi.onLoad({ filter: /\.(?:mreact\.)?[cm]?[jt]sx$/ }, async (args) => {
3889
3963
  if (!isRouteClientDependencySourcePath(args.path, routeFiles)) {
3890
3964
  return undefined;
3891
3965
  }
3892
3966
  const source = await readFile(args.path, "utf8");
3967
+ const compilerFilename = options.debugLabels ? basename(args.path) : args.path;
3893
3968
  const moduleContext = createCompilerModuleContext({
3894
3969
  code: source,
3895
- filename: args.path,
3970
+ filename: compilerFilename,
3896
3971
  });
3897
3972
  if (!hasJsxSyntax(moduleContext.program)) {
3898
3973
  return undefined;
3899
3974
  }
3900
3975
  const output = transformCompilerModuleContext({
3901
3976
  code: source,
3902
- dev: true,
3903
- filename: args.path,
3977
+ dev: options.debugLabels,
3978
+ filename: compilerFilename,
3904
3979
  mode: isCompatSourcePath(args.path) ? "compat" : "reactive",
3905
3980
  moduleContext,
3906
3981
  target: "client",