@alloy-js/core 0.23.0-dev.1 → 0.23.0-dev.10
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/CHANGELOG.md +0 -22
- package/dist/devtools/index.html +68 -0
- package/dist/src/binder.d.ts +2 -0
- package/dist/src/binder.d.ts.map +1 -1
- package/dist/src/binder.js +55 -12
- package/dist/src/binder.js.map +1 -1
- package/dist/src/components/AccessExpression.d.ts +78 -0
- package/dist/src/components/AccessExpression.d.ts.map +1 -0
- package/dist/src/components/AccessExpression.js +218 -0
- package/dist/src/components/AccessExpression.js.map +1 -0
- package/dist/src/components/AccessExpression.test.d.ts +2 -0
- package/dist/src/components/AccessExpression.test.d.ts.map +1 -0
- package/dist/src/components/AccessExpression.test.js +137 -0
- package/dist/src/components/AccessExpression.test.js.map +1 -0
- package/dist/src/components/AppendFile.d.ts.map +1 -1
- package/dist/src/components/AppendFile.js +14 -3
- package/dist/src/components/AppendFile.js.map +1 -1
- package/dist/src/components/Block.js +1 -1
- package/dist/src/components/Block.js.map +1 -1
- package/dist/src/components/Declaration.d.ts.map +1 -1
- package/dist/src/components/Declaration.js +2 -1
- package/dist/src/components/Declaration.js.map +1 -1
- package/dist/src/components/Prose.js +2 -2
- package/dist/src/components/Prose.js.map +1 -1
- package/dist/src/components/Scope.d.ts.map +1 -1
- package/dist/src/components/Scope.js +6 -1
- package/dist/src/components/Scope.js.map +1 -1
- package/dist/src/components/SourceDirectory.d.ts.map +1 -1
- package/dist/src/components/SourceDirectory.js +1 -2
- package/dist/src/components/SourceDirectory.js.map +1 -1
- package/dist/src/components/TemplateFile.d.ts.map +1 -1
- package/dist/src/components/TemplateFile.js +18 -3
- package/dist/src/components/TemplateFile.js.map +1 -1
- package/dist/src/components/index.d.ts +1 -0
- package/dist/src/components/index.d.ts.map +1 -1
- package/dist/src/components/index.js +1 -0
- package/dist/src/components/index.js.map +1 -1
- package/dist/src/content-slot.d.ts.map +1 -1
- package/dist/src/content-slot.js +7 -6
- package/dist/src/content-slot.js.map +1 -1
- package/dist/src/context.d.ts.map +1 -1
- package/dist/src/context.js +10 -3
- package/dist/src/context.js.map +1 -1
- package/dist/src/debug/cli.d.ts +6 -0
- package/dist/src/debug/cli.d.ts.map +1 -0
- package/dist/src/{debug.js → debug/cli.js} +79 -82
- package/dist/src/debug/cli.js.map +1 -0
- package/dist/src/debug/diagnostics.test.d.ts +2 -0
- package/dist/src/debug/diagnostics.test.d.ts.map +1 -0
- package/dist/src/debug/diagnostics.test.js +45 -0
- package/dist/src/debug/diagnostics.test.js.map +1 -0
- package/dist/src/debug/effects.d.ts +73 -0
- package/dist/src/debug/effects.d.ts.map +1 -0
- package/dist/src/debug/effects.js +228 -0
- package/dist/src/debug/effects.js.map +1 -0
- package/dist/src/debug/effects.test.d.ts +2 -0
- package/dist/src/debug/effects.test.d.ts.map +1 -0
- package/dist/src/debug/effects.test.js +84 -0
- package/dist/src/debug/effects.test.js.map +1 -0
- package/dist/src/debug/files.d.ts +14 -0
- package/dist/src/debug/files.d.ts.map +1 -0
- package/dist/src/debug/files.js +40 -0
- package/dist/src/debug/files.js.map +1 -0
- package/dist/src/debug/files.test.d.ts +2 -0
- package/dist/src/debug/files.test.d.ts.map +1 -0
- package/dist/src/debug/files.test.js +89 -0
- package/dist/src/debug/files.test.js.map +1 -0
- package/dist/src/debug/index.d.ts +61 -0
- package/dist/src/debug/index.d.ts.map +1 -0
- package/dist/src/debug/index.js +69 -0
- package/dist/src/debug/index.js.map +1 -0
- package/dist/src/debug/render.d.ts +57 -0
- package/dist/src/debug/render.d.ts.map +1 -0
- package/dist/src/debug/render.js +519 -0
- package/dist/src/debug/render.js.map +1 -0
- package/dist/src/debug/render.test.d.ts +2 -0
- package/dist/src/debug/render.test.d.ts.map +1 -0
- package/dist/src/debug/render.test.js +328 -0
- package/dist/src/debug/render.test.js.map +1 -0
- package/dist/src/debug/serialize.d.ts +9 -0
- package/dist/src/debug/serialize.d.ts.map +1 -0
- package/dist/src/debug/serialize.js +70 -0
- package/dist/src/debug/serialize.js.map +1 -0
- package/dist/src/debug/symbols.d.ts +15 -0
- package/dist/src/debug/symbols.d.ts.map +1 -0
- package/dist/src/debug/symbols.js +173 -0
- package/dist/src/debug/symbols.js.map +1 -0
- package/dist/src/debug/symbols.test.d.ts +2 -0
- package/dist/src/debug/symbols.test.d.ts.map +1 -0
- package/dist/src/debug/symbols.test.js +104 -0
- package/dist/src/debug/symbols.test.js.map +1 -0
- package/dist/src/debug/trace.d.ts +342 -0
- package/dist/src/debug/trace.d.ts.map +1 -0
- package/dist/src/debug/trace.js +443 -0
- package/dist/src/debug/trace.js.map +1 -0
- package/dist/src/devtools/devtools-protocol.d.ts +232 -0
- package/dist/src/devtools/devtools-protocol.d.ts.map +1 -0
- package/dist/src/devtools/devtools-protocol.js +2 -0
- package/dist/src/devtools/devtools-protocol.js.map +1 -0
- package/dist/src/devtools/devtools-server.browser.d.ts +28 -0
- package/dist/src/devtools/devtools-server.browser.d.ts.map +1 -0
- package/dist/src/devtools/devtools-server.browser.js +36 -0
- package/dist/src/devtools/devtools-server.browser.js.map +1 -0
- package/dist/src/devtools/devtools-server.d.ts +72 -0
- package/dist/src/devtools/devtools-server.d.ts.map +1 -0
- package/dist/src/devtools/devtools-server.js +256 -0
- package/dist/src/devtools/devtools-server.js.map +1 -0
- package/dist/src/devtools/devtools-transport.d.ts +23 -0
- package/dist/src/devtools/devtools-transport.d.ts.map +1 -0
- package/dist/src/devtools/devtools-transport.js +114 -0
- package/dist/src/devtools/devtools-transport.js.map +1 -0
- package/dist/src/devtools-entry.browser.d.ts +4 -0
- package/dist/src/devtools-entry.browser.d.ts.map +1 -0
- package/dist/src/devtools-entry.browser.js +2 -0
- package/dist/src/devtools-entry.browser.js.map +1 -0
- package/dist/src/devtools-entry.d.ts +4 -0
- package/dist/src/devtools-entry.d.ts.map +1 -0
- package/dist/src/devtools-entry.js +2 -0
- package/dist/src/devtools-entry.js.map +1 -0
- package/dist/src/diagnostics.d.ts +34 -0
- package/dist/src/diagnostics.d.ts.map +1 -0
- package/dist/src/diagnostics.js +89 -0
- package/dist/src/diagnostics.js.map +1 -0
- package/dist/src/index.d.ts +3 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/print-hook.d.ts +14 -0
- package/dist/src/print-hook.d.ts.map +1 -0
- package/dist/src/print-hook.js +10 -0
- package/dist/src/print-hook.js.map +1 -0
- package/dist/src/reactive-union-set.d.ts.map +1 -1
- package/dist/src/reactive-union-set.js +28 -3
- package/dist/src/reactive-union-set.js.map +1 -1
- package/dist/src/reactivity.d.ts +50 -8
- package/dist/src/reactivity.d.ts.map +1 -1
- package/dist/src/reactivity.js +225 -39
- package/dist/src/reactivity.js.map +1 -1
- package/dist/src/render-stack.d.ts +18 -1
- package/dist/src/render-stack.d.ts.map +1 -1
- package/dist/src/render-stack.js +61 -1
- package/dist/src/render-stack.js.map +1 -1
- package/dist/src/render.d.ts +8 -15
- package/dist/src/render.d.ts.map +1 -1
- package/dist/src/render.js +370 -109
- package/dist/src/render.js.map +1 -1
- package/dist/src/resource.d.ts.map +1 -1
- package/dist/src/resource.js +5 -0
- package/dist/src/resource.js.map +1 -1
- package/dist/src/scheduler.d.ts +8 -0
- package/dist/src/scheduler.d.ts.map +1 -1
- package/dist/src/scheduler.js +69 -3
- package/dist/src/scheduler.js.map +1 -1
- package/dist/src/symbols/basic-symbol.d.ts.map +1 -1
- package/dist/src/symbols/basic-symbol.js +6 -1
- package/dist/src/symbols/basic-symbol.js.map +1 -1
- package/dist/src/symbols/decl.d.ts.map +1 -1
- package/dist/src/symbols/decl.js +5 -1
- package/dist/src/symbols/decl.js.map +1 -1
- package/dist/src/symbols/output-scope.d.ts +2 -1
- package/dist/src/symbols/output-scope.d.ts.map +1 -1
- package/dist/src/symbols/output-scope.js +13 -8
- package/dist/src/symbols/output-scope.js.map +1 -1
- package/dist/src/symbols/output-symbol.d.ts +1 -0
- package/dist/src/symbols/output-symbol.d.ts.map +1 -1
- package/dist/src/symbols/output-symbol.js +25 -8
- package/dist/src/symbols/output-symbol.js.map +1 -1
- package/dist/src/symbols/symbol-flow.d.ts.map +1 -1
- package/dist/src/symbols/symbol-flow.js +24 -8
- package/dist/src/symbols/symbol-flow.js.map +1 -1
- package/dist/src/symbols/symbol-slot.d.ts.map +1 -1
- package/dist/src/symbols/symbol-slot.js +15 -0
- package/dist/src/symbols/symbol-slot.js.map +1 -1
- package/dist/src/symbols/symbol-slot.test.d.ts +2 -0
- package/dist/src/symbols/symbol-slot.test.d.ts.map +1 -0
- package/dist/src/symbols/symbol-slot.test.js +35 -0
- package/dist/src/symbols/symbol-slot.test.js.map +1 -0
- package/dist/src/symbols/symbol-table.d.ts.map +1 -1
- package/dist/src/symbols/symbol-table.js +6 -5
- package/dist/src/symbols/symbol-table.js.map +1 -1
- package/dist/src/trace.d.ts +2 -0
- package/dist/src/trace.d.ts.map +1 -0
- package/dist/src/trace.js +2 -0
- package/dist/src/trace.js.map +1 -0
- package/dist/src/tracer.d.ts +2 -228
- package/dist/src/tracer.d.ts.map +1 -1
- package/dist/src/tracer.js +5 -298
- package/dist/src/tracer.js.map +1 -1
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +7 -5
- package/dist/src/utils.js.map +1 -1
- package/dist/test/components/append-file.test.d.ts.map +1 -1
- package/dist/test/components/append-file.test.js +18 -10
- package/dist/test/components/append-file.test.js.map +1 -1
- package/dist/test/components/template-file.test.d.ts.map +1 -1
- package/dist/test/components/template-file.test.js +6 -4
- package/dist/test/components/template-file.test.js.map +1 -1
- package/dist/test/lazy-isempty.test.d.ts +2 -0
- package/dist/test/lazy-isempty.test.d.ts.map +1 -0
- package/dist/test/lazy-isempty.test.js +89 -0
- package/dist/test/lazy-isempty.test.js.map +1 -0
- package/dist/test/reactive-union-set-disposers.test.d.ts +2 -0
- package/dist/test/reactive-union-set-disposers.test.d.ts.map +1 -0
- package/dist/test/reactive-union-set-disposers.test.js +98 -0
- package/dist/test/reactive-union-set-disposers.test.js.map +1 -0
- package/dist/test/reactivity/shallow-reactive.test.d.ts +2 -0
- package/dist/test/reactivity/shallow-reactive.test.d.ts.map +1 -0
- package/dist/test/reactivity/shallow-reactive.test.js +52 -0
- package/dist/test/reactivity/shallow-reactive.test.js.map +1 -0
- package/dist/test/rendering/basic.test.js +3 -0
- package/dist/test/rendering/basic.test.js.map +1 -1
- package/dist/test/rendering/print-render-stack.test.d.ts.map +1 -1
- package/dist/test/rendering/print-render-stack.test.js +91 -98
- package/dist/test/rendering/print-render-stack.test.js.map +1 -1
- package/dist/test/scheduler-extended.test.d.ts +2 -0
- package/dist/test/scheduler-extended.test.d.ts.map +1 -0
- package/dist/test/scheduler-extended.test.js +96 -0
- package/dist/test/scheduler-extended.test.js.map +1 -0
- package/dist/test/scheduler.test.d.ts +2 -0
- package/dist/test/scheduler.test.d.ts.map +1 -0
- package/dist/test/scheduler.test.js +46 -0
- package/dist/test/scheduler.test.js.map +1 -0
- package/dist/testing/create-test-wrapper.d.ts +1 -1
- package/dist/testing/create-test-wrapper.d.ts.map +1 -1
- package/dist/testing/create-test-wrapper.js +1 -1
- package/dist/testing/create-test-wrapper.js.map +1 -1
- package/dist/testing/devtools-utils.d.ts +26 -0
- package/dist/testing/devtools-utils.d.ts.map +1 -0
- package/dist/testing/devtools-utils.js +140 -0
- package/dist/testing/devtools-utils.js.map +1 -0
- package/dist/testing/extend-expect.d.ts.map +1 -1
- package/dist/testing/extend-expect.js +63 -1
- package/dist/testing/extend-expect.js.map +1 -1
- package/dist/testing/render.d.ts +2 -2
- package/dist/testing/render.d.ts.map +1 -1
- package/dist/testing/render.js +2 -2
- package/dist/testing/render.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +21 -7
- package/scripts/copy-devtools-ui.mjs +26 -0
- package/src/binder.ts +71 -16
- package/src/components/AccessExpression.test.tsx +132 -0
- package/src/components/AccessExpression.tsx +344 -0
- package/src/components/AppendFile.tsx +14 -9
- package/src/components/Block.tsx +1 -1
- package/src/components/Declaration.tsx +2 -1
- package/src/components/Prose.tsx +1 -1
- package/src/components/Scope.tsx +6 -1
- package/src/components/SourceDirectory.tsx +1 -2
- package/src/components/TemplateFile.tsx +18 -9
- package/src/components/index.tsx +1 -0
- package/src/content-slot.tsx +7 -7
- package/src/context.ts +17 -6
- package/src/{debug.ts → debug/cli.ts} +114 -125
- package/src/debug/diagnostics.test.tsx +55 -0
- package/src/debug/effects.test.tsx +89 -0
- package/src/debug/effects.ts +317 -0
- package/src/debug/files.test.tsx +96 -0
- package/src/debug/files.ts +40 -0
- package/src/debug/index.ts +128 -0
- package/src/debug/render.test.tsx +379 -0
- package/src/debug/render.ts +639 -0
- package/src/debug/serialize.ts +85 -0
- package/src/debug/symbols.test.tsx +106 -0
- package/src/debug/symbols.ts +239 -0
- package/src/debug/trace.ts +312 -0
- package/src/devtools/devtools-protocol.ts +312 -0
- package/src/devtools/devtools-server.browser.ts +71 -0
- package/src/devtools/devtools-server.ts +290 -0
- package/src/devtools/devtools-transport.ts +154 -0
- package/src/devtools-entry.browser.ts +52 -0
- package/src/devtools-entry.ts +54 -0
- package/src/diagnostics.ts +141 -0
- package/src/index.ts +2 -7
- package/src/print-hook.ts +22 -0
- package/src/reactive-union-set.ts +85 -44
- package/src/reactivity.ts +301 -59
- package/src/render-stack.ts +73 -1
- package/src/render.ts +470 -161
- package/src/resource.ts +28 -19
- package/src/scheduler.ts +80 -4
- package/src/symbols/basic-symbol.ts +6 -1
- package/src/symbols/decl.ts +5 -1
- package/src/symbols/output-scope.ts +21 -13
- package/src/symbols/output-symbol.ts +34 -14
- package/src/symbols/symbol-flow.ts +76 -39
- package/src/symbols/symbol-slot.test.tsx +41 -0
- package/src/symbols/symbol-slot.tsx +47 -20
- package/src/symbols/symbol-table.ts +6 -10
- package/src/trace.ts +1 -0
- package/src/tracer.ts +13 -242
- package/src/utils.tsx +24 -17
- package/temp/api.json +5658 -3095
- package/test/components/append-file.test.tsx +36 -29
- package/test/components/template-file.test.tsx +11 -11
- package/test/lazy-isempty.test.tsx +106 -0
- package/test/reactive-union-set-disposers.test.tsx +112 -0
- package/test/reactivity/shallow-reactive.test.tsx +56 -0
- package/test/rendering/basic.test.tsx +4 -0
- package/test/rendering/print-render-stack.test.tsx +52 -43
- package/test/scheduler-extended.test.tsx +122 -0
- package/test/scheduler.test.tsx +50 -0
- package/testing/create-test-wrapper.tsx +1 -1
- package/testing/devtools-utils.ts +203 -0
- package/testing/extend-expect.ts +89 -0
- package/testing/render.ts +2 -2
- package/testing/vitest.d.ts +9 -0
- package/dist/src/debug.d.ts +0 -14
- package/dist/src/debug.d.ts.map +0 -1
- package/dist/src/debug.js.map +0 -1
package/src/reactivity.ts
CHANGED
|
@@ -2,21 +2,28 @@ import {
|
|
|
2
2
|
isRef,
|
|
3
3
|
pauseTracking,
|
|
4
4
|
ReactiveEffectRunner,
|
|
5
|
-
ref,
|
|
6
5
|
Ref,
|
|
7
6
|
resetTracking,
|
|
8
7
|
ShallowReactive,
|
|
9
|
-
shallowRef,
|
|
10
8
|
stop,
|
|
9
|
+
computed as vueComputed,
|
|
11
10
|
effect as vueEffect,
|
|
11
|
+
ref as vueRef,
|
|
12
|
+
shallowReactive as vueShallowReactive,
|
|
13
|
+
shallowRef as vueShallowRef,
|
|
14
|
+
toRef as vueToRef,
|
|
15
|
+
toRefs as vueToRefs,
|
|
12
16
|
} from "@vue/reactivity";
|
|
13
|
-
import
|
|
14
|
-
|
|
17
|
+
import {
|
|
18
|
+
captureSourceLocation,
|
|
19
|
+
debug,
|
|
20
|
+
isDevtoolsEnabled,
|
|
21
|
+
} from "./debug/index.js";
|
|
22
|
+
import { RenderedTextTree } from "./render.js";
|
|
23
|
+
import { Children, ComponentCreator } from "./runtime/component.js";
|
|
15
24
|
import { scheduler } from "./scheduler.js";
|
|
16
25
|
import type { OutputSymbol } from "./symbols/output-symbol.js";
|
|
17
|
-
import { trace, TracePhase } from "./tracer.js";
|
|
18
26
|
|
|
19
|
-
// check for multiple versions of alloy here.
|
|
20
27
|
if ((globalThis as any).__ALLOY__) {
|
|
21
28
|
throw new Error(
|
|
22
29
|
"Multiple versions of Alloy are loaded for this project. This will likely cause undesirable behavior.",
|
|
@@ -25,7 +32,8 @@ if ((globalThis as any).__ALLOY__) {
|
|
|
25
32
|
(globalThis as any).__ALLOY__ = true;
|
|
26
33
|
|
|
27
34
|
export function getElementCache() {
|
|
28
|
-
|
|
35
|
+
const ctx = getContext()!;
|
|
36
|
+
return (ctx.elementCache ??= new Map());
|
|
29
37
|
}
|
|
30
38
|
|
|
31
39
|
export type ElementCacheKey =
|
|
@@ -39,8 +47,12 @@ export interface Disposable {
|
|
|
39
47
|
(): void;
|
|
40
48
|
}
|
|
41
49
|
|
|
50
|
+
let contextIdCounter = 0;
|
|
51
|
+
|
|
42
52
|
export interface Context {
|
|
43
|
-
|
|
53
|
+
/** Monotonic numeric ID for trace/debug correlation. */
|
|
54
|
+
id: number;
|
|
55
|
+
disposables?: Disposable[];
|
|
44
56
|
owner: Context | null;
|
|
45
57
|
|
|
46
58
|
// context providers
|
|
@@ -53,7 +65,7 @@ export interface Context {
|
|
|
53
65
|
* A cache of RenderTextTree nodes created within this context,
|
|
54
66
|
* indexed by the component or function which created them.
|
|
55
67
|
*/
|
|
56
|
-
elementCache
|
|
68
|
+
elementCache?: ElementCache;
|
|
57
69
|
/**
|
|
58
70
|
* When this context was created by a component, this will
|
|
59
71
|
* be the component that created it.
|
|
@@ -78,9 +90,17 @@ export interface Context {
|
|
|
78
90
|
|
|
79
91
|
/**
|
|
80
92
|
* A ref that indicates whether the component is empty.
|
|
93
|
+
* Only allocated when reactively observed (ContentSlot, mapJoin).
|
|
81
94
|
*/
|
|
82
95
|
isEmpty?: Ref<boolean>;
|
|
83
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Cheap boolean tracking the last propagated empty state.
|
|
99
|
+
* Used by notifyContentState() for early-return optimization
|
|
100
|
+
* without requiring a reactive ref on every context.
|
|
101
|
+
*/
|
|
102
|
+
_lastEmpty: boolean;
|
|
103
|
+
|
|
84
104
|
/**
|
|
85
105
|
* Whether this context is a root context
|
|
86
106
|
*/
|
|
@@ -92,21 +112,46 @@ export function getContext() {
|
|
|
92
112
|
return globalContext;
|
|
93
113
|
}
|
|
94
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Walk up the owner chain to find the nearest ancestor context that
|
|
117
|
+
* corresponds to an effect (has meta.effectId). This bridges non-effect
|
|
118
|
+
* scopes (like createRoot iterations in For) so the owner chain always
|
|
119
|
+
* connects effect-to-effect.
|
|
120
|
+
*/
|
|
121
|
+
function resolveOwnerEffectContextId(context: Context): number | null {
|
|
122
|
+
let owner = context.owner;
|
|
123
|
+
while (owner) {
|
|
124
|
+
if (owner.meta?.effectId !== undefined) {
|
|
125
|
+
return owner.id;
|
|
126
|
+
}
|
|
127
|
+
owner = owner.owner;
|
|
128
|
+
}
|
|
129
|
+
return context.owner?.id ?? null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Ensure that a context has an isEmpty ref, creating one if needed.
|
|
134
|
+
* Only call this when you need to reactively observe isEmpty (e.g.,
|
|
135
|
+
* ContentSlot, mapJoin). Most contexts don't need an isEmpty ref.
|
|
136
|
+
*/
|
|
137
|
+
export function ensureIsEmpty(context: Context): Ref<boolean> {
|
|
138
|
+
context.isEmpty ??= ref(context.childrenWithContent === 0);
|
|
139
|
+
return context.isEmpty;
|
|
140
|
+
}
|
|
141
|
+
|
|
95
142
|
export interface RootOptions {
|
|
96
143
|
componentOwner?: ComponentCreator<any>;
|
|
97
144
|
}
|
|
98
145
|
|
|
99
146
|
export function root<T>(fn: (d: Disposable) => T, options?: RootOptions): T {
|
|
100
147
|
const context: Context = {
|
|
148
|
+
id: contextIdCounter++,
|
|
101
149
|
componentOwner: options?.componentOwner,
|
|
102
|
-
disposables: [],
|
|
103
150
|
owner: globalContext,
|
|
104
|
-
context: {},
|
|
105
|
-
elementCache: new Map(),
|
|
106
151
|
takesSymbols: false,
|
|
107
152
|
takenSymbols: undefined,
|
|
108
153
|
childrenWithContent: 0,
|
|
109
|
-
|
|
154
|
+
_lastEmpty: true,
|
|
110
155
|
isRoot: true,
|
|
111
156
|
};
|
|
112
157
|
|
|
@@ -115,8 +160,8 @@ export function root<T>(fn: (d: Disposable) => T, options?: RootOptions): T {
|
|
|
115
160
|
try {
|
|
116
161
|
ret = untrack(() =>
|
|
117
162
|
fn(() => {
|
|
118
|
-
for (const d of context!.disposables) {
|
|
119
|
-
d
|
|
163
|
+
for (const d of context!.disposables ?? []) {
|
|
164
|
+
untrack(d);
|
|
120
165
|
}
|
|
121
166
|
}),
|
|
122
167
|
);
|
|
@@ -127,6 +172,15 @@ export function root<T>(fn: (d: Disposable) => T, options?: RootOptions): T {
|
|
|
127
172
|
return ret;
|
|
128
173
|
}
|
|
129
174
|
|
|
175
|
+
export interface EffectDebugOptions {
|
|
176
|
+
name?: string;
|
|
177
|
+
type?: string;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export interface EffectOptions {
|
|
181
|
+
debug?: EffectDebugOptions;
|
|
182
|
+
}
|
|
183
|
+
|
|
130
184
|
export function untrack<T>(fn: () => T): T {
|
|
131
185
|
pauseTracking();
|
|
132
186
|
const v = fn();
|
|
@@ -134,68 +188,148 @@ export function untrack<T>(fn: () => T): T {
|
|
|
134
188
|
return v;
|
|
135
189
|
}
|
|
136
190
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
191
|
+
/**
|
|
192
|
+
* Walk up the context owner chain to find the nearest effect ID.
|
|
193
|
+
* Used to attribute reactive mutations to the effect that caused them.
|
|
194
|
+
*/
|
|
195
|
+
export function findCurrentEffectId(): number | undefined {
|
|
196
|
+
let ctx = globalContext;
|
|
197
|
+
while (ctx) {
|
|
198
|
+
const id = ctx.meta?.effectId;
|
|
199
|
+
if (id !== undefined && id !== -1) return id;
|
|
200
|
+
ctx = ctx.owner;
|
|
201
|
+
}
|
|
202
|
+
return undefined;
|
|
146
203
|
}
|
|
147
204
|
|
|
148
|
-
export function
|
|
205
|
+
export function memo<T>(fn: () => T, equal?: boolean, name?: string): () => T {
|
|
206
|
+
const o = shallowRef<T>();
|
|
207
|
+
effect(
|
|
208
|
+
(prev) => {
|
|
209
|
+
const res = fn();
|
|
210
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
211
|
+
(!equal || prev !== res) && (o.value = res);
|
|
212
|
+
return res;
|
|
213
|
+
},
|
|
214
|
+
undefined as T,
|
|
215
|
+
{
|
|
216
|
+
debug: { name: name ? `memo:${name}` : "memo" },
|
|
217
|
+
},
|
|
218
|
+
);
|
|
219
|
+
const getter = (() => o.value as T) as () => T;
|
|
220
|
+
if (name) {
|
|
221
|
+
Object.defineProperty(getter, "name", { value: name, configurable: true });
|
|
222
|
+
}
|
|
223
|
+
return getter;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function effect<T>(
|
|
227
|
+
fn: (prev?: T) => T,
|
|
228
|
+
current?: T,
|
|
229
|
+
options?: EffectOptions,
|
|
230
|
+
) {
|
|
149
231
|
const context: Context = {
|
|
150
|
-
|
|
151
|
-
disposables: [] as (() => void)[],
|
|
232
|
+
id: contextIdCounter++,
|
|
152
233
|
owner: globalContext,
|
|
153
|
-
elementCache: new Map(),
|
|
154
234
|
takesSymbols: false,
|
|
155
235
|
takenSymbols: undefined,
|
|
156
236
|
childrenWithContent: 0,
|
|
237
|
+
_lastEmpty: true,
|
|
157
238
|
isRoot: false,
|
|
158
239
|
};
|
|
159
240
|
|
|
241
|
+
const debugInfo = options?.debug;
|
|
242
|
+
const effectId = debug.effect.register({
|
|
243
|
+
name: debugInfo?.name ?? fn.name,
|
|
244
|
+
type: debugInfo?.type,
|
|
245
|
+
createdAt: captureSourceLocation(),
|
|
246
|
+
contextId: context.id,
|
|
247
|
+
ownerContextId: resolveOwnerEffectContextId(context),
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
if (effectId !== -1) {
|
|
251
|
+
context.meta ??= {};
|
|
252
|
+
context.meta.effectId = effectId;
|
|
253
|
+
}
|
|
254
|
+
|
|
160
255
|
const cleanupFn = (final: boolean) => {
|
|
161
256
|
const d = context.disposables;
|
|
162
|
-
context.disposables =
|
|
163
|
-
|
|
257
|
+
context.disposables = undefined;
|
|
258
|
+
if (d) {
|
|
259
|
+
for (let k = 0, len = d.length; k < len; k++) untrack(d[k]);
|
|
260
|
+
}
|
|
164
261
|
|
|
165
262
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
166
263
|
final && stop(runner);
|
|
167
264
|
};
|
|
168
265
|
|
|
169
266
|
onCleanup(() => cleanupFn(true));
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
267
|
+
const effectOpts: Record<string, unknown> = {
|
|
268
|
+
// allow recursive effects with 32, 1 and 4 are default flags
|
|
269
|
+
flags: 1 | 4 | 32,
|
|
270
|
+
scheduler: scheduler(),
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
if (effectId !== -1) {
|
|
274
|
+
effectOpts.onTrack = (event: any) => {
|
|
275
|
+
const targetKey =
|
|
276
|
+
typeof event.key === "symbol" ? event.key.toString() : event.key;
|
|
277
|
+
if (isRef(event.target)) {
|
|
278
|
+
const id = refId(event.target);
|
|
279
|
+
debug.effect.ensureRef({ id, kind: "ref" });
|
|
280
|
+
debug.effect.track({
|
|
281
|
+
effectId,
|
|
282
|
+
target: event.target,
|
|
283
|
+
refId: id,
|
|
284
|
+
targetKey,
|
|
285
|
+
});
|
|
286
|
+
} else {
|
|
287
|
+
debug.effect.track({
|
|
288
|
+
effectId,
|
|
289
|
+
target: event.target,
|
|
290
|
+
targetKey,
|
|
291
|
+
});
|
|
180
292
|
}
|
|
181
|
-
}
|
|
182
|
-
{
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
293
|
+
};
|
|
294
|
+
effectOpts.onTrigger = (event: any) => {
|
|
295
|
+
const targetKey =
|
|
296
|
+
typeof event.key === "symbol" ? event.key.toString() : event.key;
|
|
297
|
+
if (isRef(event.target)) {
|
|
298
|
+
const id = refId(event.target);
|
|
299
|
+
debug.effect.ensureRef({ id, kind: "ref" });
|
|
300
|
+
debug.effect.trigger({
|
|
301
|
+
effectId,
|
|
302
|
+
target: event.target,
|
|
303
|
+
refId: id,
|
|
304
|
+
targetKey,
|
|
305
|
+
kind: "triggered-by",
|
|
190
306
|
});
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
307
|
+
} else {
|
|
308
|
+
debug.effect.trigger({
|
|
309
|
+
effectId,
|
|
310
|
+
target: event.target,
|
|
311
|
+
targetKey,
|
|
312
|
+
kind: "triggered-by",
|
|
195
313
|
});
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const runner: ReactiveEffectRunner<void> = vueEffect(() => {
|
|
319
|
+
cleanupFn(false);
|
|
320
|
+
|
|
321
|
+
const oldContext = globalContext;
|
|
322
|
+
globalContext = context;
|
|
323
|
+
try {
|
|
324
|
+
current = fn(current);
|
|
325
|
+
} finally {
|
|
326
|
+
globalContext = oldContext;
|
|
327
|
+
}
|
|
328
|
+
}, effectOpts as any);
|
|
329
|
+
|
|
330
|
+
if (effectId !== -1) {
|
|
331
|
+
effectIdMap.set(runner.effect, effectId);
|
|
332
|
+
}
|
|
199
333
|
}
|
|
200
334
|
|
|
201
335
|
/**
|
|
@@ -218,7 +352,7 @@ export function effect<T>(fn: (prev?: T) => T, current?: T) {
|
|
|
218
352
|
*/
|
|
219
353
|
export function onCleanup(fn: Disposable) {
|
|
220
354
|
if (globalContext != null) {
|
|
221
|
-
globalContext.disposables.push(fn);
|
|
355
|
+
(globalContext.disposables ??= []).push(fn);
|
|
222
356
|
}
|
|
223
357
|
}
|
|
224
358
|
|
|
@@ -253,14 +387,122 @@ export function isCustomContext(child: Children): child is CustomContext {
|
|
|
253
387
|
);
|
|
254
388
|
}
|
|
255
389
|
|
|
390
|
+
export function ref<T>(
|
|
391
|
+
value?: T,
|
|
392
|
+
options?: { isInfrastructure?: boolean },
|
|
393
|
+
): Ref<T> {
|
|
394
|
+
const result = vueRef(value) as Ref<T>;
|
|
395
|
+
debug.effect.registerRef({
|
|
396
|
+
id: refId(result, options?.isInfrastructure),
|
|
397
|
+
kind: "ref",
|
|
398
|
+
createdAt: captureSourceLocation(),
|
|
399
|
+
createdByEffectId: globalContext?.meta?.effectId,
|
|
400
|
+
isInfrastructure: options?.isInfrastructure,
|
|
401
|
+
});
|
|
402
|
+
return result;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Stores creation location for shallowReactive objects so registerNonRefTarget
|
|
406
|
+
// can look it up later (since targets are lazily registered on first track/trigger).
|
|
407
|
+
const reactiveCreationLocations = new WeakMap<
|
|
408
|
+
object,
|
|
409
|
+
ReturnType<typeof captureSourceLocation>
|
|
410
|
+
>();
|
|
411
|
+
|
|
412
|
+
export function getReactiveCreationLocation(target: object) {
|
|
413
|
+
return reactiveCreationLocations.get(target);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
export function shallowReactive<T extends object>(
|
|
417
|
+
target: T,
|
|
418
|
+
): ShallowReactive<T> {
|
|
419
|
+
const result = vueShallowReactive(target);
|
|
420
|
+
if (isDevtoolsEnabled()) {
|
|
421
|
+
// Store by raw target — Vue's onTrack/onTrigger events pass the raw object, not the proxy.
|
|
422
|
+
reactiveCreationLocations.set(target, captureSourceLocation());
|
|
423
|
+
}
|
|
424
|
+
return result;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export function shallowRef<T>(value?: T): Ref<T> {
|
|
428
|
+
const result = vueShallowRef(value) as Ref<T>;
|
|
429
|
+
debug.effect.registerRef({
|
|
430
|
+
id: refId(result),
|
|
431
|
+
kind: "shallowRef",
|
|
432
|
+
createdAt: captureSourceLocation(),
|
|
433
|
+
createdByEffectId: globalContext?.meta?.effectId,
|
|
434
|
+
});
|
|
435
|
+
return result;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
export function computed<T>(getter: () => T): Ref<T> {
|
|
439
|
+
const result = vueComputed(getter) as Ref<T>;
|
|
440
|
+
debug.effect.registerRef({
|
|
441
|
+
id: refId(result),
|
|
442
|
+
kind: "computed",
|
|
443
|
+
createdAt: captureSourceLocation(),
|
|
444
|
+
createdByEffectId: globalContext?.meta?.effectId,
|
|
445
|
+
});
|
|
446
|
+
return result;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
export function toRef<T extends object, K extends keyof T>(
|
|
450
|
+
object: T,
|
|
451
|
+
key: K,
|
|
452
|
+
defaultValue?: T[K],
|
|
453
|
+
): Ref<T[K]> {
|
|
454
|
+
const result =
|
|
455
|
+
defaultValue === undefined ?
|
|
456
|
+
(vueToRef(object, key) as Ref<T[K]>)
|
|
457
|
+
: (vueToRef(object, key, defaultValue) as Ref<T[K]>);
|
|
458
|
+
debug.effect.registerRef({
|
|
459
|
+
id: refId(result),
|
|
460
|
+
kind: "toRef",
|
|
461
|
+
createdAt: captureSourceLocation(),
|
|
462
|
+
createdByEffectId: globalContext?.meta?.effectId,
|
|
463
|
+
});
|
|
464
|
+
return result;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
export function toRefs<T extends object>(
|
|
468
|
+
object: T,
|
|
469
|
+
): { [K in keyof T]: Ref<T[K]> } {
|
|
470
|
+
const result = vueToRefs(object) as { [K in keyof T]: Ref<T[K]> };
|
|
471
|
+
for (const refValue of Object.values(result) as Ref<unknown>[]) {
|
|
472
|
+
debug.effect.registerRef({
|
|
473
|
+
id: refId(refValue),
|
|
474
|
+
kind: "toRef",
|
|
475
|
+
createdAt: captureSourceLocation(),
|
|
476
|
+
createdByEffectId: globalContext?.meta?.effectId,
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
return result;
|
|
480
|
+
}
|
|
481
|
+
|
|
256
482
|
const seenRefs = new WeakMap<Ref<unknown>, number>();
|
|
257
483
|
let refIdCounter = 1;
|
|
484
|
+
let infraRefIdCounter = -1;
|
|
485
|
+
const effectIdMap = new WeakMap<object, number>();
|
|
258
486
|
|
|
259
|
-
export function refId(ref: Ref<unknown
|
|
487
|
+
export function refId(ref: Ref<unknown>, isInfrastructure?: boolean): number {
|
|
260
488
|
let id = seenRefs.get(ref);
|
|
261
489
|
if (id === undefined) {
|
|
262
|
-
id = refIdCounter++;
|
|
490
|
+
id = isInfrastructure ? infraRefIdCounter-- : refIdCounter++;
|
|
263
491
|
seenRefs.set(ref, id);
|
|
264
492
|
}
|
|
265
493
|
return id;
|
|
266
494
|
}
|
|
495
|
+
|
|
496
|
+
/** Allocate a unique reactive target ID from the same counter space as ref IDs. */
|
|
497
|
+
export function nextReactiveId(): number {
|
|
498
|
+
return refIdCounter++;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
export function resetRefIdCounter(): void {
|
|
502
|
+
refIdCounter = 1;
|
|
503
|
+
infraRefIdCounter = -1;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
export function getEffectDebugId(effect: object): number | undefined {
|
|
507
|
+
return effectIdMap.get(effect);
|
|
508
|
+
}
|
package/src/render-stack.ts
CHANGED
|
@@ -13,6 +13,14 @@ const renderStack: Array<{
|
|
|
13
13
|
source?: SourceLocation;
|
|
14
14
|
}> = [];
|
|
15
15
|
|
|
16
|
+
export interface RenderStackSnapshotEntry {
|
|
17
|
+
component: Component<any>;
|
|
18
|
+
props: Props;
|
|
19
|
+
context: Context | null;
|
|
20
|
+
source?: SourceLocation;
|
|
21
|
+
displayName: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
16
24
|
export function pushStack(
|
|
17
25
|
component: Component<any>,
|
|
18
26
|
props: Props,
|
|
@@ -25,10 +33,51 @@ export function popStack() {
|
|
|
25
33
|
renderStack.pop();
|
|
26
34
|
}
|
|
27
35
|
|
|
36
|
+
export function currentComponentName(): string | undefined {
|
|
37
|
+
const entry = renderStack[renderStack.length - 1];
|
|
38
|
+
return entry?.component.name || undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
28
41
|
export function clearRenderStack() {
|
|
29
42
|
renderStack.length = 0;
|
|
30
43
|
}
|
|
31
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Get the current rendering path from the render stack context.
|
|
47
|
+
* Prefers SourceFileContext over SourceDirectoryContext.
|
|
48
|
+
*/
|
|
49
|
+
export function getCurrentRenderPath(): string | undefined {
|
|
50
|
+
let currentPath: string | undefined;
|
|
51
|
+
for (let i = renderStack.length - 1; i >= 0; i--) {
|
|
52
|
+
const { context } = renderStack[i];
|
|
53
|
+
// Prefer SourceFileContext over SourceDirectoryContext
|
|
54
|
+
if (context?.context?.[SourceFileContext.id]) {
|
|
55
|
+
const fileContext = context.context[
|
|
56
|
+
SourceFileContext.id
|
|
57
|
+
] as SourceFileContext;
|
|
58
|
+
return fileContext.path;
|
|
59
|
+
}
|
|
60
|
+
if (!currentPath && context?.context?.[SourceDirectoryContext.id]) {
|
|
61
|
+
const dirContext = context.context[
|
|
62
|
+
SourceDirectoryContext.id
|
|
63
|
+
] as SourceDirectoryContext;
|
|
64
|
+
currentPath = dirContext.path;
|
|
65
|
+
// Don't break - keep looking for a SourceFileContext
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return currentPath;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function getRenderStackSnapshot(): RenderStackSnapshotEntry[] {
|
|
72
|
+
return renderStack.map((entry) => ({
|
|
73
|
+
component: entry.component,
|
|
74
|
+
props: entry.props,
|
|
75
|
+
context: entry.context,
|
|
76
|
+
source: entry.source,
|
|
77
|
+
displayName: getComponentDisplayName(entry.component, entry.props),
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
|
|
32
81
|
// Helper functions
|
|
33
82
|
function getComponentDisplayName(
|
|
34
83
|
component: Component<any>,
|
|
@@ -93,8 +142,10 @@ function formatSourceLocation(source: SourceLocation): string {
|
|
|
93
142
|
* This differs from debug.component.stack in that this uses a purpose-built
|
|
94
143
|
* stack rather than walking the context chain. When this is called, the context
|
|
95
144
|
* chain has been restored. In the future this can probably be unified nicely.
|
|
145
|
+
*
|
|
146
|
+
* @param error - Optional error to print the stack trace from
|
|
96
147
|
*/
|
|
97
|
-
export function printRenderStack() {
|
|
148
|
+
export function printRenderStack(error?: unknown) {
|
|
98
149
|
// Find the nearest SourceFileContext or SourceDirectoryContext from the render stack
|
|
99
150
|
let currentPath: string | undefined;
|
|
100
151
|
for (let i = renderStack.length - 1; i >= 0; i--) {
|
|
@@ -124,6 +175,27 @@ export function printRenderStack() {
|
|
|
124
175
|
console.error(pc.red("Error rendering:"));
|
|
125
176
|
}
|
|
126
177
|
|
|
178
|
+
// Print the error message and stack if provided
|
|
179
|
+
if (error) {
|
|
180
|
+
if (error instanceof Error) {
|
|
181
|
+
// eslint-disable-next-line no-console
|
|
182
|
+
console.error(pc.red(` ${error.message}`));
|
|
183
|
+
if (error.stack) {
|
|
184
|
+
// Print stack lines (skip the first line which is the error message)
|
|
185
|
+
const stackLines = error.stack.split("\n").slice(1);
|
|
186
|
+
for (const line of stackLines) {
|
|
187
|
+
// eslint-disable-next-line no-console
|
|
188
|
+
console.error(pc.gray(line));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
// eslint-disable-next-line no-console
|
|
193
|
+
console.error(pc.red(` ${String(error)}`));
|
|
194
|
+
}
|
|
195
|
+
// eslint-disable-next-line no-console
|
|
196
|
+
console.error(); // Empty line before component stack
|
|
197
|
+
}
|
|
198
|
+
|
|
127
199
|
// First pass: determine which providers should be nested vs standalone
|
|
128
200
|
// A provider should be nested under its parent if it's from a different file
|
|
129
201
|
// (i.e., it's part of the component's implementation, not user-provided)
|