@absolutejs/absolute 0.20.0-beta.170 → 0.20.0-beta.171

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.
Files changed (142) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +33 -4
  3. package/changelog.json +112 -0
  4. package/dist/angular/browser.js +2 -2
  5. package/dist/angular/browser.js.map +3 -3
  6. package/dist/angular/components/constants.js +1 -0
  7. package/dist/angular/components/core/streamingSlotRegistrar.js +2 -2
  8. package/dist/angular/components/core/streamingSlotRegistry.js +3 -3
  9. package/dist/angular/index.js +184 -29
  10. package/dist/angular/index.js.map +15 -14
  11. package/dist/angular/server.js +184 -29
  12. package/dist/angular/server.js.map +15 -14
  13. package/dist/build.js +1241 -853
  14. package/dist/build.js.map +32 -30
  15. package/dist/cli/{add-6a7pjkn9.js → add-ae6mg79m.js} +10 -10
  16. package/dist/cli/{analyze-606fshwa.js → analyze-e57zz9x8.js} +3 -3
  17. package/dist/cli/{androidEmulatorController-68jjdxmx.js → androidEmulatorController-vqc5m2rp.js} +6 -6
  18. package/dist/cli/{androidNativeWatcher-x032x9yr.js → androidNativeWatcher-s03aczxv.js} +6 -6
  19. package/dist/cli/{api-mshjr4ah.js → api-8ppxw487.js} +8 -8
  20. package/dist/cli/{build-g81xqbb9.js → build-fz7qbdn3.js} +3 -3
  21. package/dist/cli/{buildDepVendor-29d8v9cx.js → buildDepVendor-qvsh6dsj.js} +7 -3
  22. package/dist/cli/{compile-9y1w9zpj.js → compile-ndc36zna.js} +10 -7
  23. package/dist/cli/config/server.js +36 -6
  24. package/dist/cli/{db-5b4s1kvx.js → db-qbzjw3en.js} +2 -2
  25. package/dist/cli/{dev-hyeb89hp.js → dev-x49583aj.js} +43 -15
  26. package/dist/cli/{doctor-h55y9hj1.js → doctor-repprx92.js} +42 -12
  27. package/dist/cli/{emulatorDoctor-p3yt7140.js → emulatorDoctor-s1z5tkp7.js} +5 -5
  28. package/dist/cli/{emulatorInstaller-4pm2kx9w.js → emulatorInstaller-pgkdswvq.js} +6 -6
  29. package/dist/cli/{env-snb0597q.js → env-5nz1qm8e.js} +3 -3
  30. package/dist/cli/{expoDevController-0vj8dvkh.js → expoDevController-018anvbm.js} +6 -6
  31. package/dist/cli/{generate-20x1pkf7.js → generate-brvjesd6.js} +8 -8
  32. package/dist/cli/{heapDiff-wqj8vrq8.js → heapDiff-kncvdtn8.js} +3 -3
  33. package/dist/cli/htmx/htmx.min.js +1 -1
  34. package/dist/cli/{htmx-6mz2h7k1.js → htmx-5twhjz7p.js} +7 -7
  35. package/dist/cli/{index-w09fzs9f.js → index-0z5fgb80.js} +1 -1
  36. package/dist/cli/{index-exfkpjja.js → index-125cfzvn.js} +2 -2
  37. package/dist/cli/{index-32ge2w1f.js → index-25e7k849.js} +16 -8
  38. package/dist/cli/{index-ys2rzqhg.js → index-2shf4aya.js} +1 -1
  39. package/dist/cli/{index-74m7kh8c.js → index-3ewt17wm.js} +1 -1
  40. package/dist/cli/{index-zd3cna19.js → index-3sfcmb49.js} +1 -1
  41. package/dist/cli/{index-14fbvz34.js → index-50ag35xg.js} +2 -2
  42. package/dist/cli/{index-h5qx5jpk.js → index-7854xf0m.js} +1 -1
  43. package/dist/cli/{index-3y57kd7x.js → index-8khbn8qr.js} +3 -3
  44. package/dist/cli/{index-xw1byvzp.js → index-9djrap79.js} +1 -1
  45. package/dist/cli/{index-ppphkyps.js → index-a6xx0p9e.js} +1 -1
  46. package/dist/cli/{index-g4ehq6ev.js → index-aemhvvwb.js} +1 -1
  47. package/dist/cli/{index-z6f43b20.js → index-b1wht4q5.js} +1 -1
  48. package/dist/cli/{index-s3xvccq3.js → index-btp4fyf1.js} +2 -2
  49. package/dist/cli/{index-066qhc6n.js → index-cgmp9d02.js} +1 -1
  50. package/dist/cli/{index-3wz3mktm.js → index-gyjxrnnz.js} +1 -1
  51. package/dist/cli/index-jwfambwe.js +13 -0
  52. package/dist/cli/{index-3wm8p7rg.js → index-n6faa3m3.js} +1 -1
  53. package/dist/cli/{index-kye08pjx.js → index-pr1skxen.js} +1 -1
  54. package/dist/cli/{index-t8cf0zj9.js → index-prza6tkp.js} +3 -3
  55. package/dist/cli/{index-qbrzvmh9.js → index-q3snnhyd.js} +1 -1
  56. package/dist/cli/{index-3vmc30gf.js → index-rz56x932.js} +1 -1
  57. package/dist/cli/{index-9sksd8c8.js → index-vh68jb0e.js} +2 -2
  58. package/dist/cli/index.js +32 -32
  59. package/dist/cli/{info-mn125212.js → info-k124sh7g.js} +4 -4
  60. package/dist/cli/{inspect-c7s117j0.js → inspect-n6pcfvfw.js} +5 -5
  61. package/dist/cli/{iosNativeWatcher-0nm6qad5.js → iosNativeWatcher-wdq2fvj1.js} +7 -7
  62. package/dist/cli/{iosSimulatorController-hza2c3rv.js → iosSimulatorController-n97dpa8p.js} +7 -7
  63. package/dist/cli/{islands-dc54e5y1.js → islands-p4vgx7a2.js} +3 -3
  64. package/dist/cli/{logs-ej1add2b.js → logs-shg23fbt.js} +3 -3
  65. package/dist/cli/{ls-b6zz24nt.js → ls-0hfx323m.js} +3 -3
  66. package/dist/cli/{mem-5q7n3ymb.js → mem-16sy1707.js} +5 -5
  67. package/dist/cli/{migrate-6nm4kdf6.js → migrate-k91k050s.js} +2 -2
  68. package/dist/cli/{mobile-ajnnvqtv.js → mobile-nqdkk3gd.js} +13 -13
  69. package/dist/cli/prerender-010z771p.js +10 -0
  70. package/dist/cli/{prettier-phdtvqef.js → prettier-dh5fh786.js} +1 -1
  71. package/dist/cli/{ps-ezmzyscn.js → ps-pk7ncjac.js} +6 -6
  72. package/dist/cli/{psTui-3x3ykjve.js → psTui-50wf0b72.js} +7 -7
  73. package/dist/cli/{remoteMacProtocol-g09myqt5.js → remoteMacProtocol-g10s408w.js} +7 -7
  74. package/dist/cli/{remove-t8x1mrxg.js → remove-abaf0w6k.js} +8 -8
  75. package/dist/cli/{routes-3xj256gx.js → routes-mc3ymp8t.js} +3 -3
  76. package/dist/cli/{start-d6p831ny.js → start-t3cq57wn.js} +5 -5
  77. package/dist/cli/{typecheck-stbt0qt5.js → typecheck-209bemxm.js} +17 -4
  78. package/dist/cli/{workspace-61hh6jq1.js → workspace-7sbyyc59.js} +5 -5
  79. package/dist/client/chunks/islands-s2ta5ajb.js.map +1 -1
  80. package/dist/client/navigation.js +2 -1
  81. package/dist/client/navigation.js.map +3 -3
  82. package/dist/dev/client/domTracker.ts +52 -19
  83. package/dist/dev/client/frameworkDetect.ts +11 -0
  84. package/dist/dev/client/handlers/html.ts +6 -1
  85. package/dist/dev/client/handlers/htmx.ts +6 -1
  86. package/dist/dev/client/hmrClient.ts +10 -0
  87. package/dist/dev/serverBootstrap.js +112 -6
  88. package/dist/index.js +1147 -757
  89. package/dist/index.js.map +32 -30
  90. package/dist/islands/index.js +16 -5
  91. package/dist/islands/index.js.map +6 -6
  92. package/dist/mobile/index.js +2 -2
  93. package/dist/mobile/index.js.map +3 -3
  94. package/dist/react/components/index.js +2 -2
  95. package/dist/react/components/index.js.map +3 -3
  96. package/dist/react/index.js +85 -18
  97. package/dist/react/index.js.map +11 -11
  98. package/dist/react/server.js +71 -15
  99. package/dist/react/server.js.map +8 -8
  100. package/dist/src/angular/baseHref.d.ts +2 -0
  101. package/dist/src/angular/components/constants.d.ts +1 -0
  102. package/dist/src/angular/ssrRender.d.ts +1 -1
  103. package/dist/src/build/compileAngular.d.ts +15 -0
  104. package/dist/src/build/resolveBuildMode.d.ts +6 -0
  105. package/dist/src/build/scanAngularHandlerCalls.d.ts +3 -1
  106. package/dist/src/build/verifyAngularCoreUniqueness.d.ts +3 -1
  107. package/dist/src/cli/htmx/install.d.ts +1 -1
  108. package/dist/src/cli/typeGraphCoherence.d.ts +9 -2
  109. package/dist/src/constants.d.ts +1 -0
  110. package/dist/src/core/appErrorHooks.d.ts +8 -0
  111. package/dist/src/core/islandPageContext.d.ts +5 -1
  112. package/dist/src/core/requestContext.d.ts +5 -0
  113. package/dist/src/core/serverOnlyStaticFiles.d.ts +7 -0
  114. package/dist/src/dev/editorTempFiles.d.ts +3 -0
  115. package/dist/src/dev/hot/analyze.d.ts +1 -1
  116. package/dist/src/dev/hot/runtime.d.ts +20 -0
  117. package/dist/src/dev/importCost/instrument.d.ts +1 -1
  118. package/dist/src/dev/rebuildTrigger.d.ts +1 -0
  119. package/dist/src/utils/cleanStaleOutputs.d.ts +4 -1
  120. package/dist/src/utils/logger.d.ts +3 -2
  121. package/dist/src/utils/resolveConvention.d.ts +1 -0
  122. package/dist/src/utils/ssrErrorPage.d.ts +3 -0
  123. package/dist/svelte/index.js +85 -18
  124. package/dist/svelte/index.js.map +11 -11
  125. package/dist/svelte/router/Route.svelte.d.ts +1 -2
  126. package/dist/svelte/router/Router.svelte +2 -4
  127. package/dist/svelte/server.js +73 -16
  128. package/dist/svelte/server.js.map +9 -9
  129. package/dist/types/globals.d.ts +3 -0
  130. package/dist/types/svelteRouter.ts +3 -12
  131. package/dist/vue/components/Image.js +2 -2
  132. package/dist/vue/components/Image.js.map +3 -3
  133. package/dist/vue/components/index.js +2 -2
  134. package/dist/vue/components/index.js.map +3 -3
  135. package/dist/vue/index.js +158 -91
  136. package/dist/vue/index.js.map +12 -12
  137. package/dist/vue/server.js +144 -88
  138. package/dist/vue/server.js.map +9 -9
  139. package/package.json +1 -1
  140. package/dist/cli/prerender-fqpp0m1j.js +0 -10
  141. /package/dist/cli/{index-vtp20gwf.js → index-2bxfhs32.js} +0 -0
  142. /package/dist/cli/{index-fgz435qn.js → index-t8jmj8w1.js} +0 -0
