@use-voltra/core 2.0.0 → 2.1.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/build/cjs/index.js +1 -0
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/widget-environment.js +26 -0
- package/build/cjs/widget-environment.js.map +1 -0
- package/build/esm/index.js +1 -0
- package/build/esm/index.js.map +1 -1
- package/build/esm/widget-environment.js +22 -0
- package/build/esm/widget-environment.js.map +1 -0
- package/build/types/index.d.ts +1 -0
- package/build/types/index.d.ts.map +1 -1
- package/build/types/widget-environment.d.ts +85 -0
- package/build/types/widget-environment.d.ts.map +1 -0
- package/package.json +2 -1
- package/src/index.ts +6 -0
- package/src/jsx/createVoltraComponent.ts +36 -0
- package/src/payload/short-names.ts +379 -0
- package/src/payload.ts +23 -0
- package/src/renderer/context-registry.ts +40 -0
- package/src/renderer/dispatcher.ts +105 -0
- package/src/renderer/element-registry.ts +69 -0
- package/src/renderer/flatten-styles.ts +24 -0
- package/src/renderer/index.ts +14 -0
- package/src/renderer/render-cache.ts +27 -0
- package/src/renderer/renderer.ts +491 -0
- package/src/renderer/stylesheet-registry.ts +54 -0
- package/src/renderer/types.ts +5 -0
- package/src/types.ts +14 -0
- package/src/widget-environment.ts +114 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Context } from 'react'
|
|
2
|
+
|
|
3
|
+
declare module 'react' {
|
|
4
|
+
interface Context<T> {
|
|
5
|
+
_currentValue: T
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type ContextRegistry = {
|
|
10
|
+
pushProvider: <T>(context: Context<T>, value: T) => void
|
|
11
|
+
popProvider: <T>(context: Context<T>) => void
|
|
12
|
+
readContext: <T>(context: Context<T>) => T
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const getContextRegistry = (): ContextRegistry => {
|
|
16
|
+
const contextMap = new Map()
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
pushProvider: <T>(context: Context<T>, value: T) => {
|
|
20
|
+
const stack = contextMap.get(context) || []
|
|
21
|
+
stack.push(value)
|
|
22
|
+
contextMap.set(context, stack)
|
|
23
|
+
},
|
|
24
|
+
popProvider: <T>(context: Context<T>) => {
|
|
25
|
+
const stack = contextMap.get(context)
|
|
26
|
+
if (stack) {
|
|
27
|
+
stack.pop()
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
readContext: <T>(context: Context<T>) => {
|
|
31
|
+
const stack = contextMap.get(context)
|
|
32
|
+
|
|
33
|
+
if (stack && stack.length > 0) {
|
|
34
|
+
return stack[stack.length - 1]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return context._currentValue
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import React, { Context, ReactDispatcher, ReactHooksDispatcher } from 'react'
|
|
2
|
+
|
|
3
|
+
import { ContextRegistry } from './context-registry.js'
|
|
4
|
+
|
|
5
|
+
const REACT_CONTEXT_TYPE = Symbol.for('react.context')
|
|
6
|
+
const REACT_MEMO_CACHE_SENTINEL = Symbol.for('react.memo_cache_sentinel')
|
|
7
|
+
|
|
8
|
+
declare module 'react' {
|
|
9
|
+
type HookFn = (...args: any[]) => any
|
|
10
|
+
|
|
11
|
+
export type ReactHooksDispatcher = {
|
|
12
|
+
useState: HookFn
|
|
13
|
+
useReducer: HookFn
|
|
14
|
+
useEffect: HookFn
|
|
15
|
+
useLayoutEffect: HookFn
|
|
16
|
+
useInsertionEffect: HookFn
|
|
17
|
+
useCallback: HookFn
|
|
18
|
+
useMemo: HookFn
|
|
19
|
+
useRef: HookFn
|
|
20
|
+
useContext: HookFn
|
|
21
|
+
useId: HookFn
|
|
22
|
+
useImperativeHandle: HookFn
|
|
23
|
+
useDebugValue: HookFn
|
|
24
|
+
useDeferredValue: HookFn
|
|
25
|
+
useTransition: HookFn
|
|
26
|
+
useSyncExternalStore: HookFn
|
|
27
|
+
use: HookFn
|
|
28
|
+
useActionState: HookFn
|
|
29
|
+
useOptimistic: HookFn
|
|
30
|
+
useEffectEvent: HookFn
|
|
31
|
+
useMemoCache: HookFn
|
|
32
|
+
useCacheRefresh: HookFn
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type ReactDispatcher = {
|
|
36
|
+
H: ReactHooksDispatcher
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE: ReactDispatcher
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const getHooksDispatcher = (registry: ContextRegistry): ReactHooksDispatcher => ({
|
|
43
|
+
useContext: <T>(context: Context<T>) => registry.readContext(context),
|
|
44
|
+
use: <T>(usable: React.Usable<T>): T => {
|
|
45
|
+
if (
|
|
46
|
+
usable !== null &&
|
|
47
|
+
typeof usable === 'object' &&
|
|
48
|
+
(usable as { $$typeof?: symbol }).$$typeof === REACT_CONTEXT_TYPE
|
|
49
|
+
) {
|
|
50
|
+
return registry.readContext(usable as unknown as Context<T>)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (
|
|
54
|
+
usable !== null &&
|
|
55
|
+
(typeof usable === 'object' || typeof usable === 'function') &&
|
|
56
|
+
typeof (usable as { then?: unknown }).then === 'function'
|
|
57
|
+
) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
'use() with promises is not supported in Voltra. Async data fetching is not available in this synchronous renderer.'
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
throw new Error(`An unsupported type was passed to use(): ${String(usable)}`)
|
|
64
|
+
},
|
|
65
|
+
useState: <S>(initial?: S | (() => S)) => [
|
|
66
|
+
typeof initial === 'function' ? (initial as () => S)() : initial,
|
|
67
|
+
() => {},
|
|
68
|
+
],
|
|
69
|
+
useReducer: <S, I, A extends React.AnyActionArg>(
|
|
70
|
+
_: (prevState: S, ...args: A) => S,
|
|
71
|
+
initialArg: I,
|
|
72
|
+
init?: (i: I) => S
|
|
73
|
+
): [S, React.ActionDispatch<A>] => {
|
|
74
|
+
const state = init ? init(initialArg) : initialArg
|
|
75
|
+
return [state as S, () => {}]
|
|
76
|
+
},
|
|
77
|
+
useMemo: (factory) => factory(),
|
|
78
|
+
useCallback: (cb) => cb,
|
|
79
|
+
useRef: (initial) => ({ current: initial }),
|
|
80
|
+
useEffect: () => {},
|
|
81
|
+
useLayoutEffect: () => {},
|
|
82
|
+
useInsertionEffect: () => {},
|
|
83
|
+
useId: () => Math.random().toString(36).substr(2, 9),
|
|
84
|
+
useDebugValue: () => {},
|
|
85
|
+
useImperativeHandle: () => {},
|
|
86
|
+
useDeferredValue: <T>(value: T) => value,
|
|
87
|
+
useTransition: () => [false, (func: () => void) => func()],
|
|
88
|
+
useSyncExternalStore: (_, getSnapshot) => getSnapshot(),
|
|
89
|
+
useActionState: <S>(_: unknown, initialState: S, _permalink?: string) =>
|
|
90
|
+
[initialState, () => {}, false] as [S, () => void, boolean],
|
|
91
|
+
useOptimistic: <T>(passthrough: T) => [passthrough, () => {}] as [T, (action: unknown) => void],
|
|
92
|
+
useEffectEvent: <T extends Function>(callback: T): T => callback,
|
|
93
|
+
useMemoCache: (size: number) => {
|
|
94
|
+
const data = new Array<unknown>(size)
|
|
95
|
+
for (let i = 0; i < size; i++) {
|
|
96
|
+
data[i] = REACT_MEMO_CACHE_SENTINEL
|
|
97
|
+
}
|
|
98
|
+
return data
|
|
99
|
+
},
|
|
100
|
+
useCacheRefresh: () => () => {},
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
export const getReactCurrentDispatcher = (): ReactDispatcher => {
|
|
104
|
+
return React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE
|
|
105
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
import { VoltraNodeJson } from '../types.js'
|
|
4
|
+
|
|
5
|
+
export type ElementRegistry = {
|
|
6
|
+
isRegistered: (element: ReactNode) => number | undefined
|
|
7
|
+
register: (element: ReactNode, rendered: VoltraNodeJson) => number
|
|
8
|
+
getElements: () => VoltraNodeJson[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const createElementRegistry = (): ElementRegistry => {
|
|
12
|
+
const elementToIndex = new Map<ReactNode, number>()
|
|
13
|
+
const elements: VoltraNodeJson[] = []
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
isRegistered: (element: ReactNode): number | undefined => {
|
|
17
|
+
return elementToIndex.get(element)
|
|
18
|
+
},
|
|
19
|
+
register: (element: ReactNode, rendered: VoltraNodeJson): number => {
|
|
20
|
+
const index = elements.length
|
|
21
|
+
elementToIndex.set(element, index)
|
|
22
|
+
elements.push(rendered)
|
|
23
|
+
return index
|
|
24
|
+
},
|
|
25
|
+
getElements: (): VoltraNodeJson[] => elements,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const preScanForDuplicates = (element: ReactNode): Set<ReactNode> => {
|
|
30
|
+
const seen = new Set<ReactNode>()
|
|
31
|
+
const duplicates = new Set<ReactNode>()
|
|
32
|
+
|
|
33
|
+
const scan = (node: ReactNode): void => {
|
|
34
|
+
if (node === null || node === undefined || typeof node !== 'object') {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (Array.isArray(node)) {
|
|
39
|
+
for (const child of node) {
|
|
40
|
+
scan(child)
|
|
41
|
+
}
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (seen.has(node)) {
|
|
46
|
+
duplicates.add(node)
|
|
47
|
+
} else {
|
|
48
|
+
seen.add(node)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if ('props' in node) {
|
|
52
|
+
const props = (node as { props: Record<string, unknown> }).props
|
|
53
|
+
if (props) {
|
|
54
|
+
if (props.children !== undefined) {
|
|
55
|
+
scan(props.children as ReactNode)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
for (const [key, value] of Object.entries(props)) {
|
|
59
|
+
if (key !== 'children' && value && typeof value === 'object' && 'type' in value && 'props' in value) {
|
|
60
|
+
scan(value as ReactNode)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
scan(element)
|
|
68
|
+
return duplicates
|
|
69
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type StyleProp<T> = T | T[] | null | undefined | false
|
|
2
|
+
|
|
3
|
+
type FlatStyleProp<T> = T extends (infer U)[] ? FlatStyleProp<U> : T
|
|
4
|
+
|
|
5
|
+
export const flattenStyle = <T extends StyleProp<unknown>>(style: T | null | undefined): FlatStyleProp<T> | null => {
|
|
6
|
+
if (!style) {
|
|
7
|
+
return null
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (!Array.isArray(style)) {
|
|
11
|
+
return style as FlatStyleProp<T>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const result: Record<string, unknown> = {}
|
|
15
|
+
for (let i = 0, styleLength = style.length; i < styleLength; ++i) {
|
|
16
|
+
const computedStyle = flattenStyle(style[i])
|
|
17
|
+
if (computedStyle) {
|
|
18
|
+
for (const key in computedStyle) {
|
|
19
|
+
result[key] = computedStyle[key]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return result as FlatStyleProp<T>
|
|
24
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { type ContextRegistry, getContextRegistry } from './context-registry.js'
|
|
2
|
+
export { getHooksDispatcher, getReactCurrentDispatcher } from './dispatcher.js'
|
|
3
|
+
export { createElementRegistry, type ElementRegistry, preScanForDuplicates } from './element-registry.js'
|
|
4
|
+
export { flattenStyle } from './flatten-styles.js'
|
|
5
|
+
export { getRenderCache, type RenderCache } from './render-cache.js'
|
|
6
|
+
export {
|
|
7
|
+
type ComponentRegistry,
|
|
8
|
+
createVoltraRenderer,
|
|
9
|
+
renderVariantToJson,
|
|
10
|
+
transformProps,
|
|
11
|
+
VOLTRA_PAYLOAD_VERSION,
|
|
12
|
+
} from './renderer.js'
|
|
13
|
+
export { createStylesheetRegistry, type StylesheetRegistry } from './stylesheet-registry.js'
|
|
14
|
+
export type { VoltraVariantRenderer } from './types.js'
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
import { VoltraNodeJson } from '../types.js'
|
|
4
|
+
import { VoltraVariantRenderer } from './types.js'
|
|
5
|
+
|
|
6
|
+
export type RenderCache = {
|
|
7
|
+
getOrRender: VoltraVariantRenderer
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const getRenderCache = (renderer: VoltraVariantRenderer): RenderCache => {
|
|
11
|
+
const renderCache = new Map<ReactNode, VoltraNodeJson>()
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
getOrRender: (node: ReactNode): VoltraNodeJson => {
|
|
15
|
+
const cached = renderCache.get(node)
|
|
16
|
+
|
|
17
|
+
if (cached) {
|
|
18
|
+
return cached
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const result = renderer(node)
|
|
22
|
+
renderCache.set(node, result)
|
|
23
|
+
|
|
24
|
+
return result
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
}
|