@expressive/dom 0.0.0 → 0.2.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.
@@ -0,0 +1,37 @@
1
+ import { Context, State } from "@expressive/mvc";
2
+
3
+ //#region src/adapter.d.ts
4
+ interface UseState extends State {
5
+ use?(...props: any[]): Promise<void> | void;
6
+ mount?(): (() => void) | void;
7
+ }
8
+ type NoVoid<T> = T extends undefined | void ? null : T;
9
+ declare module '@expressive/mvc' {
10
+ interface UseState extends State {
11
+ use?(...props: any[]): Promise<void> | void;
12
+ mount?(): (() => void) | void;
13
+ }
14
+ namespace State {
15
+ type ForceRefresh = {
16
+ (): void;
17
+ <T = void>(waitFor: Promise<T>): Promise<T>;
18
+ <T = void>(invoke: () => Promise<T>): Promise<T>;
19
+ };
20
+ type GetFactory<T extends State, R> = (this: T, current: T, refresh: ForceRefresh) => R;
21
+ type UseArgs<T extends State> = T extends {
22
+ use(...props: infer P): any;
23
+ } ? P : State.Args<T>;
24
+ function get<T extends State>(this: State.Extends<T>): T;
25
+ function get<T extends State>(this: State.Extends<T>, required: false): T | undefined;
26
+ function get<T extends State>(this: State.Extends<T>, required: true): Required<T>;
27
+ function get<T extends State, R>(this: State.Extends<T>, factory: GetFactory<T, R>): NoVoid<R>;
28
+ function use<T extends UseState>(this: State.Type<T>, ...args: UseArgs<T>): T;
29
+ }
30
+ namespace Component {
31
+ const use: never;
32
+ }
33
+ interface Component {
34
+ mount?(): (() => void) | void;
35
+ }
36
+ }
37
+ //# sourceMappingURL=chunk-adapter-4jlvwsge.d.ts.map
@@ -0,0 +1,178 @@
1
+ import { pending } from "@expressive/mvc";
2
+
3
+ //#region src/scheduler.ts
4
+ const urgent = /* @__PURE__ */ new Set();
5
+ const passive = /* @__PURE__ */ new Set();
6
+ const after = [];
7
+ let depth = 0;
8
+ let urgentQueued = false;
9
+ let passiveQueued = false;
10
+ function flush(scopes) {
11
+ for (const scope of scopes) {
12
+ scopes.delete(scope);
13
+ scope.queued = void 0;
14
+ let waiting = void 0;
15
+ try {
16
+ waiting = scope.update(false);
17
+ } catch (error) {
18
+ console.error(error);
19
+ }
20
+ settle(scope);
21
+ if (!waiting) unblock(scope);
22
+ }
23
+ }
24
+ function flushUrgent() {
25
+ urgentQueued = false;
26
+ flush(urgent);
27
+ for (const run of after.splice(0)) run();
28
+ }
29
+ function afterFlush(run) {
30
+ after.push(run);
31
+ if (!urgentQueued) {
32
+ urgentQueued = true;
33
+ queueMicrotask(flushUrgent);
34
+ }
35
+ }
36
+ function flushPassive() {
37
+ passiveQueued = false;
38
+ const batch = [...passive].filter((scope) => scope.queued === "passive");
39
+ passive.clear();
40
+ for (const scope of batch) {
41
+ const waiting = scope.probe?.();
42
+ if (waiting) return defer(batch, waiting, scope);
43
+ }
44
+ batch.sort((a, b) => Number(!!b.empty?.()) - Number(!!a.empty?.()));
45
+ for (let index = 0; index < batch.length; index++) {
46
+ const scope = batch[index];
47
+ if (scope.queued !== "passive") continue;
48
+ scope.queued = void 0;
49
+ let waiting = void 0;
50
+ try {
51
+ waiting = scope.update(true);
52
+ } catch (error) {
53
+ console.error(error);
54
+ }
55
+ settle(scope);
56
+ if (waiting) return defer(batch.slice(index + 1), waiting, scope);
57
+ unblock(scope);
58
+ }
59
+ }
60
+ function defer(scopes, waiting, source) {
61
+ const held = scopes.filter((scope) => scope.queued === "passive");
62
+ for (const scope of held) {
63
+ scope.queued = "deferred";
64
+ scope.probed = void 0;
65
+ }
66
+ const resume = () => {
67
+ for (const scope of held) if (scope.queued === "deferred") {
68
+ scope.queued = void 0;
69
+ transition(() => schedule(scope));
70
+ }
71
+ };
72
+ (source.blocking ||= []).push(resume);
73
+ waiting.then(resume, resume);
74
+ }
75
+ function unblock(scope) {
76
+ const { blocking } = scope;
77
+ scope.blocking = void 0;
78
+ blocking?.forEach((resume) => resume());
79
+ }
80
+ function release(holds) {
81
+ holds?.forEach((held) => held());
82
+ }
83
+ function settle(scope) {
84
+ const { holds } = scope;
85
+ scope.holds = void 0;
86
+ release(holds);
87
+ }
88
+ function schedule(scope) {
89
+ const held = pending();
90
+ if (held) (scope.holds ||= []).push(held);
91
+ scope.probed = void 0;
92
+ if (!depth) {
93
+ passive.delete(scope);
94
+ scope.queued = "urgent";
95
+ urgent.add(scope);
96
+ if (!urgentQueued) {
97
+ urgentQueued = true;
98
+ queueMicrotask(flushUrgent);
99
+ }
100
+ return;
101
+ }
102
+ if (scope.queued && scope.queued !== "deferred") return;
103
+ scope.queued = "passive";
104
+ passive.add(scope);
105
+ if (!passiveQueued) {
106
+ passiveQueued = true;
107
+ setTimeout(flushPassive);
108
+ }
109
+ }
110
+ function transition(work) {
111
+ depth++;
112
+ try {
113
+ work();
114
+ } finally {
115
+ depth--;
116
+ }
117
+ }
118
+ function claim(scope) {
119
+ urgent.delete(scope);
120
+ passive.delete(scope);
121
+ scope.queued = void 0;
122
+ scope.probed = void 0;
123
+ }
124
+ function unschedule(scope) {
125
+ urgent.delete(scope);
126
+ passive.delete(scope);
127
+ scope.queued = void 0;
128
+ scope.probed = void 0;
129
+ settle(scope);
130
+ unblock(scope);
131
+ }
132
+
133
+ //#endregion
134
+ //#region src/hot.ts
135
+ const FAMILIES = /* @__PURE__ */ new Map();
136
+ const FAMILY = /* @__PURE__ */ new WeakMap();
137
+ /** The latest implementation of a component registered for hot refresh. */
138
+ function latest(type) {
139
+ return FAMILY.get(type)?.current || type;
140
+ }
141
+ /** Whether two element types are one component, across hot edits. */
142
+ function same(a, b) {
143
+ if (a === b) return true;
144
+ const family = typeof a == "function" && FAMILY.get(a);
145
+ return !!family && family === FAMILY.get(b);
146
+ }
147
+ function track(type, scope) {
148
+ FAMILY.get(type)?.scopes.add(scope);
149
+ }
150
+ function untrack(type, scope) {
151
+ FAMILY.get(type)?.scopes.delete(scope);
152
+ }
153
+ /**
154
+ * Register a module's function components under stable ids. A component seen
155
+ * before takes its new implementation and re-renders where it is mounted.
156
+ */
157
+ function hot(id, components) {
158
+ for (const [name, type] of Object.entries(components)) {
159
+ if (typeof type != "function") continue;
160
+ const key = `${id}#${name}`;
161
+ let family = FAMILIES.get(key);
162
+ if (!family) FAMILIES.set(key, family = {
163
+ current: type,
164
+ scopes: /* @__PURE__ */ new Set()
165
+ });
166
+ FAMILY.set(type, family);
167
+ if (family.current === type) continue;
168
+ family.current = type;
169
+ for (const scope of family.scopes) {
170
+ scope.hot = true;
171
+ schedule(scope);
172
+ }
173
+ }
174
+ }
175
+
176
+ //#endregion
177
+ export { untrack as a, release as c, transition as d, unschedule as f, track as i, schedule as l, latest as n, afterFlush as o, same as r, claim as s, hot as t, settle as u };
178
+ //# sourceMappingURL=chunk-hot-gq8TyYJV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-hot-gq8TyYJV.js","names":[],"sources":["../src/scheduler.ts","../src/hot.ts"],"sourcesContent":["import { pending } from '@expressive/mvc';\n\ninterface Schedulable {\n queued?: 'urgent' | 'passive' | 'deferred';\n holds?: (() => void)[];\n probed?: { output: unknown };\n blocking?: (() => void)[];\n update(passive: boolean): PromiseLike<unknown> | void;\n probe?(): PromiseLike<unknown> | void;\n empty?(): boolean;\n}\n\nconst urgent = new Set<Schedulable>();\nconst passive = new Set<Schedulable>();\nconst after: (() => void)[] = [];\n\nlet depth = 0;\nlet urgentQueued = false;\nlet passiveQueued = false;\n\nfunction flush(scopes: Set<Schedulable>) {\n for (const scope of scopes) {\n scopes.delete(scope);\n scope.queued = undefined;\n\n let waiting: PromiseLike<unknown> | void = undefined;\n\n try {\n waiting = scope.update(false);\n } catch (error) {\n console.error(error);\n }\n\n settle(scope);\n if (!waiting) unblock(scope);\n }\n}\n\nfunction flushUrgent() {\n urgentQueued = false;\n flush(urgent);\n\n for (const run of after.splice(0)) run();\n}\n\nfunction afterFlush(run: () => void) {\n after.push(run);\n\n if (!urgentQueued) {\n urgentQueued = true;\n queueMicrotask(flushUrgent);\n }\n}\n\nfunction flushPassive() {\n passiveQueued = false;\n\n const batch = [...passive].filter((scope) => scope.queued === 'passive');\n passive.clear();\n\n for (const scope of batch) {\n const waiting = scope.probe?.();\n if (waiting) return defer(batch, waiting, scope);\n }\n\n batch.sort((a, b) => Number(!!b.empty?.()) - Number(!!a.empty?.()));\n\n for (let index = 0; index < batch.length; index++) {\n const scope = batch[index];\n\n if (scope.queued !== 'passive') continue;\n\n scope.queued = undefined;\n\n let waiting: PromiseLike<unknown> | void = undefined;\n\n try {\n waiting = scope.update(true);\n } catch (error) {\n console.error(error);\n }\n\n settle(scope);\n\n if (waiting) return defer(batch.slice(index + 1), waiting, scope);\n unblock(scope);\n }\n}\n\nfunction defer(scopes: Schedulable[], waiting: PromiseLike<unknown>, source: Schedulable) {\n const held = scopes.filter((scope) => scope.queued === 'passive');\n\n for (const scope of held) {\n scope.queued = 'deferred';\n scope.probed = undefined;\n }\n\n const resume = () => {\n for (const scope of held)\n if (scope.queued === 'deferred') {\n scope.queued = undefined;\n transition(() => schedule(scope));\n }\n };\n\n (source.blocking ||= []).push(resume);\n waiting.then(resume, resume);\n}\n\nfunction unblock(scope: Schedulable) {\n const { blocking } = scope;\n\n scope.blocking = undefined;\n blocking?.forEach((resume) => resume());\n}\n\nfunction release(holds?: (() => void)[]) {\n holds?.forEach((held) => held());\n}\n\nfunction settle(scope: Schedulable) {\n const { holds } = scope;\n\n scope.holds = undefined;\n release(holds);\n}\n\nfunction schedule(scope: Schedulable) {\n const held = pending();\n\n if (held) (scope.holds ||= []).push(held);\n scope.probed = undefined;\n\n if (!depth) {\n passive.delete(scope);\n scope.queued = 'urgent';\n urgent.add(scope);\n\n if (!urgentQueued) {\n urgentQueued = true;\n queueMicrotask(flushUrgent);\n }\n\n return;\n }\n\n if (scope.queued && scope.queued !== 'deferred') return;\n\n scope.queued = 'passive';\n passive.add(scope);\n\n if (!passiveQueued) {\n passiveQueued = true;\n setTimeout(flushPassive);\n }\n}\n\nfunction transition(work: () => void) {\n depth++;\n\n try {\n work();\n } finally {\n depth--;\n }\n}\n\nfunction claim(scope: Schedulable) {\n urgent.delete(scope);\n passive.delete(scope);\n scope.queued = undefined;\n scope.probed = undefined;\n}\n\nfunction unschedule(scope: Schedulable) {\n urgent.delete(scope);\n passive.delete(scope);\n scope.queued = undefined;\n scope.probed = undefined;\n settle(scope);\n unblock(scope);\n}\n\nexport { afterFlush, claim, release, schedule, settle, transition, unschedule };\nexport type { Schedulable };\n","import type { Scope } from './adapter';\nimport { schedule } from './scheduler';\n\ninterface Family {\n current: Function;\n scopes: Set<Scope>;\n}\n\nconst FAMILIES = new Map<string, Family>();\nconst FAMILY = new WeakMap<Function, Family>();\n\n/** The latest implementation of a component registered for hot refresh. */\nfunction latest(type: Function) {\n return FAMILY.get(type)?.current || type;\n}\n\n/** Whether two element types are one component, across hot edits. */\nfunction same(a: unknown, b: unknown) {\n if (a === b) return true;\n\n const family = typeof a == 'function' && FAMILY.get(a);\n\n return !!family && family === FAMILY.get(b as Function);\n}\n\nfunction track(type: Function, scope: Scope) {\n FAMILY.get(type)?.scopes.add(scope);\n}\n\nfunction untrack(type: Function, scope: Scope) {\n FAMILY.get(type)?.scopes.delete(scope);\n}\n\n/**\n * Register a module's function components under stable ids. A component seen\n * before takes its new implementation and re-renders where it is mounted.\n */\nfunction hot(id: string, components: Record<string, unknown>) {\n for (const [name, type] of Object.entries(components)) {\n if (typeof type != 'function') continue;\n\n const key = `${id}#${name}`;\n let family = FAMILIES.get(key);\n\n if (!family) FAMILIES.set(key, (family = { current: type, scopes: new Set() }));\n\n FAMILY.set(type, family);\n\n if (family.current === type) continue;\n\n family.current = type;\n\n for (const scope of family.scopes) {\n scope.hot = true;\n schedule(scope);\n }\n }\n}\n\nexport { hot, latest, same, track, untrack };\n"],"mappings":"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"}
@@ -22,4 +22,4 @@ declare function vnode<P>(type: VNode<P>['type'], props: P, key?: unknown): VNod
22
22
  declare function createPortal(children: Node, container: Element | DocumentFragment, key?: Key): VNode;