@@ -1,13 +1,58 @@
1
1
  import type {} from '../../types/globals';
2
2
  /* Snapshot/restore for JS-modified DOM state across HMR updates.
3
3
  * Before patching, captures text and dynamic children of elements with IDs.
4
- * After patching, restores values that were changed by user scripts. */
4
+ * After patching, restores values that were changed by user scripts.
5
+ *
6
+ * "Changed by user scripts" means the live value differs from what the
7
+ * server last sent for that element (the baseline), not from what it sends
8
+ * now. Comparing against the new markup alone would treat every edited
9
+ * text as a script change and put the old text back. The baseline is the
10
+ * page as parsed when the HMR client starts (captureServerDOM), then each
11
+ * update's markup. */
5
12
 
6
13
  type DOMSnapshot = {
7
14
  children: Map<string, string>;
8
15
  text: Map<string, string>;
9
16
  };
10
17
 
18
+ let serverBaseline: DOMSnapshot | null = null;
19
+
20
+ const collectIdState = (root: ParentNode): DOMSnapshot => {
21
+ const text = new Map<string, string>();
22
+ const children = new Map<string, string>();
23
+
24
+ root.querySelectorAll('[id]').forEach((elem) => {
25
+ const { childNodes } = elem;
26
+ const isTextLeaf = Array.from(childNodes).every(
27
+ (child) => child.nodeType === Node.TEXT_NODE
28
+ );
29
+
30
+ if (isTextLeaf && childNodes.length > 0) {
31
+ text.set(elem.id, elem.textContent || '');
32
+ } else if (elem.children.length > 0) {
33
+ children.set(elem.id, elem.innerHTML);
34
+ }
35
+ });
36
+
37
+ return { children, text };
38
+ };
39
+
40
+ /* Record the server-rendered DOM as the baseline. Call once, when the HMR
41
+ * client starts: it runs at the end of <body>, before most page scripts
42
+ * have changed anything. */
43
+ export const captureServerDOM = (root: ParentNode | null | undefined) => {
44
+ if (!root || serverBaseline) return;
45
+ serverBaseline = collectIdState(root);
46
+ };
47
+
48
+ /* True when the live value still matches what the server last sent, i.e.
49
+ * no script touched it: the new server value wins. */
50
+ const isUntouched = (
51
+ baseline: Map<string, string> | undefined,
52
+ elId: string,
53
+ liveValue: string
54
+ ) => baseline?.has(elId) === true && baseline.get(elId) === liveValue;
55
+
11
56
  export const restoreDOMChanges = (
12
57
  root: HTMLElement,
13
58
  snapshot: DOMSnapshot,
@@ -15,12 +60,14 @@ export const restoreDOMChanges = (
15
60
  ) => {
16
61
  const tempDiv = document.createElement('div');
17
62
  tempDiv.innerHTML = newHTML;
63
+ const baseline = serverBaseline;
18
64
 
19
65
  /* Restore JS-modified text on leaf elements */
20
66
  snapshot.text.forEach((liveText, elId) => {
21
67
  const newEl = tempDiv.querySelector(`#${CSS.escape(elId)}`);
22
68
  const newText = newEl ? newEl.textContent || '' : '';
23
69
  if (liveText === newText) return;
70
+ if (isUntouched(baseline?.text, elId, liveText)) return;
24
71
 
25
72
  const liveEl = root.querySelector(`#${CSS.escape(elId)}`);
26
73
  if (liveEl) {
@@ -33,29 +80,15 @@ export const restoreDOMChanges = (
33
80
  const newEl = tempDiv.querySelector(`#${CSS.escape(elId)}`);
34
81
  const newInner = newEl ? newEl.innerHTML : '';
35
82
  if (liveHTML === newInner || liveHTML.length <= newInner.length) return;
83
+ if (isUntouched(baseline?.children, elId, liveHTML)) return;
36
84
 
37
85
  const liveEl = root.querySelector(`#${CSS.escape(elId)}`);
38
86
  if (liveEl) {
39
87
  liveEl.innerHTML = liveHTML;
40
88
  }
41
89
  });
42
- };
43
- export const snapshotDOMChanges = (root: HTMLElement): DOMSnapshot => {
44
- const text = new Map<string, string>();
45
- const children = new Map<string, string>();
46
-
47
- root.querySelectorAll('[id]').forEach((elem) => {
48
- const { childNodes } = elem;
49
- const isTextLeaf = Array.from(childNodes).every(
50
- (child) => child.nodeType === Node.TEXT_NODE
51
- );
52
-
53
- if (isTextLeaf && childNodes.length > 0) {
54
- text.set(elem.id, elem.textContent || '');
55
- } else if (elem.children.length > 0) {
56
- children.set(elem.id, elem.innerHTML);
57
- }
58
- });
59
90
 
60
- return { children, text };
91
+ // The markup just applied is what the server sent last.
92
+ serverBaseline = collectIdState(tempDiv);
61
93
  };
94
+ export const snapshotDOMChanges = (root: HTMLElement) => collectIdState(root);
@@ -61,3 +61,14 @@ export const getComponentNameFromPath = (filePath: string) => {
61
61
  .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
62
62
  .join('');
63
63
  };
64
+ /* Static (HTML/HTMX) pages are told which built page they are (see
65
+ * injectHMRIntoHTMLFile in build.ts). Every tab receives every page's
66
+ * html-update / htmx-update, so a tab applies only its own page's. A
67
+ * document without a page name (older builds) applies all of them. */
68
+ export const isUpdateForThisPage = (sourceFile: unknown) => {
69
+ const page = window.__HMR_PAGE__;
70
+ if (!page || typeof sourceFile !== 'string') return true;
71
+ const normalized = sourceFile.replace(/\\/g, '/');
72
+
73
+ return normalized.slice(normalized.lastIndexOf('/') + 1) === page;
74
+ };
@@ -14,7 +14,10 @@ import {
14
14
  } from '../domState';
15
15
  import { processCSSLinks, waitForCSSAndUpdate } from '../cssUtils';
16
16
  import { patchHeadInPlace } from '../headPatch';
17
- import { detectCurrentFramework } from '../frameworkDetect';
17
+ import {
18
+ detectCurrentFramework,
19
+ isUpdateForThisPage
20
+ } from '../frameworkDetect';
18
21
  import type { ScriptInfo } from '../../../types/client';
19
22
  import { restoreDOMChanges, snapshotDOMChanges } from '../domTracker';
20
23
  import { hmrState } from '../hmrState';
@@ -86,6 +89,7 @@ export const handleHTMLUpdate = (message: {
86
89
  data: {
87
90
  html?: string | { body?: string; head?: string } | null;
88
91
  serverDuration?: number;
92
+ sourceFile?: string;
89
93
  };
90
94
  timestamp?: number;
91
95
  }) => {
@@ -93,6 +97,7 @@ export const handleHTMLUpdate = (message: {
93
97
  if (htmlFrameworkCheck !== 'html') {
94
98
  return;
95
99
  }
100
+ if (!isUpdateForThisPage(message.data.sourceFile)) return;
96
101
 
97
102
  if (window.__REACT_ROOT__) {
98
103
  window.__REACT_ROOT__ = undefined;
@@ -14,7 +14,10 @@ import {
14
14
  } from '../domState';
15
15
  import { processCSSLinks, waitForCSSAndUpdate } from '../cssUtils';
16
16
  import { patchHeadInPlace } from '../headPatch';
17
- import { detectCurrentFramework } from '../frameworkDetect';
17
+ import {
18
+ detectCurrentFramework,
19
+ isUpdateForThisPage
20
+ } from '../frameworkDetect';
18
21
  import { type HTMXSavedState, type ScriptInfo } from '../../../types/client';
19
22
  import { hmrState } from '../hmrState';
20
23
  import { sendAbsoluteHmrTiming } from '../hmrTiming';
@@ -72,11 +75,13 @@ export const handleHTMXUpdate = (message: {
72
75
  data: {
73
76
  html?: string | { body?: string; head?: string } | null;
74
77
  serverDuration?: number;
78
+ sourceFile?: string;
75
79
  };
76
80
  timestamp?: number;
77
81
  }) => {
78
82
  const htmxFrameworkCheck = detectCurrentFramework();
79
83
  if (htmxFrameworkCheck !== 'htmx') return;
84
+ if (!isUpdateForThisPage(message.data.sourceFile)) return;
80
85
 
81
86
  if (window.__REACT_ROOT__) {
82
87
  window.__REACT_ROOT__ = undefined;
@@ -15,6 +15,7 @@ import {
15
15
  type ServerSnapshot
16
16
  } from './reconnect';
17
17
  import { detectCurrentFramework } from './frameworkDetect';
18
+ import { captureServerDOM } from './domTracker';
18
19
  import {
19
20
  absoluteHmrClientTarget,
20
21
  restoreAbsoluteHmrApply,
@@ -159,6 +160,15 @@ if (typeof window !== 'undefined') {
159
160
  if (!window.__HMR_SERVER_VERSIONS__) {
160
161
  window.__HMR_SERVER_VERSIONS__ = {};
161
162
  }
163
+ // Static pages: remember the server-rendered text of elements with an
164
+ // id, so an HMR update can tell script changes from edited markup.
165
+ const framework = window.__HMR_FRAMEWORK__;
166
+ if (
167
+ (framework === 'html' || framework === 'htmx') &&
168
+ typeof document !== 'undefined'
169
+ ) {
170
+ captureServerDOM(document.body);
171
+ }
162
172
  }
163
173
 
164
174
  // Catch uncaught runtime errors and show the error overlay
@@ -739,7 +739,7 @@ var init_workspacePackages = __esm(() => {
739
739
 
740
740
  // src/dev/hot/analyze.ts
741
741
  import { dirname, extname } from "path";
742
- var ANALYZER_VERSION = 1, hashText = (text) => Bun.hash(text).toString(36), scriptKindFor = (tsc, path) => {
742
+ var ANALYZER_VERSION = 2, hashText = (text) => Bun.hash(text).toString(36), scriptKindFor = (tsc, path) => {
743
743
  switch (extname(path)) {
744
744
  case ".tsx":
745
745
  return tsc.ScriptKind.TSX;
@@ -997,7 +997,23 @@ var ANALYZER_VERSION = 1, hashText = (text) => Bun.hash(text).toString(36), scri
997
997
  while (current && (tsc.isParenthesizedExpression(current) || tsc.isAsExpression(current) || tsc.isSatisfiesExpression(current)))
998
998
  current = current.expression;
999
999
  return current !== undefined && (tsc.isArrowFunction(current) || tsc.isFunctionExpression(current));
1000
- }, IDENTIFIER, rejection = (reason) => ({
1000
+ }, IDENTIFIER, STATEFUL_FACTORIES, STATIC_IDENTIFIERS, isStaticExpression = (tsc, node) => {
1001
+ if (tsc.isParenthesizedExpression(node) || tsc.isAsExpression(node) || tsc.isSatisfiesExpression(node) || tsc.isSpreadElement(node) || tsc.isSpreadAssignment(node))
1002
+ return isStaticExpression(tsc, node.expression);
1003
+ if (tsc.isStringLiteral(node) || tsc.isNumericLiteral(node) || tsc.isBigIntLiteral(node) || tsc.isNoSubstitutionTemplateLiteral(node) || tsc.isRegularExpressionLiteral(node) || node.kind === tsc.SyntaxKind.TrueKeyword || node.kind === tsc.SyntaxKind.FalseKeyword || node.kind === tsc.SyntaxKind.NullKeyword || tsc.isOmittedExpression(node))
1004
+ return true;
1005
+ if (tsc.isIdentifier(node))
1006
+ return STATIC_IDENTIFIERS.has(node.text);
1007
+ if (tsc.isPrefixUnaryExpression(node))
1008
+ return isStaticExpression(tsc, node.operand);
1009
+ if (tsc.isArrayLiteralExpression(node))
1010
+ return node.elements.every((element) => isStaticExpression(tsc, element));
1011
+ if (tsc.isObjectLiteralExpression(node))
1012
+ return node.properties.every((property) => tsc.isSpreadAssignment(property) ? isStaticExpression(tsc, property) : tsc.isPropertyAssignment(property) && (!tsc.isComputedPropertyName(property.name) || isStaticExpression(tsc, property.name.expression)) && isStaticExpression(tsc, property.initializer));
1013
+ if (tsc.isTemplateExpression(node))
1014
+ return node.templateSpans.every((span) => isStaticExpression(tsc, span.expression));
1015
+ return false;
1016
+ }, rejection = (reason) => ({
1001
1017
  ok: false,
1002
1018
  reason
1003
1019
  }), addImport = (scope, local, binding) => {
@@ -1008,14 +1024,23 @@ var ANALYZER_VERSION = 1, hashText = (text) => Bun.hash(text).toString(36), scri
1008
1024
  if (!clause || clause.isTypeOnly)
1009
1025
  return;
1010
1026
  const specifier = statement.moduleSpecifier;
1011
- const module = tsc.isStringLiteralLike(specifier) || tsc.isIdentifier(specifier) ? resolveImport(specifier.text) : undefined;
1027
+ const written = tsc.isStringLiteralLike(specifier) || tsc.isIdentifier(specifier) ? specifier.text : undefined;
1028
+ const module = written === undefined ? undefined : resolveImport(written);
1012
1029
  if (clause.name)
1013
- addImport(scope, clause.name.text, { module, name: "default" });
1030
+ addImport(scope, clause.name.text, {
1031
+ module,
1032
+ name: "default",
1033
+ specifier: written
1034
+ });
1014
1035
  const bindings = clause.namedBindings;
1015
1036
  if (!bindings)
1016
1037
  return;
1017
1038
  if (tsc.isNamespaceImport(bindings)) {
1018
- addImport(scope, bindings.name.text, { module, name: "*" });
1039
+ addImport(scope, bindings.name.text, {
1040
+ module,
1041
+ name: "*",
1042
+ specifier: written
1043
+ });
1019
1044
  return;
1020
1045
  }
1021
1046
  for (const element of bindings.elements) {
@@ -1023,7 +1048,8 @@ var ANALYZER_VERSION = 1, hashText = (text) => Bun.hash(text).toString(36), scri
1023
1048
  continue;
1024
1049
  addImport(scope, element.name.text, {
1025
1050
  module,
1026
- name: (element.propertyName ?? element.name).text
1051
+ name: (element.propertyName ?? element.name).text,
1052
+ specifier: written
1027
1053
  });
1028
1054
  }
1029
1055
  }, recordExportList = (tsc, scope, statement) => {
@@ -1397,8 +1423,58 @@ var ANALYZER_VERSION = 1, hashText = (text) => Bun.hash(text).toString(36), scri
1397
1423
  edit(node.getStart(file), node.end - node.getStart(file), replacement);
1398
1424
  }
1399
1425
  };
1426
+ const statefulCall = (reference) => {
1427
+ if (reference.deferred || !reference.call)
1428
+ return;
1429
+ const imported = imports.get(reference.name);
1430
+ const factories = imported?.specifier ? STATEFUL_FACTORIES.get(imported.specifier) : undefined;
1431
+ if (!imported || !factories)
1432
+ return;
1433
+ const viaNamespace = imported.name === "*" && reference.member !== undefined;
1434
+ const name = viaNamespace ? reference.member : imported.name;
1435
+ const callee = viaNamespace ? reference.node.parent : reference.node;
1436
+ const call = callee.parent;
1437
+ if (!name || !factories.has(name) || !call || !tsc.isCallExpression(call) || call.expression !== callee)
1438
+ return;
1439
+ return { call, name };
1440
+ };
1441
+ const ownerId = (statement, node) => {
1442
+ const { node: root } = statement;
1443
+ if (!tsc.isVariableStatement(root))
1444
+ return statement.id;
1445
+ const declaration = root.declarationList.declarations.find((item) => item.pos <= node.pos && node.end <= item.end);
1446
+ const name = declaration ? bindingNames(tsc, declaration.name)[0] : undefined;
1447
+ if (!name)
1448
+ return statement.id;
1449
+ return statement.kind === "let" ? `${statement.id}:${name}` : `v:${name}`;
1450
+ };
1451
+ const rewriteStatefulCalls = (statement) => {
1452
+ if (statement.kind === "import" || statement.kind === "skip")
1453
+ return;
1454
+ const occurrences = new Map;
1455
+ for (const reference of statement.refs) {
1456
+ const found = statefulCall(reference);
1457
+ if (!found)
1458
+ continue;
1459
+ const { call, name } = found;
1460
+ const owner = ownerId(statement, call);
1461
+ const line = file.getLineAndCharacterOfPosition(call.getStart(file)).line + 1;
1462
+ const label = quote(`${name}(\u2026) on line ${line}`);
1463
+ const isStatic = call.arguments.every((argument) => isStaticExpression(tsc, argument));
1464
+ if (!isStatic && hasTopLevelAwait(tsc, call))
1465
+ continue;
1466
+ const textHash = hashText(call.getText(file));
1467
+ const occurrence = occurrences.get(`${owner}#${textHash}`) ?? 0;
1468
+ occurrences.set(`${owner}#${textHash}`, occurrence + 1);
1469
+ const key = quote(`${owner}#${textHash}#${occurrence}`);
1470
+ edit(call.getStart(file), 0, isStatic ? `__absH.p(${quote(owner)},${key},${label},()=>(` : `__absH.w(${quote(owner)},${key},${label},()=>(`);
1471
+ edit(call.end, 0, "))");
1472
+ }
1473
+ };
1400
1474
  for (const statement of statements)
1401
1475
  rewriteStatement(statement);
1476
+ for (const statement of statements)
1477
+ rewriteStatefulCalls(statement);
1402
1478
  for (const statement of statements)
1403
1479
  rewriteCellReferences(statement);
1404
1480
  edits.sort(compareEdits);
@@ -1453,6 +1529,10 @@ var ANALYZER_VERSION = 1, hashText = (text) => Bun.hash(text).toString(36), scri
1453
1529
  };
1454
1530
  var init_analyze = __esm(() => {
1455
1531
  IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
1532
+ STATEFUL_FACTORIES = new Map([
1533
+ ["@absolutejs/scoped-state", new Set(["scopedState"])]
1534
+ ]);
1535
+ STATIC_IDENTIFIERS = new Set(["Infinity", "NaN", "undefined"]);
1456
1536
  });
1457
1537
 
1458
1538
  // src/dev/hot/runtime.ts
@@ -1464,6 +1544,7 @@ var records, recordFor = (path) => {
1464
1544
  const created = {
1465
1545
  cells: {},
1466
1546
  generation: 0,
1547
+ instances: new Map,
1467
1548
  keys: new Map,
1468
1549
  lastChanged: new Set,
1469
1550
  lastTimings: new Map,
@@ -1472,6 +1553,7 @@ var records, recordFor = (path) => {
1472
1553
  revisions: new Map,
1473
1554
  slots: new Map,
1474
1555
  tag: 0,
1556
+ warned: new Set,
1475
1557
  watchers: new Map
1476
1558
  };
1477
1559
  records.set(path, created);
@@ -1514,6 +1596,7 @@ var records, recordFor = (path) => {
1514
1596
  record.generation += 1;
1515
1597
  const pending = {
1516
1598
  generation: record.generation,
1599
+ instances: new Map,
1517
1600
  meta,
1518
1601
  reran: new Set,
1519
1602
  seen: new Set,
@@ -1549,6 +1632,7 @@ var records, recordFor = (path) => {
1549
1632
  pending.slots.set(id, { generation, value });
1550
1633
  return value;
1551
1634
  };
1635
+ const where = (label) => `${label} of ${relative(process.cwd(), path) || path}`;
1552
1636
  return {
1553
1637
  cells: record.cells,
1554
1638
  c: (name, id, init) => {
@@ -1576,6 +1660,14 @@ var records, recordFor = (path) => {
1576
1660
  return previous.value;
1577
1661
  return store(id, await ownedAsync(id, factory));
1578
1662
  },
1663
+ p: (owner, key, label, create) => {
1664
+ const previous = record.instances.get(key);
1665
+ const value = previous ? previous.value : create();
1666
+ if (!previous && record.keys.has(owner))
1667
+ console.log(`[hmr] ${where(label)} is new or changed, so its state starts empty`);
1668
+ pending.instances.set(key, { owner, value });
1669
+ return value;
1670
+ },
1579
1671
  r: (id, effect) => {
1580
1672
  if (kept(id))
1581
1673
  return;
@@ -1587,6 +1679,13 @@ var records, recordFor = (path) => {
1587
1679
  return;
1588
1680
  await ownedAsync(id, effect);
1589
1681
  store(id, undefined);
1682
+ },
1683
+ w: (owner, key, label, create) => {
1684
+ if (record.keys.has(owner) && !record.warned.has(key)) {
1685
+ record.warned.add(key);
1686
+ console.warn(`[hmr] ${where(label)} ran again because its statement re-ran, so the state it holds was reset. Create it in its own top-level const (const visitorState = scopedState({ \u2026 })) and pass that to .use() to keep it across edits.`);
1687
+ }
1688
+ return create();
1590
1689
  }
1591
1690
  };
1592
1691
  }, exportToken = (module, name, call) => {
@@ -1612,6 +1711,12 @@ var records, recordFor = (path) => {
1612
1711
  await Promise.all([...replaced.values()].map((slot) => disposeValue(slot.value).catch((error) => console.error(`[hmr] disposing a replaced value failed:`, error))));
1613
1712
  if (record.generation > 1)
1614
1713
  logDisposed(record.path, counts);
1714
+ }, keptInstances = (record, pending) => {
1715
+ const instances = new Map(pending.instances);
1716
+ for (const [key, instance] of record.instances)
1717
+ if (!instances.has(key) && pending.seen.has(instance.owner) && !pending.reran.has(instance.owner))
1718
+ instances.set(key, instance);
1719
+ return instances;
1615
1720
  }, commit = (record, pending) => {
1616
1721
  if (record.pending !== pending)
1617
1722
  return;
@@ -1625,6 +1730,7 @@ var records, recordFor = (path) => {
1625
1730
  record.keys = pending.keys;
1626
1731
  record.lastChanged = pending.changedExports;
1627
1732
  record.lastTimings = pending.timings;
1733
+ record.instances = keptInstances(record, pending);
1628
1734
  for (const name of pending.changedExports)
1629
1735
  record.revisions.set(name, (record.revisions.get(name) ?? 0) + 1);
1630
1736
  if (replaced.size === 0)