@expressive/dom 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/{chunk-adapter-4jlvwsge.d.ts → chunk-adapter-i1piHw8T.d.ts} +14 -14
- package/dist/chunk-hot-9LhRiwFx.js +331 -0
- package/dist/chunk-hot-9LhRiwFx.js.map +1 -0
- package/dist/chunk-loader-CZ-53pyV.d.ts +58 -0
- package/dist/{chunk-vnode-CzmFSz5_.js → chunk-vnode-wpCWQ_Zg.js} +12 -9
- package/dist/chunk-vnode-wpCWQ_Zg.js.map +1 -0
- package/dist/index.d.ts +9 -34
- package/dist/index.js +831 -927
- package/dist/index.js.map +1 -1
- package/dist/jsx-dev-runtime.d.ts +3 -4
- package/dist/jsx-dev-runtime.js +3 -3
- package/dist/jsx-dev-runtime.js.map +1 -1
- package/dist/jsx-runtime.d.ts +44 -5
- package/dist/jsx-runtime.js +2 -2
- package/dist/jsx-runtime.js.map +1 -1
- package/dist/vite.js +45 -32
- package/dist/vite.js.map +1 -1
- package/package.json +6 -3
- package/dist/chunk-hot-gq8TyYJV.js +0 -178
- package/dist/chunk-hot-gq8TyYJV.js.map +0 -1
- package/dist/chunk-vnode-BsbDCqd1.d.ts +0 -25
- package/dist/chunk-vnode-CzmFSz5_.js.map +0 -1
package/dist/jsx-runtime.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { i as
|
|
2
|
-
import {
|
|
3
|
-
import { JSX } from "@expressive/mvc/jsx-runtime";
|
|
1
|
+
import { c as vnode, i as Node, n as Module, o as VNode, t as Attributes$1 } from "./chunk-loader-CZ-53pyV.js";
|
|
2
|
+
import { Component, Model } from "@expressive/mvc";
|
|
3
|
+
import { Fragment, JSX as JSX$1 } from "@expressive/mvc/jsx-runtime";
|
|
4
4
|
|
|
5
5
|
//#region src/jsx-runtime.d.ts
|
|
6
6
|
type Words = 'AnimationCancel' | 'AnimationEnd' | 'AnimationIteration' | 'AnimationStart' | 'AuxClick' | 'BeforeInput' | 'BeforeMatch' | 'BeforeToggle' | 'CanPlay' | 'CanPlayThrough' | 'CompositionEnd' | 'CompositionStart' | 'CompositionUpdate' | 'ContextLost' | 'ContextMenu' | 'ContextRestored' | 'CueChange' | 'DblClick' | 'DragEnd' | 'DragEnter' | 'DragLeave' | 'DragOver' | 'DragStart' | 'DurationChange' | 'FocusIn' | 'FocusOut' | 'FormData' | 'GotPointerCapture' | 'KeyDown' | 'KeyPress' | 'KeyUp' | 'LoadedData' | 'LoadedMetadata' | 'LoadStart' | 'LostPointerCapture' | 'MouseDown' | 'MouseEnter' | 'MouseLeave' | 'MouseMove' | 'MouseOut' | 'MouseOver' | 'MouseUp' | 'PointerCancel' | 'PointerDown' | 'PointerEnter' | 'PointerLeave' | 'PointerMove' | 'PointerOut' | 'PointerOver' | 'PointerRawUpdate' | 'PointerUp' | 'RateChange' | 'ScrollEnd' | 'SecurityPolicyViolation' | 'SelectionChange' | 'SelectStart' | 'SlotChange' | 'TimeUpdate' | 'TouchCancel' | 'TouchEnd' | 'TouchMove' | 'TouchStart' | 'TransitionCancel' | 'TransitionEnd' | 'TransitionRun' | 'TransitionStart' | 'VolumeChange';
|
|
@@ -36,10 +36,26 @@ type Attributes<T extends Element> = EventAttributes<T> & { [K in keyof T as K e
|
|
|
36
36
|
[attribute: `aria-${string}`]: unknown;
|
|
37
37
|
};
|
|
38
38
|
type Intrinsics = { [K in keyof HTMLElementTagNameMap]: Attributes<HTMLElementTagNameMap[K]> } & { [K in keyof SVGElementTagNameMap]: Attributes<SVGElementTagNameMap[K]> & SVGAttributes };
|
|
39
|
-
declare module '@expressive/mvc/runtime' {
|
|
39
|
+
declare module '@expressive/mvc/jsx-runtime' {
|
|
40
40
|
interface Host {
|
|
41
41
|
node: Node;
|
|
42
42
|
intrinsics: Intrinsics;
|
|
43
|
+
element: () => PromiseLike<Module>;
|
|
44
|
+
elementClass: Model;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
declare module '@expressive/mvc' {
|
|
48
|
+
namespace Model {
|
|
49
|
+
/** JSX attributes of a Model with no `props` member: its settable fields, `is`, a boundary, and what `render` accepts. */
|
|
50
|
+
type Props<T extends Model> = Component.ModelProps<T> & {
|
|
51
|
+
is?: (instance: T) => void; /** Shown while this element's content is suspended or recovering. `false` opts out. */
|
|
52
|
+
fallback?: Reserved<T, 'fallback', Component.Node | false>; /** Called when this element's content throws. A returned promise retries once settled. */
|
|
53
|
+
catch?: Reserved<T, 'catch', (error: Error, instance: T) => Promise<void> | void>;
|
|
54
|
+
} & Component.RenderProps<T extends {
|
|
55
|
+
render: infer R;
|
|
56
|
+
} ? R : never>;
|
|
57
|
+
/** Boundary attribute type, narrowed by a member of the same name so a mismatch fails at the element. */
|
|
58
|
+
type Reserved<T, K extends string, V> = K extends keyof T ? T[K] & V : V;
|
|
43
59
|
}
|
|
44
60
|
}
|
|
45
61
|
declare module '@expressive/mvc/jsx-runtime' {
|
|
@@ -52,6 +68,29 @@ declare module '@expressive/mvc/jsx-runtime' {
|
|
|
52
68
|
}
|
|
53
69
|
declare const jsx: typeof vnode;
|
|
54
70
|
declare const jsxs: typeof vnode;
|
|
71
|
+
declare namespace JSX {
|
|
72
|
+
type Element = JSX$1.Element;
|
|
73
|
+
type ElementType = keyof IntrinsicElements | ((props: any) => Component.Node) | (() => PromiseLike<Module>) | (abstract new (...args: any[]) => ElementClass);
|
|
74
|
+
/**
|
|
75
|
+
* Any Model renders as a class element - one with `render` produces content,
|
|
76
|
+
* one without passes children through and provides itself.
|
|
77
|
+
*/
|
|
78
|
+
interface ElementClass extends Model {
|
|
79
|
+
render?(props?: any): Component.Node;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Empty, so a class element's attributes resolve from its instance type and
|
|
83
|
+
* {@link LibraryManagedAttributes} picks `props` when declared, else derives
|
|
84
|
+
* them from the Model.
|
|
85
|
+
*/
|
|
86
|
+
interface ElementAttributesProperty {}
|
|
87
|
+
type LibraryManagedAttributes<C, P> = C extends (abstract new (...args: any[]) => infer I) ? P extends {
|
|
88
|
+
props: infer Q;
|
|
89
|
+
} ? Q : I extends Model ? Model.Props<I> : P : C extends (() => PromiseLike<Module<infer T>>) ? Attributes$1<T> : P;
|
|
90
|
+
interface ElementChildrenAttribute extends JSX$1.ElementChildrenAttribute {}
|
|
91
|
+
interface IntrinsicAttributes extends JSX$1.IntrinsicAttributes {}
|
|
92
|
+
type IntrinsicElements = JSX$1.IntrinsicElements;
|
|
93
|
+
}
|
|
55
94
|
//#endregion
|
|
56
|
-
export { Fragment,
|
|
95
|
+
export { Fragment, JSX, type VNode as JSXElement, jsx, jsxs };
|
|
57
96
|
//# sourceMappingURL=jsx-runtime.d.ts.map
|
package/dist/jsx-runtime.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Fragment, host } from "@expressive/mvc/runtime";
|
|
1
|
+
import { i as childrenOf, o as isVNode, s as vnode } from "./chunk-vnode-wpCWQ_Zg.js";
|
|
2
|
+
import { Fragment, host } from "@expressive/mvc/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/jsx-runtime.ts
|
|
5
5
|
host({
|
package/dist/jsx-runtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsx-runtime.js","names":[],"sources":["../src/jsx-runtime.ts"],"sourcesContent":["import { Fragment, host } from '@expressive/mvc/runtime';\n\nimport { childrenOf, isVNode, vnode } from './vnode';\nimport type { Node, VNode } from './vnode';\n\ntype Words =\n | 'AnimationCancel' | 'AnimationEnd' | 'AnimationIteration' | 'AnimationStart'\n | 'AuxClick' | 'BeforeInput' | 'BeforeMatch' | 'BeforeToggle' | 'CanPlay' | 'CanPlayThrough'\n | 'CompositionEnd' | 'CompositionStart' | 'CompositionUpdate'\n | 'ContextLost' | 'ContextMenu' | 'ContextRestored' | 'CueChange' | 'DblClick'\n | 'DragEnd' | 'DragEnter' | 'DragLeave' | 'DragOver' | 'DragStart' | 'DurationChange'\n | 'FocusIn' | 'FocusOut' | 'FormData' | 'GotPointerCapture' | 'KeyDown' | 'KeyPress' | 'KeyUp'\n | 'LoadedData' | 'LoadedMetadata' | 'LoadStart' | 'LostPointerCapture'\n | 'MouseDown' | 'MouseEnter' | 'MouseLeave' | 'MouseMove' | 'MouseOut' | 'MouseOver' | 'MouseUp'\n | 'PointerCancel' | 'PointerDown' | 'PointerEnter' | 'PointerLeave' | 'PointerMove'\n | 'PointerOut' | 'PointerOver' | 'PointerRawUpdate' | 'PointerUp'\n | 'RateChange' | 'ScrollEnd' | 'SecurityPolicyViolation' | 'SelectionChange' | 'SelectStart'\n | 'SlotChange' | 'TimeUpdate' | 'TouchCancel' | 'TouchEnd' | 'TouchMove' | 'TouchStart'\n | 'TransitionCancel' | 'TransitionEnd' | 'TransitionRun' | 'TransitionStart' | 'VolumeChange';\n\ntype Cased = { [W in Words as Lowercase<W>]: W };\ntype EventName<K> = K extends keyof Cased ? Cased[K] : Capitalize<K & string>;\n\ntype EventAttributes<T extends Element> = {\n [K in keyof GlobalEventHandlersEventMap as `on${EventName<K>}`]?:\n (event: GlobalEventHandlersEventMap[K] & { currentTarget: T }) => unknown;\n} & {\n [K in keyof GlobalEventHandlersEventMap as `on${EventName<K>}Capture`]?:\n (event: GlobalEventHandlersEventMap[K] & { currentTarget: T }) => unknown;\n};\n\ntype Declarations = Partial<Record<keyof CSSStyleDeclaration, string | number | null>> & {\n [property: `--${string}`]: string | number | null | undefined;\n};\ntype Style = string | Declarations | false | null | undefined | readonly Style[];\n\ntype SVGAttributes = {\n [K in\n | 'clipPathUnits' | 'cx' | 'cy' | 'd' | 'dx' | 'dy' | 'fill' | 'filter' | 'fx' | 'fy'\n | 'gradientTransform' | 'gradientUnits' | 'height' | 'href' | 'in' | 'in2' | 'mask'\n | 'markerHeight' | 'markerUnits' | 'markerWidth' | 'maskUnits' | 'offset' | 'opacity'\n | 'orient' | 'pathLength' | 'patternUnits' | 'points' | 'preserveAspectRatio' | 'r'\n | 'refX' | 'refY' | 'result' | 'rotate' | 'rx' | 'ry' | 'stdDeviation' | 'stroke'\n | 'textLength' | 'transform' | 'type' | 'values' | 'version' | 'viewBox' | 'width'\n | 'x' | 'x1' | 'x2' | 'xmlns' | 'y' | 'y1' | 'y2']?: string | number;\n};\n\ntype Attributes<T extends Element> = EventAttributes<T> & {\n [K in keyof T as K extends 'children' | 'className' | 'style'\n ? never\n : T[K] extends Function\n ? never\n : K]?: T[K] | string | number | boolean;\n} & {\n children?: Node;\n className?: string;\n autoFocus?: boolean;\n onDoubleClick?: (event: MouseEvent & { currentTarget: T }) => unknown;\n dangerouslySetInnerHTML?: { __html: string };\n key?: string | number | null;\n ref?: ((node: T | null) => unknown) | { current: T | null };\n style?: Style;\n [attribute: `_${string}`]: unknown;\n [attribute: `data-${string}`]: unknown;\n [attribute: `aria-${string}`]: unknown;\n};\n\ntype Intrinsics = {\n [K in keyof HTMLElementTagNameMap]: Attributes<HTMLElementTagNameMap[K]>;\n} & {\n [K in keyof SVGElementTagNameMap]: Attributes<SVGElementTagNameMap[K]> & SVGAttributes;\n};\n\ndeclare module '@expressive/mvc/runtime' {\n interface Host {\n node: Node;\n intrinsics: Intrinsics;\n }\n}\n\ndeclare module '@expressive/mvc/jsx-runtime' {\n namespace JSX {\n interface IntrinsicAttributes {\n style?: Style;\n [attribute: `_${string}`]: unknown;\n }\n }\n}\n\nhost({\n jsx: vnode,\n jsxs: vnode,\n Fragment,\n childrenOf,\n isElement: isVNode,\n typeOf(node: unknown) {\n return isVNode(node) ? node.type : undefined;\n },\n propsOf(node: unknown) {\n return isVNode(node) ? node.props : {};\n }\n});\n\nconst jsx = vnode;\nconst jsxs = vnode;\n\nexport { Fragment, jsx, jsxs };\nexport type { VNode as JSXElement };\nexport type {
|
|
1
|
+
{"version":3,"file":"jsx-runtime.js","names":[],"sources":["../src/jsx-runtime.ts"],"sourcesContent":["import { Fragment, host } from '@expressive/mvc/jsx-runtime';\nimport type { Component, Model } from '@expressive/mvc';\nimport type { JSX as Base } from '@expressive/mvc/jsx-runtime';\n\nimport { childrenOf, isVNode, vnode } from './node/vnode';\nimport type { Node, VNode } from './node/vnode';\nimport type { Attributes as Loaded, Module } from './node/loader';\n\ntype Words =\n | 'AnimationCancel' | 'AnimationEnd' | 'AnimationIteration' | 'AnimationStart'\n | 'AuxClick' | 'BeforeInput' | 'BeforeMatch' | 'BeforeToggle' | 'CanPlay' | 'CanPlayThrough'\n | 'CompositionEnd' | 'CompositionStart' | 'CompositionUpdate'\n | 'ContextLost' | 'ContextMenu' | 'ContextRestored' | 'CueChange' | 'DblClick'\n | 'DragEnd' | 'DragEnter' | 'DragLeave' | 'DragOver' | 'DragStart' | 'DurationChange'\n | 'FocusIn' | 'FocusOut' | 'FormData' | 'GotPointerCapture' | 'KeyDown' | 'KeyPress' | 'KeyUp'\n | 'LoadedData' | 'LoadedMetadata' | 'LoadStart' | 'LostPointerCapture'\n | 'MouseDown' | 'MouseEnter' | 'MouseLeave' | 'MouseMove' | 'MouseOut' | 'MouseOver' | 'MouseUp'\n | 'PointerCancel' | 'PointerDown' | 'PointerEnter' | 'PointerLeave' | 'PointerMove'\n | 'PointerOut' | 'PointerOver' | 'PointerRawUpdate' | 'PointerUp'\n | 'RateChange' | 'ScrollEnd' | 'SecurityPolicyViolation' | 'SelectionChange' | 'SelectStart'\n | 'SlotChange' | 'TimeUpdate' | 'TouchCancel' | 'TouchEnd' | 'TouchMove' | 'TouchStart'\n | 'TransitionCancel' | 'TransitionEnd' | 'TransitionRun' | 'TransitionStart' | 'VolumeChange';\n\ntype Cased = { [W in Words as Lowercase<W>]: W };\ntype EventName<K> = K extends keyof Cased ? Cased[K] : Capitalize<K & string>;\n\ntype EventAttributes<T extends Element> = {\n [K in keyof GlobalEventHandlersEventMap as `on${EventName<K>}`]?:\n (event: GlobalEventHandlersEventMap[K] & { currentTarget: T }) => unknown;\n} & {\n [K in keyof GlobalEventHandlersEventMap as `on${EventName<K>}Capture`]?:\n (event: GlobalEventHandlersEventMap[K] & { currentTarget: T }) => unknown;\n};\n\ntype Declarations = Partial<Record<keyof CSSStyleDeclaration, string | number | null>> & {\n [property: `--${string}`]: string | number | null | undefined;\n};\ntype Style = string | Declarations | false | null | undefined | readonly Style[];\n\ntype SVGAttributes = {\n [K in\n | 'clipPathUnits' | 'cx' | 'cy' | 'd' | 'dx' | 'dy' | 'fill' | 'filter' | 'fx' | 'fy'\n | 'gradientTransform' | 'gradientUnits' | 'height' | 'href' | 'in' | 'in2' | 'mask'\n | 'markerHeight' | 'markerUnits' | 'markerWidth' | 'maskUnits' | 'offset' | 'opacity'\n | 'orient' | 'pathLength' | 'patternUnits' | 'points' | 'preserveAspectRatio' | 'r'\n | 'refX' | 'refY' | 'result' | 'rotate' | 'rx' | 'ry' | 'stdDeviation' | 'stroke'\n | 'textLength' | 'transform' | 'type' | 'values' | 'version' | 'viewBox' | 'width'\n | 'x' | 'x1' | 'x2' | 'xmlns' | 'y' | 'y1' | 'y2']?: string | number;\n};\n\ntype Attributes<T extends Element> = EventAttributes<T> & {\n [K in keyof T as K extends 'children' | 'className' | 'style'\n ? never\n : T[K] extends Function\n ? never\n : K]?: T[K] | string | number | boolean;\n} & {\n children?: Node;\n className?: string;\n autoFocus?: boolean;\n onDoubleClick?: (event: MouseEvent & { currentTarget: T }) => unknown;\n dangerouslySetInnerHTML?: { __html: string };\n key?: string | number | null;\n ref?: ((node: T | null) => unknown) | { current: T | null };\n style?: Style;\n [attribute: `_${string}`]: unknown;\n [attribute: `data-${string}`]: unknown;\n [attribute: `aria-${string}`]: unknown;\n};\n\ntype Intrinsics = {\n [K in keyof HTMLElementTagNameMap]: Attributes<HTMLElementTagNameMap[K]>;\n} & {\n [K in keyof SVGElementTagNameMap]: Attributes<SVGElementTagNameMap[K]> & SVGAttributes;\n};\n\ndeclare module '@expressive/mvc/jsx-runtime' {\n interface Host {\n node: Node;\n intrinsics: Intrinsics;\n element: () => PromiseLike<Module>;\n elementClass: Model;\n }\n}\n\ndeclare module '@expressive/mvc' {\n namespace Model {\n /** JSX attributes of a Model with no `props` member: its settable fields, `is`, a boundary, and what `render` accepts. */\n type Props<T extends Model> =\n & Component.ModelProps<T>\n & {\n is?: (instance: T) => void;\n /** Shown while this element's content is suspended or recovering. `false` opts out. */\n fallback?: Reserved<T, 'fallback', Component.Node | false>;\n /** Called when this element's content throws. A returned promise retries once settled. */\n catch?: Reserved<T, 'catch', (error: Error, instance: T) => Promise<void> | void>;\n }\n & Component.RenderProps<T extends { render: infer R } ? R : never>;\n\n /** Boundary attribute type, narrowed by a member of the same name so a mismatch fails at the element. */\n type Reserved<T, K extends string, V> = K extends keyof T ? T[K] & V : V;\n }\n}\n\ndeclare module '@expressive/mvc/jsx-runtime' {\n namespace JSX {\n interface IntrinsicAttributes {\n style?: Style;\n [attribute: `_${string}`]: unknown;\n }\n }\n}\n\nhost({\n jsx: vnode,\n jsxs: vnode,\n Fragment,\n childrenOf,\n isElement: isVNode,\n typeOf(node: unknown) {\n return isVNode(node) ? node.type : undefined;\n },\n propsOf(node: unknown) {\n return isVNode(node) ? node.props : {};\n }\n});\n\nconst jsx = vnode;\nconst jsxs = vnode;\n\nexport { Fragment, jsx, jsxs };\nexport type { VNode as JSXElement };\nexport declare namespace JSX {\n type Element = Base.Element;\n type ElementType =\n | keyof IntrinsicElements\n | ((props: any) => Component.Node)\n | (() => PromiseLike<Module>)\n | (abstract new (...args: any[]) => ElementClass);\n /**\n * Any Model renders as a class element - one with `render` produces content,\n * one without passes children through and provides itself.\n */\n interface ElementClass extends Model { render?(props?: any): Component.Node }\n /**\n * Empty, so a class element's attributes resolve from its instance type and\n * {@link LibraryManagedAttributes} picks `props` when declared, else derives\n * them from the Model.\n */\n interface ElementAttributesProperty {}\n type LibraryManagedAttributes<C, P> =\n C extends abstract new (...args: any[]) => infer I\n ? P extends { props: infer Q } ? Q\n : I extends Model ? Model.Props<I>\n : P\n : C extends () => PromiseLike<Module<infer T>>\n ? Loaded<T>\n : P;\n interface ElementChildrenAttribute extends Base.ElementChildrenAttribute {}\n interface IntrinsicAttributes extends Base.IntrinsicAttributes {}\n type IntrinsicElements = Base.IntrinsicElements;\n}\n"],"mappings":";;;;AAiHA,KAAK;CACH,KAAK;CACL,MAAM;CACN;CACA;CACA,WAAW;CACX,OAAO,MAAe;AACpB,SAAO,QAAQ,KAAK,GAAG,KAAK,OAAO;;CAErC,QAAQ,MAAe;AACrB,SAAO,QAAQ,KAAK,GAAG,KAAK,QAAQ,EAAE;;CAEzC,CAAC;AAEF,MAAM,MAAM;AACZ,MAAM,OAAO"}
|
package/dist/vite.js
CHANGED
|
@@ -47,21 +47,8 @@ function scan(body) {
|
|
|
47
47
|
exports
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
* refresh. A State class it could not patch reloads the page; after an update,
|
|
53
|
-
* a changed component re-renders in place and any other changed export
|
|
54
|
-
* invalidates importers.
|
|
55
|
-
*/
|
|
56
|
-
function inject(id, body, ssr = false) {
|
|
57
|
-
const { classes, hidden, components, exports } = scan(body);
|
|
58
|
-
if (!classes.length && !components.length) return "";
|
|
59
|
-
const list = classes.join(", ");
|
|
60
|
-
const record = Object.entries(exports).map(([name, local]) => `${JSON.stringify(name)}: ${local}`);
|
|
61
|
-
const refresh = Object.keys(exports).filter((name) => components.includes(exports[name]));
|
|
62
|
-
const effect = ssr ? "replace it instead of patching" : "will trigger a full reload";
|
|
63
|
-
const notes = hidden.map((name) => `${JSON.stringify(name)}: ${JSON.stringify(`[expressive] ${name} (${id}) declares #private members, so edits to its module ${effect}. Use _ properties instead to keep HMR.`)}`);
|
|
64
|
-
const replace = ssr ? ` const __notes = { ${notes.join(", ")} };
|
|
50
|
+
const server = (notes, list) => `\
|
|
51
|
+
const __notes = ${JSON.stringify(notes)};
|
|
65
52
|
const __before = import.meta.hot.data.expressive;
|
|
66
53
|
const __classes = (import.meta.hot.data.expressive = { ${list} });
|
|
67
54
|
const __warned = (import.meta.hot.data.warned ||= {});
|
|
@@ -69,9 +56,10 @@ function inject(id, body, ssr = false) {
|
|
|
69
56
|
if (__before && __classes[key] !== __before[key] && !__warned[key]) {
|
|
70
57
|
__warned[key] = true;
|
|
71
58
|
console.warn(__notes[key]);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const
|
|
59
|
+
}`;
|
|
60
|
+
const browser = (id, notes, list) => `\
|
|
61
|
+
const __notes = ${JSON.stringify(notes)};
|
|
62
|
+
const __flag = (key) => 'expressive:private:' + ${id} + ':' + key;
|
|
75
63
|
for (const key in __notes)
|
|
76
64
|
try {
|
|
77
65
|
if (sessionStorage.getItem(__flag(key)) == 'due') {
|
|
@@ -82,7 +70,7 @@ function inject(id, body, ssr = false) {
|
|
|
82
70
|
const __before = import.meta.hot.data.expressive;
|
|
83
71
|
const __classes = (import.meta.hot.data.expressive = { ${list} });
|
|
84
72
|
for (const key in __before)
|
|
85
|
-
if (__classes[key] !== __before[key] && __classes[key]?.prototype instanceof
|
|
73
|
+
if (__classes[key] !== __before[key] && __classes[key]?.prototype instanceof __Model) {
|
|
86
74
|
const note = __notes[key];
|
|
87
75
|
if (note)
|
|
88
76
|
try {
|
|
@@ -90,27 +78,52 @@ function inject(id, body, ssr = false) {
|
|
|
90
78
|
} catch {
|
|
91
79
|
console.warn(note);
|
|
92
80
|
}
|
|
93
|
-
dispatchEvent(new CustomEvent('expressive:reload', { detail: { module: ${
|
|
81
|
+
dispatchEvent(new CustomEvent('expressive:reload', { detail: { module: ${id}, class: key, reason: note ? 'private members' : 'class changed shape' } }));
|
|
94
82
|
location.reload();
|
|
95
83
|
break;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
84
|
+
}`;
|
|
85
|
+
const patchClasses = (id, classes) => `\
|
|
86
|
+
{
|
|
87
|
+
const __hot = __accept(${id}, { ${classes.join(", ")} });
|
|
88
|
+
${classes.map((name) => `${name} = __hot.${name};`).join("\n ")}
|
|
89
|
+
}`;
|
|
90
|
+
const acceptUpdates = (replace, record, refresh) => `\
|
|
91
|
+
if (import.meta.hot) {
|
|
92
|
+
${replace}
|
|
93
|
+
const __exports = { ${record} };
|
|
103
94
|
import.meta.hot.accept((next) => {
|
|
104
95
|
if (!next) return;
|
|
105
96
|
for (const key in __exports) {
|
|
106
97
|
const after = next[key];
|
|
107
|
-
if (after !== __exports[key] && !(after?.prototype instanceof
|
|
98
|
+
if (after !== __exports[key] && !(after?.prototype instanceof __Model) && !${JSON.stringify(refresh)}.includes(key))
|
|
108
99
|
return import.meta.hot.invalidate(\`"\${key}" export cannot be hot-patched.\`);
|
|
109
100
|
}
|
|
110
101
|
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
102
|
+
}`;
|
|
103
|
+
/**
|
|
104
|
+
* Bind a module's classes to `hot.accept` and its function components to dom's
|
|
105
|
+
* refresh. A Model class it could not patch reloads the page; after an update,
|
|
106
|
+
* a changed component re-renders in place and any other changed export
|
|
107
|
+
* invalidates importers.
|
|
108
|
+
*/
|
|
109
|
+
function inject(id, body, ssr = false) {
|
|
110
|
+
const { classes, hidden, components, exports } = scan(body);
|
|
111
|
+
if (!classes.length && !components.length) return "";
|
|
112
|
+
const key = JSON.stringify(id);
|
|
113
|
+
const list = classes.join(", ");
|
|
114
|
+
const record = Object.entries(exports).map(([name, local]) => `${JSON.stringify(name)}: ${local}`);
|
|
115
|
+
const refresh = Object.keys(exports).filter((name) => components.includes(exports[name]));
|
|
116
|
+
const effect = ssr ? "replace it instead of patching" : "will trigger a full reload";
|
|
117
|
+
const notes = Object.fromEntries(hidden.map((name) => [name, `[expressive] ${name} (${id}) declares #private members, so edits to its module ${effect}. Use _ properties instead to keep HMR.`]));
|
|
118
|
+
const replace = ssr ? server(notes, list) : browser(key, notes, list);
|
|
119
|
+
return `\n${[
|
|
120
|
+
`import { Model as __Model } from '@expressive/mvc';`,
|
|
121
|
+
classes.length && `import { accept as __accept } from '@expressive/mvc/hot';`,
|
|
122
|
+
components.length && `import { hot as __refresh } from '@expressive/dom/jsx-dev-runtime';`,
|
|
123
|
+
classes.length && patchClasses(key, classes),
|
|
124
|
+
components.length && `__refresh(${key}, { ${components.join(", ")} });`,
|
|
125
|
+
acceptUpdates(replace, record.join(", "), refresh)
|
|
126
|
+
].filter(Boolean).join("\n")}\n`;
|
|
114
127
|
}
|
|
115
128
|
/** Hot-patch classes and refresh function components in place during `vite` dev. */
|
|
116
129
|
function expressive() {
|
|
@@ -126,7 +139,7 @@ function expressive() {
|
|
|
126
139
|
async transform(code, id, options) {
|
|
127
140
|
const [file] = id.split("?");
|
|
128
141
|
if (!SOURCE.test(file) || file.includes("/node_modules/")) return;
|
|
129
|
-
runtime ||= Promise.all(["@expressive/mvc/
|
|
142
|
+
runtime ||= Promise.all(["@expressive/mvc/hot", "@expressive/dom/jsx-dev-runtime"].map((entry) => this.resolve(entry).then((found) => found && dirname(found.id))));
|
|
130
143
|
for (const own of await runtime) if (own && file.startsWith(own)) return;
|
|
131
144
|
const output = inject(file.startsWith(root + "/") ? file.slice(root.length) : file, this.parse(code).body, options?.ssr);
|
|
132
145
|
if (output) return {
|
package/dist/vite.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.js","names":[],"sources":["../src/vite.ts"],"sourcesContent":["import { dirname } from 'node:path';\nimport type { Plugin } from 'vite';\n\ntype Node = { type: string; [key: string]: any };\n\nconst SOURCE = /\\.[cm]?[jt]sx?$/;\n\nconst COMPONENT = /^[A-Z]/;\nconst FUNCTION = ['ArrowFunctionExpression', 'FunctionExpression'];\n\n/**\n * Top-level classes a module can reassign, those declaring private members,\n * function components by name, and its exports by local binding.\n */\nfunction scan(body: Node[]) {\n const classes: string[] = [];\n const hidden: string[] = [];\n const components: string[] = [];\n const exports: Record<string, string> = {};\n\n function add(name: string, node: Node) {\n classes.push(name);\n\n if (node.body.body.some((member: Node) => member.key?.type == 'PrivateIdentifier')) hidden.push(name);\n }\n\n function collect(node: Node) {\n if (node.type == 'ClassDeclaration' && node.id) add(node.id.name, node);\n else if (node.type == 'FunctionDeclaration' && COMPONENT.test(node.id?.name)) components.push(node.id.name);\n else if (node.type == 'VariableDeclaration')\n for (const { id, init } of node.declarations) {\n if (id.type != 'Identifier') continue;\n if (init?.type == 'ClassExpression' && node.kind != 'const') add(id.name, init);\n else if (FUNCTION.includes(init?.type) && COMPONENT.test(id.name)) components.push(id.name);\n }\n }\n\n function names(node: Node): string[] {\n if (node.type == 'VariableDeclaration')\n return node.declarations.flatMap(({ id }: Node) => (id.type == 'Identifier' ? [id.name] : []));\n\n return [node.id.name];\n }\n\n for (const node of body) {\n if (node.type == 'ExportNamedDeclaration') {\n if (node.declaration) {\n collect(node.declaration);\n for (const name of names(node.declaration)) exports[name] = name;\n } else if (!node.source)\n for (const { local, exported } of node.specifiers)\n exports[exported.name ?? exported.value] = local.name;\n } else if (node.type == 'ExportDefaultDeclaration') {\n const { declaration } = node;\n\n collect(declaration);\n\n if (declaration.id) exports.default = declaration.id.name;\n } else collect(node);\n }\n\n return { classes, hidden, components, exports };\n}\n\n/**\n * Bind a module's classes to `hot.accept` and its function components to dom's\n * refresh. A State class it could not patch reloads the page; after an update,\n * a changed component re-renders in place and any other changed export\n * invalidates importers.\n */\nfunction inject(id: string, body: Node[], ssr = false) {\n const { classes, hidden, components, exports } = scan(body);\n\n if (!classes.length && !components.length) return '';\n\n const list = classes.join(', ');\n const record = Object.entries(exports).map(([name, local]) => `${JSON.stringify(name)}: ${local}`);\n const refresh = Object.keys(exports).filter((name) => components.includes(exports[name]));\n const effect = ssr ? 'replace it instead of patching' : 'will trigger a full reload';\n const notes = hidden.map(\n (name) =>\n `${JSON.stringify(name)}: ${JSON.stringify(`[expressive] ${name} (${id}) declares #private members, so edits to its module ${effect}. Use _ properties instead to keep HMR.`)}`\n );\n const replace = ssr\n ? ` const __notes = { ${notes.join(', ')} };\n const __before = import.meta.hot.data.expressive;\n const __classes = (import.meta.hot.data.expressive = { ${list} });\n const __warned = (import.meta.hot.data.warned ||= {});\n for (const key in __notes)\n if (__before && __classes[key] !== __before[key] && !__warned[key]) {\n __warned[key] = true;\n console.warn(__notes[key]);\n }\n`\n : ` const __notes = { ${notes.join(', ')} };\n const __flag = (key) => 'expressive:private:' + ${JSON.stringify(id)} + ':' + key;\n for (const key in __notes)\n try {\n if (sessionStorage.getItem(__flag(key)) == 'due') {\n console.warn(__notes[key]);\n sessionStorage.setItem(__flag(key), 'shown');\n }\n } catch {}\n const __before = import.meta.hot.data.expressive;\n const __classes = (import.meta.hot.data.expressive = { ${list} });\n for (const key in __before)\n if (__classes[key] !== __before[key] && __classes[key]?.prototype instanceof __State) {\n const note = __notes[key];\n if (note)\n try {\n if (!sessionStorage.getItem(__flag(key))) sessionStorage.setItem(__flag(key), 'due');\n } catch {\n console.warn(note);\n }\n dispatchEvent(new CustomEvent('expressive:reload', { detail: { module: ${JSON.stringify(id)}, class: key, reason: note ? 'private members' : 'class changed shape' } }));\n location.reload();\n break;\n }\n`;\n const lines = ['', `import { State as __State } from '@expressive/mvc';`];\n\n if (classes.length)\n lines.push(\n `import { hot as __expressive } from '@expressive/mvc/runtime';`,\n '{',\n ` const __hot = __expressive.accept(${JSON.stringify(id)}, { ${list} });`,\n ...classes.map((name) => ` ${name} = __hot.${name};`),\n '}'\n );\n\n if (components.length)\n lines.push(\n `import { hot as __refresh } from '@expressive/dom/jsx-dev-runtime';`,\n `__refresh(${JSON.stringify(id)}, { ${components.join(', ')} });`\n );\n\n lines.push(`if (import.meta.hot) {\n${replace} const __exports = { ${record.join(', ')} };\n import.meta.hot.accept((next) => {\n if (!next) return;\n for (const key in __exports) {\n const after = next[key];\n if (after !== __exports[key] && !(after?.prototype instanceof __State) && !${JSON.stringify(refresh)}.includes(key))\n return import.meta.hot.invalidate(\\`\"\\${key}\" export cannot be hot-patched.\\`);\n }\n });\n}\n`);\n\n return lines.join('\\n');\n}\n\n/** Hot-patch classes and refresh function components in place during `vite` dev. */\nexport default function expressive(): Plugin {\n let root = '';\n let runtime: Promise<(string | null | undefined)[]> | undefined;\n\n return {\n name: '@expressive/dom:hot',\n apply: 'serve',\n enforce: 'post',\n configResolved(config) {\n root = config.root;\n },\n async transform(code, id, options) {\n const [file] = id.split('?');\n\n if (!SOURCE.test(file) || file.includes('/node_modules/')) return;\n\n runtime ||= Promise.all(\n ['@expressive/mvc/runtime', '@expressive/dom/jsx-dev-runtime'].map((entry) =>\n this.resolve(entry).then((found) => found && dirname(found.id))\n )\n );\n\n for (const own of await runtime) if (own && file.startsWith(own)) return;\n\n const output = inject(file.startsWith(root + '/') ? file.slice(root.length) : file, this.parse(code).body as Node[], options?.ssr);\n\n if (output) return { code: code + output, map: null };\n }\n };\n}\n"],"mappings":";;;AAKA,MAAM,SAAS;AAEf,MAAM,YAAY;AAClB,MAAM,WAAW,CAAC,2BAA2B,qBAAqB;;;;;AAMlE,SAAS,KAAK,MAAc;CAC1B,MAAM,UAAoB,EAAE;CAC5B,MAAM,SAAmB,EAAE;CAC3B,MAAM,aAAuB,EAAE;CAC/B,MAAM,UAAkC,EAAE;CAE1C,SAAS,IAAI,MAAc,MAAY;AACrC,UAAQ,KAAK,KAAK;AAElB,MAAI,KAAK,KAAK,KAAK,MAAM,WAAiB,OAAO,KAAK,QAAQ,oBAAoB,CAAE,QAAO,KAAK,KAAK;;CAGvG,SAAS,QAAQ,MAAY;AAC3B,MAAI,KAAK,QAAQ,sBAAsB,KAAK,GAAI,KAAI,KAAK,GAAG,MAAM,KAAK;WAC9D,KAAK,QAAQ,yBAAyB,UAAU,KAAK,KAAK,IAAI,KAAK,CAAE,YAAW,KAAK,KAAK,GAAG,KAAK;WAClG,KAAK,QAAQ,sBACpB,MAAK,MAAM,EAAE,IAAI,UAAU,KAAK,cAAc;AAC5C,OAAI,GAAG,QAAQ,aAAc;AAC7B,OAAI,MAAM,QAAQ,qBAAqB,KAAK,QAAQ,QAAS,KAAI,GAAG,MAAM,KAAK;YACtE,SAAS,SAAS,MAAM,KAAK,IAAI,UAAU,KAAK,GAAG,KAAK,CAAE,YAAW,KAAK,GAAG,KAAK;;;CAIjG,SAAS,MAAM,MAAsB;AACnC,MAAI,KAAK,QAAQ,sBACf,QAAO,KAAK,aAAa,SAAS,EAAE,SAAgB,GAAG,QAAQ,eAAe,CAAC,GAAG,KAAK,GAAG,EAAE,CAAE;AAEhG,SAAO,CAAC,KAAK,GAAG,KAAK;;AAGvB,MAAK,MAAM,QAAQ,KACjB,KAAI,KAAK,QAAQ,0BACf;MAAI,KAAK,aAAa;AACpB,WAAQ,KAAK,YAAY;AACzB,QAAK,MAAM,QAAQ,MAAM,KAAK,YAAY,CAAE,SAAQ,QAAQ;aACnD,CAAC,KAAK,OACf,MAAK,MAAM,EAAE,OAAO,cAAc,KAAK,WACrC,SAAQ,SAAS,QAAQ,SAAS,SAAS,MAAM;YAC5C,KAAK,QAAQ,4BAA4B;EAClD,MAAM,EAAE,gBAAgB;AAExB,UAAQ,YAAY;AAEpB,MAAI,YAAY,GAAI,SAAQ,UAAU,YAAY,GAAG;OAChD,SAAQ,KAAK;AAGtB,QAAO;EAAE;EAAS;EAAQ;EAAY;EAAS;;;;;;;;AASjD,SAAS,OAAO,IAAY,MAAc,MAAM,OAAO;CACrD,MAAM,EAAE,SAAS,QAAQ,YAAY,YAAY,KAAK,KAAK;AAE3D,KAAI,CAAC,QAAQ,UAAU,CAAC,WAAW,OAAQ,QAAO;CAElD,MAAM,OAAO,QAAQ,KAAK,KAAK;CAC/B,MAAM,SAAS,OAAO,QAAQ,QAAQ,CAAC,KAAK,CAAC,MAAM,WAAW,GAAG,KAAK,UAAU,KAAK,CAAC,IAAI,QAAQ;CAClG,MAAM,UAAU,OAAO,KAAK,QAAQ,CAAC,QAAQ,SAAS,WAAW,SAAS,QAAQ,MAAM,CAAC;CACzF,MAAM,SAAS,MAAM,mCAAmC;CACxD,MAAM,QAAQ,OAAO,KAClB,SACC,GAAG,KAAK,UAAU,KAAK,CAAC,IAAI,KAAK,UAAU,gBAAgB,KAAK,IAAI,GAAG,sDAAsD,OAAO,yCAAyC,GAChL;CACD,MAAM,UAAU,MACZ,uBAAuB,MAAM,KAAK,KAAK,CAAC;;2DAEa,KAAK;;;;;;;IAQ1D,uBAAuB,MAAM,KAAK,KAAK,CAAC;oDACM,KAAK,UAAU,GAAG,CAAC;;;;;;;;;2DASZ,KAAK;;;;;;;;;;+EAUe,KAAK,UAAU,GAAG,CAAC;;;;;CAKhG,MAAM,QAAQ,CAAC,IAAI,sDAAsD;AAEzE,KAAI,QAAQ,OACV,OAAM,KACJ,kEACA,KACA,uCAAuC,KAAK,UAAU,GAAG,CAAC,MAAM,KAAK,OACrE,GAAG,QAAQ,KAAK,SAAS,KAAK,KAAK,WAAW,KAAK,GAAG,EACtD,IACD;AAEH,KAAI,WAAW,OACb,OAAM,KACJ,uEACA,aAAa,KAAK,UAAU,GAAG,CAAC,MAAM,WAAW,KAAK,KAAK,CAAC,MAC7D;AAEH,OAAM,KAAK;EACX,QAAQ,wBAAwB,OAAO,KAAK,KAAK,CAAC;;;;;mFAK+B,KAAK,UAAU,QAAQ,CAAC;;;;;EAKzG;AAEA,QAAO,MAAM,KAAK,KAAK;;;AAIzB,SAAwB,aAAqB;CAC3C,IAAI,OAAO;CACX,IAAI;AAEJ,QAAO;EACL,MAAM;EACN,OAAO;EACP,SAAS;EACT,eAAe,QAAQ;AACrB,UAAO,OAAO;;EAEhB,MAAM,UAAU,MAAM,IAAI,SAAS;GACjC,MAAM,CAAC,QAAQ,GAAG,MAAM,IAAI;AAE5B,OAAI,CAAC,OAAO,KAAK,KAAK,IAAI,KAAK,SAAS,iBAAiB,CAAE;AAE3D,eAAY,QAAQ,IAClB,CAAC,2BAA2B,kCAAkC,CAAC,KAAK,UAClE,KAAK,QAAQ,MAAM,CAAC,MAAM,UAAU,SAAS,QAAQ,MAAM,GAAG,CAAC,CAChE,CACF;AAED,QAAK,MAAM,OAAO,MAAM,QAAS,KAAI,OAAO,KAAK,WAAW,IAAI,CAAE;GAElE,MAAM,SAAS,OAAO,KAAK,WAAW,OAAO,IAAI,GAAG,KAAK,MAAM,KAAK,OAAO,GAAG,MAAM,KAAK,MAAM,KAAK,CAAC,MAAgB,SAAS,IAAI;AAElI,OAAI,OAAQ,QAAO;IAAE,MAAM,OAAO;IAAQ,KAAK;IAAM;;EAExD"}
|
|
1
|
+
{"version":3,"file":"vite.js","names":[],"sources":["../src/vite.ts"],"sourcesContent":["import { dirname } from 'node:path';\nimport type { Plugin } from 'vite';\n\ntype Node = { type: string; [key: string]: any };\n\nconst SOURCE = /\\.[cm]?[jt]sx?$/;\n\nconst COMPONENT = /^[A-Z]/;\nconst FUNCTION = ['ArrowFunctionExpression', 'FunctionExpression'];\n\n/**\n * Top-level classes a module can reassign, those declaring private members,\n * function components by name, and its exports by local binding.\n */\nfunction scan(body: Node[]) {\n const classes: string[] = [];\n const hidden: string[] = [];\n const components: string[] = [];\n const exports: Record<string, string> = {};\n\n function add(name: string, node: Node) {\n classes.push(name);\n\n if (node.body.body.some((member: Node) => member.key?.type == 'PrivateIdentifier')) hidden.push(name);\n }\n\n function collect(node: Node) {\n if (node.type == 'ClassDeclaration' && node.id) add(node.id.name, node);\n else if (node.type == 'FunctionDeclaration' && COMPONENT.test(node.id?.name)) components.push(node.id.name);\n else if (node.type == 'VariableDeclaration')\n for (const { id, init } of node.declarations) {\n if (id.type != 'Identifier') continue;\n if (init?.type == 'ClassExpression' && node.kind != 'const') add(id.name, init);\n else if (FUNCTION.includes(init?.type) && COMPONENT.test(id.name)) components.push(id.name);\n }\n }\n\n function names(node: Node): string[] {\n if (node.type == 'VariableDeclaration')\n return node.declarations.flatMap(({ id }: Node) => (id.type == 'Identifier' ? [id.name] : []));\n\n return [node.id.name];\n }\n\n for (const node of body) {\n if (node.type == 'ExportNamedDeclaration') {\n if (node.declaration) {\n collect(node.declaration);\n for (const name of names(node.declaration)) exports[name] = name;\n } else if (!node.source)\n for (const { local, exported } of node.specifiers)\n exports[exported.name ?? exported.value] = local.name;\n } else if (node.type == 'ExportDefaultDeclaration') {\n const { declaration } = node;\n\n collect(declaration);\n\n if (declaration.id) exports.default = declaration.id.name;\n } else collect(node);\n }\n\n return { classes, hidden, components, exports };\n}\n\nconst server = (notes: Record<string, string>, list: string) => `\\\n const __notes = ${JSON.stringify(notes)};\n const __before = import.meta.hot.data.expressive;\n const __classes = (import.meta.hot.data.expressive = { ${list} });\n const __warned = (import.meta.hot.data.warned ||= {});\n for (const key in __notes)\n if (__before && __classes[key] !== __before[key] && !__warned[key]) {\n __warned[key] = true;\n console.warn(__notes[key]);\n }`;\n\nconst browser = (id: string, notes: Record<string, string>, list: string) => `\\\n const __notes = ${JSON.stringify(notes)};\n const __flag = (key) => 'expressive:private:' + ${id} + ':' + key;\n for (const key in __notes)\n try {\n if (sessionStorage.getItem(__flag(key)) == 'due') {\n console.warn(__notes[key]);\n sessionStorage.setItem(__flag(key), 'shown');\n }\n } catch {}\n const __before = import.meta.hot.data.expressive;\n const __classes = (import.meta.hot.data.expressive = { ${list} });\n for (const key in __before)\n if (__classes[key] !== __before[key] && __classes[key]?.prototype instanceof __Model) {\n const note = __notes[key];\n if (note)\n try {\n if (!sessionStorage.getItem(__flag(key))) sessionStorage.setItem(__flag(key), 'due');\n } catch {\n console.warn(note);\n }\n dispatchEvent(new CustomEvent('expressive:reload', { detail: { module: ${id}, class: key, reason: note ? 'private members' : 'class changed shape' } }));\n location.reload();\n break;\n }`;\n\nconst patchClasses = (id: string, classes: string[]) => `\\\n{\n const __hot = __accept(${id}, { ${classes.join(', ')} });\n ${classes.map((name) => `${name} = __hot.${name};`).join('\\n ')}\n}`;\n\nconst acceptUpdates = (replace: string, record: string, refresh: string[]) => `\\\nif (import.meta.hot) {\n${replace}\n const __exports = { ${record} };\n import.meta.hot.accept((next) => {\n if (!next) return;\n for (const key in __exports) {\n const after = next[key];\n if (after !== __exports[key] && !(after?.prototype instanceof __Model) && !${JSON.stringify(refresh)}.includes(key))\n return import.meta.hot.invalidate(\\`\"\\${key}\" export cannot be hot-patched.\\`);\n }\n });\n}`;\n\n/**\n * Bind a module's classes to `hot.accept` and its function components to dom's\n * refresh. A Model class it could not patch reloads the page; after an update,\n * a changed component re-renders in place and any other changed export\n * invalidates importers.\n */\nfunction inject(id: string, body: Node[], ssr = false) {\n const { classes, hidden, components, exports } = scan(body);\n\n if (!classes.length && !components.length) return '';\n\n const key = JSON.stringify(id);\n const list = classes.join(', ');\n const record = Object.entries(exports).map(([name, local]) => `${JSON.stringify(name)}: ${local}`);\n const refresh = Object.keys(exports).filter((name) => components.includes(exports[name]));\n const effect = ssr ? 'replace it instead of patching' : 'will trigger a full reload';\n const notes = Object.fromEntries(\n hidden.map((name) => [name, `[expressive] ${name} (${id}) declares #private members, so edits to its module ${effect}. Use _ properties instead to keep HMR.`])\n );\n const replace = ssr ? server(notes, list) : browser(key, notes, list);\n const lines = [\n `import { Model as __Model } from '@expressive/mvc';`,\n classes.length && `import { accept as __accept } from '@expressive/mvc/hot';`,\n components.length && `import { hot as __refresh } from '@expressive/dom/jsx-dev-runtime';`,\n classes.length && patchClasses(key, classes),\n components.length && `__refresh(${key}, { ${components.join(', ')} });`,\n acceptUpdates(replace, record.join(', '), refresh)\n ];\n\n return `\\n${lines.filter(Boolean).join('\\n')}\\n`;\n}\n\n/** Hot-patch classes and refresh function components in place during `vite` dev. */\nexport default function expressive(): Plugin {\n let root = '';\n let runtime: Promise<(string | null | undefined)[]> | undefined;\n\n return {\n name: '@expressive/dom:hot',\n apply: 'serve',\n enforce: 'post',\n configResolved(config) {\n root = config.root;\n },\n async transform(code, id, options) {\n const [file] = id.split('?');\n\n if (!SOURCE.test(file) || file.includes('/node_modules/')) return;\n\n runtime ||= Promise.all(\n ['@expressive/mvc/hot', '@expressive/dom/jsx-dev-runtime'].map((entry) =>\n this.resolve(entry).then((found) => found && dirname(found.id))\n )\n );\n\n for (const own of await runtime) if (own && file.startsWith(own)) return;\n\n const output = inject(file.startsWith(root + '/') ? file.slice(root.length) : file, this.parse(code).body as Node[], options?.ssr);\n\n if (output) return { code: code + output, map: null };\n }\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expressive/dom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "MVC-native DOM renderer.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"type": "module",
|
|
19
19
|
"sideEffects": [
|
|
20
20
|
"./src/*.ts",
|
|
21
|
+
"./src/render/adapter.ts",
|
|
21
22
|
"./dist/index.js",
|
|
22
23
|
"./dist/jsx-runtime.js",
|
|
23
24
|
"./dist/jsx-dev-runtime.js"
|
|
@@ -50,10 +51,12 @@
|
|
|
50
51
|
},
|
|
51
52
|
"scripts": {
|
|
52
53
|
"build": "tsdown",
|
|
53
|
-
"
|
|
54
|
+
"typecheck": "tsc --noEmit --incremental",
|
|
55
|
+
"coverage": "vitest run --coverage",
|
|
56
|
+
"test": "bun run typecheck && bun run coverage"
|
|
54
57
|
},
|
|
55
58
|
"peerDependencies": {
|
|
56
|
-
"@expressive/mvc": "^0.
|
|
59
|
+
"@expressive/mvc": "^0.89.0",
|
|
57
60
|
"vite": ">=5"
|
|
58
61
|
},
|
|
59
62
|
"peerDependenciesMeta": {
|
|
@@ -1,178 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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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"}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { has, map } from "@expressive/mvc";
|
|
2
|
-
|
|
3
|
-
//#region src/vnode.d.ts
|
|
4
|
-
declare const VNODE: unique symbol;
|
|
5
|
-
type Key = string | number | null | undefined;
|
|
6
|
-
type FunctionComponent<P = any> = (props: P) => Node;
|
|
7
|
-
interface ComponentInstance {
|
|
8
|
-
readonly props: any;
|
|
9
|
-
render(props?: any): Node;
|
|
10
|
-
}
|
|
11
|
-
type ComponentType<P = any> = FunctionComponent<P> | (new (props: P) => ComponentInstance);
|
|
12
|
-
type Node = VNode | ComponentInstance | string | number | bigint | boolean | null | undefined | readonly Node[] | has.List<Node> | has.Pool<Node, any, any> | map.Managed<unknown, Node>;
|
|
13
|
-
interface VNode<P = any> {
|
|
14
|
-
readonly [VNODE]: true;
|
|
15
|
-
readonly type: string | symbol | ComponentType<P>;
|
|
16
|
-
readonly props: P & {
|
|
17
|
-
children?: Node;
|
|
18
|
-
};
|
|
19
|
-
readonly key?: Key;
|
|
20
|
-
}
|
|
21
|
-
declare function vnode<P>(type: VNode<P>['type'], props: P, key?: unknown): VNode<P>;
|
|
22
|
-
declare function createPortal(children: Node, container: Element | DocumentFragment, key?: Key): VNode;
|
|
23
|
-
//#endregion
|
|
24
|
-
export { createPortal as a, VNode as i, FunctionComponent as n, vnode as o, Node as r, ComponentType as t };
|
|
25
|
-
//# sourceMappingURL=chunk-vnode-BsbDCqd1.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|