23
23
  //#endregion
24
24
  export { createPortal as a, VNode as i, FunctionComponent as n, vnode as o, Node as r, ComponentType as t };
25
- //# sourceMappingURL=chunk-vnode-DHwzzX6t.d.ts.map
25
+ //# sourceMappingURL=chunk-vnode-BsbDCqd1.d.ts.map
@@ -30,4 +30,4 @@ function createPortal(children, container, key) {
30
30
 
31
31
  //#endregion
32
32
  export { vnode as a, isVNode as i, childrenOf as n, createPortal as r, PORTAL as t };
33
- //# sourceMappingURL=chunk-vnode-DA7cLTKP.js.map
33
+ //# sourceMappingURL=chunk-vnode-CzmFSz5_.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chunk-vnode-DA7cLTKP.js","names":[],"sources":["../src/vnode.ts"],"sourcesContent":["import type { has, map } from '@expressive/mvc';\n\nconst VNODE = Symbol('@expressive/dom.vnode');\nconst PORTAL = Symbol('@expressive/dom.portal');\n\ntype Key = string | number | null | undefined;\ntype FunctionComponent<P = any> = (props: P) => Node;\ninterface ComponentInstance {\n readonly props: any;\n render(props?: any): Node;\n}\ntype ComponentType<P = any> = FunctionComponent<P> | (new (props: P) => ComponentInstance);\n\ntype Node =\n | VNode\n | ComponentInstance\n | string\n | number\n | bigint\n | boolean\n | null\n | undefined\n | readonly Node[]\n | has.List<Node>\n | has.Pool<Node, any, any>\n | map.Managed<unknown, Node>;\n\ninterface VNode<P = any> {\n readonly [VNODE]: true;\n readonly type: string | symbol | ComponentType<P>;\n readonly props: P & { children?: Node };\n readonly key?: Key;\n}\n\nfunction vnode<P>(type: VNode<P>['type'], props: P, key?: unknown): VNode<P> {\n return {\n [VNODE]: true,\n type,\n props: (props || {}) as VNode<P>['props'],\n key: key as Key\n };\n}\n\nfunction isVNode(value: unknown): value is VNode {\n return !!value && typeof value == 'object' && (value as VNode)[VNODE] === true;\n}\n\nfunction childrenOf(value: unknown): Node[] {\n const output: Node[] = [];\n\n function add(child: unknown) {\n if (Array.isArray(child)) child.forEach(add);\n else if (child !== null && child !== undefined && typeof child != 'boolean')\n output.push(child as Node);\n }\n\n add(value);\n return output;\n}\n\nfunction createPortal(children: Node, container: Element | DocumentFragment, key?: Key): VNode {\n return vnode(PORTAL, { children, container }, key);\n}\n\nexport {\n VNODE,\n PORTAL,\n childrenOf,\n createPortal,\n isVNode,\n vnode\n};\n\nexport type { ComponentInstance, ComponentType, FunctionComponent, Key, Node, VNode };\n"],"mappings":";AAEA,MAAM,QAAQ,OAAO,wBAAwB;AAC7C,MAAM,SAAS,OAAO,yBAAyB;AA+B/C,SAAS,MAAS,MAAwB,OAAU,KAAyB;AAC3E,QAAO;GACJ,QAAQ;EACT;EACA,OAAQ,SAAS,EAAE;EACd;EACN;;AAGH,SAAS,QAAQ,OAAgC;AAC/C,QAAO,CAAC,CAAC,SAAS,OAAO,SAAS,YAAa,MAAgB,WAAW;;AAG5E,SAAS,WAAW,OAAwB;CAC1C,MAAM,SAAiB,EAAE;CAEzB,SAAS,IAAI,OAAgB;AAC3B,MAAI,MAAM,QAAQ,MAAM,CAAE,OAAM,QAAQ,IAAI;WACnC,UAAU,QAAQ,UAAU,UAAa,OAAO,SAAS,UAChE,QAAO,KAAK,MAAc;;AAG9B,KAAI,MAAM;AACV,QAAO;;AAGT,SAAS,aAAa,UAAgB,WAAuC,KAAkB;AAC7F,QAAO,MAAM,QAAQ;EAAE;EAAU;EAAW,EAAE,IAAI"}
1
+ {"version":3,"file":"chunk-vnode-CzmFSz5_.js","names":[],"sources":["../src/vnode.ts"],"sourcesContent":["import type { has, map } from '@expressive/mvc';\n\nconst VNODE = Symbol('@expressive/dom.vnode');\nconst PORTAL = Symbol('@expressive/dom.portal');\n\ntype Key = string | number | null | undefined;\ntype FunctionComponent<P = any> = (props: P) => Node;\ninterface ComponentInstance {\n readonly props: any;\n render(props?: any): Node;\n}\ntype ComponentType<P = any> = FunctionComponent<P> | (new (props: P) => ComponentInstance);\n\ntype Node =\n | VNode\n | ComponentInstance\n | string\n | number\n | bigint\n | boolean\n | null\n | undefined\n | readonly Node[]\n | has.List<Node>\n | has.Pool<Node, any, any>\n | map.Managed<unknown, Node>;\n\ninterface VNode<P = any> {\n readonly [VNODE]: true;\n readonly type: string | symbol | ComponentType<P>;\n readonly props: P & { children?: Node };\n readonly key?: Key;\n}\n\nfunction vnode<P>(type: VNode<P>['type'], props: P, key?: unknown): VNode<P> {\n return {\n [VNODE]: true,\n type,\n props: (props || {}) as VNode<P>['props'],\n key: key as Key\n };\n}\n\nfunction isVNode(value: unknown): value is VNode {\n return !!value && typeof value == 'object' && (value as VNode)[VNODE] === true;\n}\n\nfunction childrenOf(value: unknown): Node[] {\n const output: Node[] = [];\n\n function add(child: unknown) {\n if (Array.isArray(child)) child.forEach(add);\n else if (child !== null && child !== undefined && typeof child != 'boolean')\n output.push(child as Node);\n }\n\n add(value);\n return output;\n}\n\nfunction createPortal(children: Node, container: Element | DocumentFragment, key?: Key): VNode {\n return vnode(PORTAL, { children, container }, key);\n}\n\nexport {\n VNODE,\n PORTAL,\n childrenOf,\n createPortal,\n isVNode,\n vnode\n};\n\nexport type { ComponentInstance, ComponentType, FunctionComponent, Key, Node, VNode };\n"],"mappings":";AAEA,MAAM,QAAQ,OAAO,wBAAwB;AAC7C,MAAM,SAAS,OAAO,yBAAyB;AA+B/C,SAAS,MAAS,MAAwB,OAAU,KAAyB;AAC3E,QAAO;GACJ,QAAQ;EACT;EACA,OAAQ,SAAS,EAAE;EACd;EACN;;AAGH,SAAS,QAAQ,OAAgC;AAC/C,QAAO,CAAC,CAAC,SAAS,OAAO,SAAS,YAAa,MAAgB,WAAW;;AAG5E,SAAS,WAAW,OAAwB;CAC1C,MAAM,SAAiB,EAAE;CAEzB,SAAS,IAAI,OAAgB;AAC3B,MAAI,MAAM,QAAQ,MAAM,CAAE,OAAM,QAAQ,IAAI;WACnC,UAAU,QAAQ,UAAU,UAAa,OAAO,SAAS,UAChE,QAAO,KAAK,MAAc;;AAG9B,KAAI,MAAM;AACV,QAAO;;AAGT,SAAS,aAAa,UAAgB,WAAuC,KAAkB;AAC7F,QAAO,MAAM,QAAQ;EAAE;EAAU;EAAW,EAAE,IAAI"}
package/dist/index.d.ts CHANGED
@@ -1,41 +1,6 @@
1
- import { a as createPortal, n as FunctionComponent, r as Node, t as ComponentType } from "./chunk-vnode-DHwzzX6t.js";
1
+ import { a as createPortal, n as FunctionComponent, r as Node, t as ComponentType } from "./chunk-vnode-BsbDCqd1.js";
2
2
  import { Component, Context, State } from "@expressive/mvc";
3
3
 
4
- //#region src/adapter.d.ts
5
- interface UseState extends State {
6
- use?(...props: any[]): Promise<void> | void;
7
- mount?(): (() => void) | void;
8
- }
9
- type NoVoid<T> = T extends undefined | void ? null : T;
10
- declare module '@expressive/mvc' {
11
- interface UseState extends State {
12
- use?(...props: any[]): Promise<void> | void;
13
- mount?(): (() => void) | void;
14
- }
15
- namespace State {
16
- type ForceRefresh = {
17
- (): void;
18
- <T = void>(waitFor: Promise<T>): Promise<T>;
19
- <T = void>(invoke: () => Promise<T>): Promise<T>;
20
- };
21
- type GetFactory<T extends State, R> = (this: T, current: T, refresh: ForceRefresh) => R;
22
- type UseArgs<T extends State> = T extends {
23
- use(...props: infer P): any;
24
- } ? P : State.Args<T>;
25
- function get<T extends State>(this: State.Extends<T>): T;
26
- function get<T extends State>(this: State.Extends<T>, required: false): T | undefined;
27
- function get<T extends State>(this: State.Extends<T>, required: true): Required<T>;
28
- function get<T extends State, R>(this: State.Extends<T>, factory: GetFactory<T, R>): NoVoid<R>;
29
- function use<T extends UseState>(this: State.Type<T>, ...args: UseArgs<T>): T;
30
- }
31
- namespace Component {
32
- const use: never;
33
- }
34
- interface Component {
35
- mount?(): (() => void) | void;
36
- }
37
- }
38
- //#endregion
39
4
  //#region src/context.d.ts
40
5
  declare namespace Consumer {
41
6
  interface Props<T extends State> {
@@ -128,7 +93,7 @@ declare const css: {
128
93
  height: any;
129
94
  };
130
95
  };
131
- declare module './stylesheet' {
96
+ declare module '@expressive/dom' {
132
97
  namespace macro {
133
98
  interface Registry {
134
99
  mx(value: style.Value): style.Map;
package/dist/index.js CHANGED
@@ -1,140 +1,10 @@
1
- import { a as vnode, i as isVNode, n as childrenOf, r as createPortal, t as PORTAL } from "./chunk-vnode-DA7cLTKP.js";
1
+ import { a as untrack, c as release, d as transition, f as unschedule, i as track, l as schedule, n as latest, o as afterFlush, r as same, s as claim$1, u as settle$1 } from "./chunk-hot-gq8TyYJV.js";
2
+ import { a as vnode, i as isVNode, n as childrenOf, r as createPortal, t as PORTAL } from "./chunk-vnode-CzmFSz5_.js";
2
3
  import "./jsx-runtime.js";
3
- import { Component, Context, State, has, map, pending, unbind } from "@expressive/mvc";
4
+ import { Component, Context, State, has, map, unbind } from "@expressive/mvc";
4
5
  import { watch } from "@expressive/mvc/observable";
5
6
  import { Fragment } from "@expressive/mvc/runtime";
6
7
 
7
- //#region src/scheduler.ts
8
- const urgent = /* @__PURE__ */ new Set();
9
- const passive = /* @__PURE__ */ new Set();
10
- const after = [];
11
- let depth$1 = 0;
12
- let urgentQueued = false;
13
- let passiveQueued = false;
14
- function flush(scopes) {
15
- for (const scope of scopes) {
16
- scopes.delete(scope);
17
- scope.queued = void 0;
18
- let waiting = void 0;
19
- try {
20
- waiting = scope.update(false);
21
- } catch (error) {
22
- console.error(error);
23
- }
24
- settle$1(scope);
25
- if (!waiting) unblock(scope);
26
- }
27
- }
28
- function flushUrgent() {
29
- urgentQueued = false;
30
- flush(urgent);
31
- for (const run of after.splice(0)) run();
32
- }
33
- function afterFlush(run) {
34
- after.push(run);
35
- if (!urgentQueued) {
36
- urgentQueued = true;
37
- queueMicrotask(flushUrgent);
38
- }
39
- }
40
- function flushPassive() {
41
- passiveQueued = false;
42
- const batch = [...passive].filter((scope) => scope.queued === "passive");
43
- passive.clear();
44
- for (const scope of batch) {
45
- const waiting = scope.probe?.();
46
- if (waiting) return defer(batch, waiting, scope);
47
- }
48
- batch.sort((a, b) => Number(!!b.empty?.()) - Number(!!a.empty?.()));
49
- for (let index = 0; index < batch.length; index++) {
50
- const scope = batch[index];
51
- if (scope.queued !== "passive") continue;
52
- scope.queued = void 0;
53
- let waiting = void 0;
54
- try {
55
- waiting = scope.update(true);
56
- } catch (error) {
57
- console.error(error);
58
- }
59
- settle$1(scope);
60
- if (waiting) return defer(batch.slice(index + 1), waiting, scope);
61
- unblock(scope);
62
- }
63
- }
64
- function defer(scopes, waiting, source) {
65
- const held = scopes.filter((scope) => scope.queued === "passive");
66
- for (const scope of held) {
67
- scope.queued = "deferred";
68
- scope.probed = void 0;
69
- }
70
- const resume = () => {
71
- for (const scope of held) if (scope.queued === "deferred") {
72
- scope.queued = void 0;
73
- transition(() => schedule(scope));
74
- }
75
- };
76
- (source.blocking ||= []).push(resume);
77
- waiting.then(resume, resume);
78
- }
79
- function unblock(scope) {
80
- const { blocking } = scope;
81
- scope.blocking = void 0;
82
- blocking?.forEach((resume) => resume());
83
- }
84
- function release(holds) {
85
- holds?.forEach((held) => held());
86
- }
87
- function settle$1(scope) {
88
- const { holds } = scope;
89
- scope.holds = void 0;
90
- release(holds);
91
- }
92
- function schedule(scope) {
93
- const held = pending();
94
- if (held) (scope.holds ||= []).push(held);
95
- scope.probed = void 0;
96
- if (!depth$1) {
97
- passive.delete(scope);
98
- scope.queued = "urgent";
99
- urgent.add(scope);
100
- if (!urgentQueued) {
101
- urgentQueued = true;
102
- queueMicrotask(flushUrgent);
103
- }
104
- return;
105
- }
106
- if (scope.queued && scope.queued !== "deferred") return;
107
- scope.queued = "passive";
108
- passive.add(scope);
109
- if (!passiveQueued) {
110
- passiveQueued = true;
111
- setTimeout(flushPassive);
112
- }
113
- }
114
- function transition(work) {
115
- depth$1++;
116
- try {
117
- work();
118
- } finally {
119
- depth$1--;
120
- }
121
- }
122
- function claim$1(scope) {
123
- urgent.delete(scope);
124
- passive.delete(scope);
125
- scope.queued = void 0;
126
- scope.probed = void 0;
127
- }
128
- function unschedule(scope) {
129
- urgent.delete(scope);
130
- passive.delete(scope);
131
- scope.queued = void 0;
132
- scope.probed = void 0;
133
- settle$1(scope);
134
- unblock(scope);
135
- }
136
-
137
- //#endregion
138
8
  //#region src/adapter.ts
139
9
  let current;
140
10
  let collecting;
@@ -148,9 +18,13 @@ function enter(scope, render) {
148
18
  scope.useIndex = 0;
149
19
  try {
150
20
  const output = render();
151
- if (scope.kind == "function" && scope.useIndex !== scope.uses.length) throw new Error("State.use() calls must keep the same order on every function-component render.");
21
+ if (scope.kind == "function" && scope.useIndex !== scope.uses.length) {
22
+ if (!scope.hot) throw new Error("State.use() calls must keep the same order on every function-component render.");
23
+ truncate(scope, scope.useIndex);
24
+ }
152
25
  return output;
153
26
  } finally {
27
+ scope.hot = void 0;
154
28
  current = parent;
155
29
  collecting = releases;
156
30
  scope.subscriptions.forEach((release) => release());
@@ -162,14 +36,18 @@ function dispose(scope) {
162
36
  unschedule(scope);
163
37
  scope.subscriptions.forEach((release) => release());
164
38
  scope.subscriptions = [];
165
- for (let i = scope.uses.length - 1; i >= 0; i--) {
39
+ truncate(scope, 0);
40
+ }
41
+ /** Destroy the `State.use()` slots of a scope from `index` on. */
42
+ function truncate(scope, index) {
43
+ for (let i = scope.uses.length - 1; i >= index; i--) {
166
44
  const slot = scope.uses[i];
167
45
  slot.release();
168
46
  if (typeof slot.cleanup == "function") slot.cleanup();
169
47
  slot.context.pop();
170
48
  slot.instance.set(null);
171
49
  }
172
- scope.uses = [];
50
+ scope.uses.length = index;
173
51
  }
174
52
  function commit(scope) {
175
53
  for (const slot of scope.uses) if (!slot.mounted) {
@@ -211,7 +89,11 @@ State.use = function use(...args) {
211
89
  if (scope.kind != "function") throw new Error("State.use() is only available at the top level of a function component.");
212
90
  const index = scope.useIndex++;
213
91
  let slot = scope.uses[index];
214
- if (slot && slot.Type !== this) throw new Error("State.use() calls must keep the same order on every function-component render.");
92
+ if (slot && slot.Type !== this) {
93
+ if (!scope.hot) throw new Error("State.use() calls must keep the same order on every function-component render.");
94
+ truncate(scope, index);
95
+ slot = void 0;
96
+ }
215
97
  if (!slot) {
216
98
  let instance;
217
99
  const assign = (value) => typeof value == "object" && value && instance.set(value);
@@ -257,16 +139,16 @@ Component.on({
257
139
  subcomponents(type.prototype);
258
140
  },
259
141
  before(self) {
260
- subcomponents(self, true);
142
+ subcomponents(self);
261
143
  }
262
144
  });
263
- function subcomponents(target, configurable) {
145
+ function subcomponents(target) {
264
146
  for (const key of Object.getOwnPropertyNames(target)) {
265
147
  if (!/^[A-Z]/.test(key)) continue;
266
148
  const { value } = Object.getOwnPropertyDescriptor(target, key);
267
149
  if (typeof value != "function") continue;
268
150
  Object.defineProperty(target, key, {
269
- configurable,
151
+ configurable: true,
270
152
  get() {
271
153
  const owner = this.is;
272
154
  let render = unbind(value);
@@ -287,7 +169,7 @@ function subcomponents(target, configurable) {
287
169
  enumerable: true,
288
170
  configurable: true
289
171
  });
290
- subcomponents(this, true);
172
+ subcomponents(this);
291
173
  }
292
174
  });
293
175
  }
@@ -457,7 +339,7 @@ const bases = /* @__PURE__ */ new WeakMap();
457
339
  const chains = /* @__PURE__ */ new WeakMap();
458
340
  const contexts = /* @__PURE__ */ new WeakMap();
459
341
  const rootScopes = /* @__PURE__ */ new WeakMap();
460
- const styles = /* @__PURE__ */ new WeakMap();
342
+ const STYLES = Symbol("@expressive/dom.styles");
461
343
  const entered = /* @__PURE__ */ new WeakSet();
462
344
  const globals = [];
463
345
  let globalContext;
@@ -546,14 +428,16 @@ function baseToken(scope) {
546
428
  }
547
429
  function style$1(type, rules) {
548
430
  if (entered.has(type)) throw new Error("Cannot add styles after a component has rendered.");
549
- const maps = styles.get(type);
550
- if (maps) maps.push(rules);
431
+ if (Object.hasOwn(type, STYLES)) type[STYLES].push(rules);
551
432
  else {
552
433
  const label = type.displayName || type.name;
553
- styles.set(type, [rules]);
434
+ Object.defineProperty(type, STYLES, {
435
+ configurable: true,
436
+ value: [rules]
437
+ });
554
438
  registerAppearance(type, (parent) => {
555
439
  entered.add(type);
556
- return extendContext(parent, styles.get(type), label);
440
+ return extendContext(parent, type[STYLES], label);
557
441
  });
558
442
  }
559
443
  return type;
@@ -904,7 +788,8 @@ function mountFunction(value, parent, before, context, boundary, appearance) {
904
788
  fiber.boundary = boundary;
905
789
  fiber.appearance = resolved.appearance;
906
790
  fiber.scope = makeScope("function", context, (passive) => runFunction(fiber, passive));
907
- fiber.render = () => enter(fiber.scope, () => fiber.type(fiber.props));
791
+ fiber.render = () => enter(fiber.scope, () => latest(fiber.type)(fiber.props));
792
+ track(fiber.type, fiber.scope);
908
793
  probing(fiber);
909
794
  return complete(fiber, () => {
910
795
  runFunction(fiber, passiveRender);
@@ -1349,6 +1234,12 @@ function patch(old, value, parent, before, context, boundary, appearance) {
1349
1234
  } else if (old.kind == "function") {
1350
1235
  const vnode = value;
1351
1236
  const resolved = componentProps(vnode.type, vnode.props, appearance);
1237
+ if (old.type !== vnode.type) {
1238
+ untrack(old.type, old.scope);
1239
+ old.type = vnode.type;
1240
+ old.scope.hot = true;
1241
+ track(old.type, old.scope);
1242
+ }
1352
1243
  old.appearance = resolved.appearance;
1353
1244
  old.props = observe(resolved.props);
1354
1245
  rerun(old, () => runFunction(old, passiveRender));
@@ -1393,7 +1284,7 @@ function compatible(fiber, value) {
1393
1284
  if (value instanceof Component) return fiber.kind == "component" && fiber.instance === value;
1394
1285
  if (isCollection(value)) return fiber.kind == "collection" && fiber.source === value;
1395
1286
  if (!isVNode(value)) return false;
1396
- if (fiber.key !== value.key || fiber.type !== value.type) return false;
1287
+ if (fiber.key !== value.key || !same(fiber.type, value.type)) return false;
1397
1288
  if (fiber.kind == "portal") return fiber.portalContainer === value.props.container;
1398
1289
  return true;
1399
1290
  }
@@ -1548,7 +1439,7 @@ function renderedAppearance(fiber) {
1548
1439
  style
1549
1440
  }));
1550
1441
  const inherited = fiber.appearance?.context || appearanceRoot();
1551
- const context = enterAppearance(inherited, fiber.type);
1442
+ const context = enterAppearance(inherited, latest(fiber.type));
1552
1443
  if (context?.base && context !== inherited) entries.push({
1553
1444
  hops: 0,
1554
1445
  style: context.base
@@ -1629,6 +1520,7 @@ function unmountFiber(fiber) {
1629
1520
  for (let index = fiber.children.length - 1; index >= 0; index--) unmountFiber(fiber.children[index]);
1630
1521
  if (fiber.placeholder) unmountFiber(fiber.placeholder);
1631
1522
  fiber.suspended?.forEach((clear) => clear());
1523
+ if (fiber.kind == "function") untrack(fiber.type, fiber.scope);
1632
1524
  if (fiber.scope) dispose(fiber.scope);
1633
1525
  fiber.release?.();
1634
1526
  if (fiber.kind == "element") {