@expressive/dom 0.2.0 → 0.3.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.
@@ -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 { JSX } from '@expressive/mvc/jsx-runtime';\n"],"mappings":";;;;AAyFA,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"}
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, State } from '@expressive/mvc';\nimport type { JSX as Base } from '@expressive/mvc/jsx-runtime';\n\nimport { childrenOf, isVNode, vnode } from './vnode';\nimport type { Node, VNode } from './vnode';\nimport type { Attributes as Loaded, Module } from './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: State;\n }\n}\n\ndeclare module '@expressive/mvc' {\n namespace State {\n /** JSX attributes of a State with no `props` member: its settable fields, `is`, a boundary, and what `render` accepts. */\n type Props<T extends State> =\n & Component.StateProps<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 State renders as a class element - one with `render` produces content,\n * one without passes children through and provides itself.\n */\n interface ElementClass extends State { 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 State.\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 State ? State.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
- * Bind a module's classes to `hot.accept` and its function components to dom's
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
- ` : ` const __notes = { ${notes.join(", ")} };
74
- const __flag = (key) => 'expressive:private:' + ${JSON.stringify(id)} + ':' + key;
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') {
@@ -90,16 +78,19 @@ function inject(id, body, ssr = false) {
90
78
  } catch {
91
79
  console.warn(note);
92
80
  }
93
- dispatchEvent(new CustomEvent('expressive:reload', { detail: { module: ${JSON.stringify(id)}, class: key, reason: note ? 'private members' : 'class changed shape' } }));
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
- const lines = ["", `import { State as __State } from '@expressive/mvc';`];
99
- if (classes.length) lines.push(`import { hot as __expressive } from '@expressive/mvc/runtime';`, "{", ` const __hot = __expressive.accept(${JSON.stringify(id)}, { ${list} });`, ...classes.map((name) => ` ${name} = __hot.${name};`), "}");
100
- if (components.length) lines.push(`import { hot as __refresh } from '@expressive/dom/jsx-dev-runtime';`, `__refresh(${JSON.stringify(id)}, { ${components.join(", ")} });`);
101
- lines.push(`if (import.meta.hot) {
102
- ${replace} const __exports = { ${record.join(", ")} };
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) {
@@ -108,9 +99,31 @@ ${replace} const __exports = { ${record.join(", ")} };
108
99
  return import.meta.hot.invalidate(\`"\${key}" export cannot be hot-patched.\`);
109
100
  }
110
101
  });
111
- }
112
- `);
113
- return lines.join("\n");
102
+ }`;
103
+ /**
104
+ * Bind a module's classes to `hot.accept` and its function components to dom's
105
+ * refresh. A State 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 { State as __State } 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/runtime", "@expressive/dom/jsx-dev-runtime"].map((entry) => this.resolve(entry).then((found) => found && dirname(found.id))));
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":"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"}
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 __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: ${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 __State) && !${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 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 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 { State as __State } 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.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "MVC-native DOM renderer.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -50,10 +50,12 @@
50
50
  },
51
51
  "scripts": {
52
52
  "build": "tsdown",
53
- "test": "tsc --noEmit && vitest run --coverage"
53
+ "typecheck": "tsc --noEmit --incremental",
54
+ "coverage": "vitest run --coverage",
55
+ "test": "bun run typecheck && bun run coverage"
54
56
  },
55
57
  "peerDependencies": {
56
- "@expressive/mvc": "^0.87.0",
58
+ "@expressive/mvc": "^0.88.0",
57
59
  "vite": ">=5"
58
60
  },
59
61
  "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"}