@vxrn/compiler 1.25.13 → 1.25.16-1788753284749
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/dist/cjs/cache.cjs +3 -1
- package/dist/cjs/configure.cjs +9 -1
- package/dist/cjs/index.cjs +82 -4
- package/dist/cjs/reactNativeCodegen.cjs +140 -0
- package/dist/cjs/reactNativeViewConfig.cjs +489 -0
- package/dist/cjs/transformBabel.cjs +15 -20
- package/dist/cjs/transformBabel.test.cjs +260 -0
- package/dist/cjs/transformHermesLoops.cjs +317 -0
- package/dist/cjs/transformHermesLoops.test.cjs +220 -0
- package/dist/cjs/transformSWC.cjs +102 -137
- package/dist/cjs/transformSWC.test.cjs +74 -0
- package/dist/cjs/transformWorklets.cjs +121 -0
- package/dist/cjs/transformWorklets.test.cjs +604 -0
- package/dist/cjs/worklets/autoworklet.cjs +265 -0
- package/dist/cjs/worklets/globals.cjs +71 -0
- package/dist/cjs/worklets/hash.cjs +38 -0
- package/dist/cjs/worklets/scope.cjs +370 -0
- package/dist/cjs/worklets/serialize.cjs +101 -0
- package/dist/cjs/worklets/transform.cjs +232 -0
- package/dist/cjs/worklets/types.cjs +18 -0
- package/dist/esm/cache.mjs +4 -2
- package/dist/esm/cache.mjs.map +1 -1
- package/dist/esm/configure.mjs +8 -1
- package/dist/esm/configure.mjs.map +1 -1
- package/dist/esm/index.js +72 -5
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.mjs +72 -5
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/reactNativeCodegen.mjs +104 -0
- package/dist/esm/reactNativeCodegen.mjs.map +1 -0
- package/dist/esm/reactNativeViewConfig.mjs +460 -0
- package/dist/esm/reactNativeViewConfig.mjs.map +1 -0
- package/dist/esm/transformBabel.mjs +14 -19
- package/dist/esm/transformBabel.mjs.map +1 -1
- package/dist/esm/transformBabel.test.mjs +261 -1
- package/dist/esm/transformBabel.test.mjs.map +1 -1
- package/dist/esm/transformHermesLoops.mjs +281 -0
- package/dist/esm/transformHermesLoops.mjs.map +1 -0
- package/dist/esm/transformHermesLoops.test.mjs +197 -0
- package/dist/esm/transformHermesLoops.test.mjs.map +1 -0
- package/dist/esm/transformSWC.mjs +89 -138
- package/dist/esm/transformSWC.mjs.map +1 -1
- package/dist/esm/transformSWC.test.mjs +75 -0
- package/dist/esm/transformSWC.test.mjs.map +1 -0
- package/dist/esm/transformWorklets.mjs +81 -0
- package/dist/esm/transformWorklets.mjs.map +1 -0
- package/dist/esm/transformWorklets.test.mjs +581 -0
- package/dist/esm/transformWorklets.test.mjs.map +1 -0
- package/dist/esm/worklets/autoworklet.mjs +234 -0
- package/dist/esm/worklets/autoworklet.mjs.map +1 -0
- package/dist/esm/worklets/globals.mjs +45 -0
- package/dist/esm/worklets/globals.mjs.map +1 -0
- package/dist/esm/worklets/hash.mjs +13 -0
- package/dist/esm/worklets/hash.mjs.map +1 -0
- package/dist/esm/worklets/scope.mjs +345 -0
- package/dist/esm/worklets/scope.mjs.map +1 -0
- package/dist/esm/worklets/serialize.mjs +75 -0
- package/dist/esm/worklets/serialize.mjs.map +1 -0
- package/dist/esm/worklets/transform.mjs +196 -0
- package/dist/esm/worklets/transform.mjs.map +1 -0
- package/dist/esm/worklets/types.mjs +2 -0
- package/dist/esm/worklets/types.mjs.map +1 -0
- package/package.json +10 -6
- package/src/cache.ts +9 -2
- package/src/configure.ts +12 -0
- package/src/index.ts +93 -13
- package/src/reactNativeCodegen.ts +181 -0
- package/src/reactNativeViewConfig.ts +593 -0
- package/src/transformBabel.test.ts +268 -1
- package/src/transformBabel.ts +21 -31
- package/src/transformHermesLoops.test.ts +282 -0
- package/src/transformHermesLoops.ts +433 -0
- package/src/transformSWC.test.ts +88 -0
- package/src/transformSWC.ts +109 -160
- package/src/transformWorklets.test.ts +708 -0
- package/src/transformWorklets.ts +160 -0
- package/src/types.ts +4 -4
- package/src/worklets/autoworklet.ts +391 -0
- package/src/worklets/globals.ts +125 -0
- package/src/worklets/hash.ts +14 -0
- package/src/worklets/scope.ts +401 -0
- package/src/worklets/serialize.ts +114 -0
- package/src/worklets/transform.ts +282 -0
- package/src/worklets/types.ts +26 -0
- package/types/cache.d.ts +1 -0
- package/types/cache.d.ts.map +1 -1
- package/types/configure.d.ts +2 -0
- package/types/configure.d.ts.map +1 -1
- package/types/index.d.ts +3 -0
- package/types/index.d.ts.map +1 -1
- package/types/reactNativeCodegen.d.ts +5 -0
- package/types/reactNativeCodegen.d.ts.map +1 -0
- package/types/reactNativeViewConfig.d.ts +35 -0
- package/types/reactNativeViewConfig.d.ts.map +1 -0
- package/types/transformBabel.d.ts +3 -3
- package/types/transformBabel.d.ts.map +1 -1
- package/types/transformHermesLoops.d.ts +5 -0
- package/types/transformHermesLoops.d.ts.map +1 -0
- package/types/transformHermesLoops.test.d.ts +2 -0
- package/types/transformHermesLoops.test.d.ts.map +1 -0
- package/types/transformSWC.d.ts +12 -3
- package/types/transformSWC.d.ts.map +1 -1
- package/types/transformSWC.test.d.ts +2 -0
- package/types/transformSWC.test.d.ts.map +1 -0
- package/types/transformWorklets.d.ts +22 -0
- package/types/transformWorklets.d.ts.map +1 -0
- package/types/transformWorklets.test.d.ts +2 -0
- package/types/transformWorklets.test.d.ts.map +1 -0
- package/types/types.d.ts +4 -4
- package/types/types.d.ts.map +1 -1
- package/types/worklets/autoworklet.d.ts +14 -0
- package/types/worklets/autoworklet.d.ts.map +1 -0
- package/types/worklets/globals.d.ts +3 -0
- package/types/worklets/globals.d.ts.map +1 -0
- package/types/worklets/hash.d.ts +5 -0
- package/types/worklets/hash.d.ts.map +1 -0
- package/types/worklets/scope.d.ts +7 -0
- package/types/worklets/scope.d.ts.map +1 -0
- package/types/worklets/serialize.d.ts +12 -0
- package/types/worklets/serialize.d.ts.map +1 -0
- package/types/worklets/transform.d.ts +6 -0
- package/types/worklets/transform.d.ts.map +1 -0
- package/types/worklets/types.d.ts +21 -0
- package/types/worklets/types.d.ts.map +1 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import { createRequire } from 'node:module'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { configuration, isNativeWorkletsEnabled } from './configure'
|
|
5
|
+
import { AUTOWORKLET_FUNCTION_ARGS, GESTURE_BUILDER_METHODS } from './worklets/autoworklet'
|
|
6
|
+
|
|
7
|
+
// every callee the transform auto-workletizes has to be in this gate, or files
|
|
8
|
+
// whose only worklets come from gesture callbacks (`.onBegin`, `.onEnd`,
|
|
9
|
+
// `.onTouchesMove`) are skipped before the transform ever sees them and ship
|
|
10
|
+
// untransformed.
|
|
11
|
+
export const REANIMATED_AUTOWORKLETIZATION_KEYWORDS = [
|
|
12
|
+
'worklet',
|
|
13
|
+
'useAnimatedGestureHandler',
|
|
14
|
+
'useAnimatedScrollHandler',
|
|
15
|
+
'useFrameCallback',
|
|
16
|
+
'useAnimatedStyle',
|
|
17
|
+
'useAnimatedProps',
|
|
18
|
+
'createAnimatedPropAdapter',
|
|
19
|
+
'useDerivedValue',
|
|
20
|
+
'useAnimatedReaction',
|
|
21
|
+
'useWorkletCallback',
|
|
22
|
+
'withTiming',
|
|
23
|
+
'withSpring',
|
|
24
|
+
'withDecay',
|
|
25
|
+
'withRepeat',
|
|
26
|
+
'runOnUI',
|
|
27
|
+
'executeOnUIRuntimeSync',
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
const REANIMATED_REGEX = new RegExp(
|
|
31
|
+
[
|
|
32
|
+
...new Set([
|
|
33
|
+
...REANIMATED_AUTOWORKLETIZATION_KEYWORDS,
|
|
34
|
+
...Object.keys(AUTOWORKLET_FUNCTION_ARGS),
|
|
35
|
+
// gesture callbacks are only workletized on a `Gesture.<Kind>()` chain,
|
|
36
|
+
// but this file gate has to be permissive or a file whose only worklets
|
|
37
|
+
// are gesture callbacks never reaches the transform at all.
|
|
38
|
+
...GESTURE_BUILDER_METHODS,
|
|
39
|
+
'withCallback',
|
|
40
|
+
]),
|
|
41
|
+
]
|
|
42
|
+
.map((name) => `\\b${name}\\b`)
|
|
43
|
+
.join('|')
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
const REANIMATED_IGNORED_PATHS = [
|
|
47
|
+
'react-native-prebuilt',
|
|
48
|
+
'node_modules/.vxrn/react-native',
|
|
49
|
+
'node_modules/react/',
|
|
50
|
+
'node_modules/react-dom/',
|
|
51
|
+
'node_modules/react-native/',
|
|
52
|
+
// react-native's own scoped packages are flow, and the worklets transform
|
|
53
|
+
// runs before flow stripping in the vite pipeline. they never hold app
|
|
54
|
+
// worklets, exactly like node_modules/react-native/ above.
|
|
55
|
+
'node_modules/@react-native/',
|
|
56
|
+
'node_modules/react-native-web/',
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
const REANIMATED_IGNORED_PATHS_REGEX = new RegExp(REANIMATED_IGNORED_PATHS.join('|'))
|
|
60
|
+
|
|
61
|
+
export function shouldTransformWorklets({ id, code }: { id: string; code: string }) {
|
|
62
|
+
if (!isNativeWorkletsEnabled() || !configuration.enableReanimated) {
|
|
63
|
+
return false
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const normalizedId = id.replace(/\\/g, '/')
|
|
67
|
+
if (REANIMATED_IGNORED_PATHS_REGEX.test(normalizedId)) {
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return REANIMATED_REGEX.test(code)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function getWorkletsVersion(projectRoot?: string, filename?: string): string {
|
|
75
|
+
if (projectRoot) {
|
|
76
|
+
try {
|
|
77
|
+
const req = createRequire(path.resolve(projectRoot, 'package.json'))
|
|
78
|
+
// Look for react-native-worklets first (preferred in modern Worklets / Reanimated 4)
|
|
79
|
+
try {
|
|
80
|
+
const ver = req('react-native-worklets/package.json')?.version
|
|
81
|
+
if (ver) return ver
|
|
82
|
+
} catch {}
|
|
83
|
+
// Fall back to react-native-reanimated (Reanimated 3 where worklets is bundled inside)
|
|
84
|
+
try {
|
|
85
|
+
const ver = req('react-native-reanimated/package.json')?.version
|
|
86
|
+
if (ver) return ver
|
|
87
|
+
} catch {}
|
|
88
|
+
} catch {}
|
|
89
|
+
|
|
90
|
+
throw new Error(
|
|
91
|
+
`[@vxrn/compiler] Unable to resolve "react-native-worklets" or "react-native-reanimated" version from project root "${projectRoot}". ` +
|
|
92
|
+
`Please ensure react-native-worklets is installed in your project.`
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// If no projectRoot, try resolving from filename if it exists on disk
|
|
97
|
+
if (filename && !filename.startsWith('\0') && fs.existsSync(filename)) {
|
|
98
|
+
try {
|
|
99
|
+
const req = createRequire(filename)
|
|
100
|
+
try {
|
|
101
|
+
const ver = req('react-native-worklets/package.json')?.version
|
|
102
|
+
if (ver) return ver
|
|
103
|
+
} catch {}
|
|
104
|
+
try {
|
|
105
|
+
const ver = req('react-native-reanimated/package.json')?.version
|
|
106
|
+
if (ver) return ver
|
|
107
|
+
} catch {}
|
|
108
|
+
} catch {}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Fall back to process.cwd() or import.meta.url for synthetic / dev environments
|
|
112
|
+
const fallbacks = [
|
|
113
|
+
() => createRequire(path.resolve(process.cwd(), 'package.json')),
|
|
114
|
+
() => createRequire(import.meta.url),
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
for (const getReq of fallbacks) {
|
|
118
|
+
try {
|
|
119
|
+
const req = getReq()
|
|
120
|
+
try {
|
|
121
|
+
const ver = req('react-native-worklets/package.json')?.version
|
|
122
|
+
if (ver) return ver
|
|
123
|
+
} catch {}
|
|
124
|
+
try {
|
|
125
|
+
const ver = req('react-native-reanimated/package.json')?.version
|
|
126
|
+
if (ver) return ver
|
|
127
|
+
} catch {}
|
|
128
|
+
} catch {}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const target = filename || process.cwd()
|
|
132
|
+
throw new Error(
|
|
133
|
+
`[@vxrn/compiler] Unable to resolve "react-native-worklets" or "react-native-reanimated" version from "${target}". ` +
|
|
134
|
+
`Please ensure react-native-worklets is installed in your project.`
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export type TransformWorkletsOptions = {
|
|
139
|
+
projectRoot?: string
|
|
140
|
+
bundleMode?: boolean
|
|
141
|
+
disableInlineStylesWarning?: boolean
|
|
142
|
+
disableSourceMaps?: boolean
|
|
143
|
+
disableWorkletClasses?: boolean
|
|
144
|
+
globals?: string[]
|
|
145
|
+
relativeSourceLocation?: boolean
|
|
146
|
+
strictGlobal?: boolean
|
|
147
|
+
pluginVersion?: string
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
import { executeWorkletTransform } from './worklets/transform'
|
|
151
|
+
|
|
152
|
+
export async function transformWorklets(
|
|
153
|
+
id: string,
|
|
154
|
+
code: string,
|
|
155
|
+
sourceMaps = false,
|
|
156
|
+
options?: TransformWorkletsOptions
|
|
157
|
+
): Promise<{ code: string; map?: any }> {
|
|
158
|
+
return executeWorkletTransform(id, code, sourceMaps, options, getWorkletsVersion)
|
|
159
|
+
}
|
|
160
|
+
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { TransformOptions as OxcTransformOptions } from 'oxc-transform'
|
|
2
2
|
|
|
3
3
|
export type Environment = 'ios' | 'android' | 'ssr' | 'client'
|
|
4
4
|
|
|
@@ -23,13 +23,13 @@ export type GetTransformProps = {
|
|
|
23
23
|
|
|
24
24
|
export type GetTransform = (props: GetTransformProps) => GetTransformResponse
|
|
25
25
|
|
|
26
|
-
export type GetTransformResponse = boolean | 'babel' | 'swc' | TransformOptions
|
|
26
|
+
export type GetTransformResponse = boolean | 'babel' | 'swc' | 'oxc' | TransformOptions
|
|
27
27
|
|
|
28
28
|
export type TransformOptions = BabelTransformOptions | SWCTransformOptions
|
|
29
29
|
|
|
30
30
|
export type SWCTransformOptions = {
|
|
31
|
-
transform: 'swc'
|
|
32
|
-
} &
|
|
31
|
+
transform: 'swc' | 'oxc'
|
|
32
|
+
} & OxcTransformOptions
|
|
33
33
|
|
|
34
34
|
export type BabelTransformOptions = {
|
|
35
35
|
transform: 'babel'
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import type { WorkletCandidate } from './types'
|
|
2
|
+
|
|
3
|
+
// null-prototype: callee names are arbitrary source identifiers, so a plain
|
|
4
|
+
// object literal would resolve `constructor`, `toString` and friends off
|
|
5
|
+
// Object.prototype and hand back a non-iterable.
|
|
6
|
+
export const AUTOWORKLET_FUNCTION_ARGS: Record<string, number[]> = Object.assign(
|
|
7
|
+
Object.create(null),
|
|
8
|
+
{
|
|
9
|
+
useFrameCallback: [0],
|
|
10
|
+
useAnimatedStyle: [0],
|
|
11
|
+
useAnimatedProps: [0],
|
|
12
|
+
createAnimatedPropAdapter: [0],
|
|
13
|
+
useDerivedValue: [0],
|
|
14
|
+
useAnimatedScrollHandler: [0],
|
|
15
|
+
useAnimatedGestureHandler: [0],
|
|
16
|
+
useAnimatedReaction: [0, 1],
|
|
17
|
+
withTiming: [2],
|
|
18
|
+
withSpring: [2],
|
|
19
|
+
withDecay: [1],
|
|
20
|
+
withRepeat: [3],
|
|
21
|
+
runOnUI: [0],
|
|
22
|
+
executeOnUIRuntimeSync: [0],
|
|
23
|
+
scheduleOnUI: [0],
|
|
24
|
+
runOnUISync: [0],
|
|
25
|
+
runOnUIAsync: [0],
|
|
26
|
+
runOnRuntime: [1],
|
|
27
|
+
runOnRuntimeSync: [1],
|
|
28
|
+
runOnRuntimeAsync: [1],
|
|
29
|
+
scheduleOnRuntime: [1],
|
|
30
|
+
runOnRuntimeSyncWithId: [1],
|
|
31
|
+
scheduleOnRuntimeWithId: [1],
|
|
32
|
+
useTapGesture: [0],
|
|
33
|
+
usePanGesture: [0],
|
|
34
|
+
usePinchGesture: [0],
|
|
35
|
+
useRotationGesture: [0],
|
|
36
|
+
useFlingGesture: [0],
|
|
37
|
+
useLongPressGesture: [0],
|
|
38
|
+
useNativeGesture: [0],
|
|
39
|
+
useManualGesture: [0],
|
|
40
|
+
useHoverGesture: [0],
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
// gesture builder callbacks are NOT matched by name. `onChange`, `onStart` and
|
|
45
|
+
// `onEnd` are ordinary identifiers in plenty of unrelated code, and turning one
|
|
46
|
+
// of those callbacks into a worklet ships a function to the UI runtime that was
|
|
47
|
+
// never written for it. the callback only counts when the receiver is a
|
|
48
|
+
// `Gesture.<Kind>()` chain, which is what the babel plugin requires too.
|
|
49
|
+
export const GESTURE_BUILDER_METHODS = new Set([
|
|
50
|
+
'onBegin',
|
|
51
|
+
'onStart',
|
|
52
|
+
'onEnd',
|
|
53
|
+
'onFinalize',
|
|
54
|
+
'onUpdate',
|
|
55
|
+
'onChange',
|
|
56
|
+
'onTouchesDown',
|
|
57
|
+
'onTouchesMove',
|
|
58
|
+
'onTouchesUp',
|
|
59
|
+
'onTouchesCancelled',
|
|
60
|
+
])
|
|
61
|
+
|
|
62
|
+
const GESTURE_KINDS = new Set([
|
|
63
|
+
'Tap',
|
|
64
|
+
'Pan',
|
|
65
|
+
'Pinch',
|
|
66
|
+
'Rotation',
|
|
67
|
+
'Fling',
|
|
68
|
+
'LongPress',
|
|
69
|
+
'ForceTouch',
|
|
70
|
+
'Native',
|
|
71
|
+
'Manual',
|
|
72
|
+
'Race',
|
|
73
|
+
'Simultaneous',
|
|
74
|
+
'Exclusive',
|
|
75
|
+
'Hover',
|
|
76
|
+
])
|
|
77
|
+
|
|
78
|
+
// `FadeIn.duration(300).withCallback(fn)` - same shape, same reason for gating
|
|
79
|
+
// on the chain root rather than on the method name.
|
|
80
|
+
const LAYOUT_ANIMATIONS = new Set(
|
|
81
|
+
`BounceIn BounceInDown BounceInLeft BounceInRight BounceInUp BounceOut BounceOutDown
|
|
82
|
+
BounceOutLeft BounceOutRight BounceOutUp FadeIn FadeInDown FadeInLeft FadeInRight FadeInUp
|
|
83
|
+
FadeOut FadeOutDown FadeOutLeft FadeOutRight FadeOutUp FlipInEasyX FlipInEasyY FlipInXDown
|
|
84
|
+
FlipInXUp FlipInYLeft FlipInYRight FlipOutEasyX FlipOutEasyY FlipOutXDown FlipOutXUp
|
|
85
|
+
FlipOutYLeft FlipOutYRight LightSpeedInLeft LightSpeedInRight LightSpeedOutLeft
|
|
86
|
+
LightSpeedOutRight PinwheelIn PinwheelOut RollInLeft RollInRight RollOutLeft RollOutRight
|
|
87
|
+
RotateInDownLeft RotateInDownRight RotateInUpLeft RotateInUpRight RotateOutDownLeft
|
|
88
|
+
RotateOutDownRight RotateOutUpLeft RotateOutUpRight SlideInDown SlideInLeft SlideInRight
|
|
89
|
+
SlideInUp SlideOutDown SlideOutLeft SlideOutRight SlideOutUp StretchInX StretchInY
|
|
90
|
+
StretchOutX StretchOutY ZoomIn ZoomInDown ZoomInEasyDown ZoomInEasyUp ZoomInLeft ZoomInRight
|
|
91
|
+
ZoomInRotate ZoomInUp ZoomOut ZoomOutDown ZoomOutEasyDown ZoomOutEasyUp ZoomOutLeft
|
|
92
|
+
ZoomOutRight ZoomOutRotate ZoomOutUp Layout LinearTransition SequencedTransition
|
|
93
|
+
FadingTransition JumpingTransition CurvedTransition EntryExitTransition`.split(/\s+/)
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
const LAYOUT_ANIMATION_CHAIN_METHODS = new Set([
|
|
97
|
+
'build',
|
|
98
|
+
'duration',
|
|
99
|
+
'delay',
|
|
100
|
+
'getDuration',
|
|
101
|
+
'randomDelay',
|
|
102
|
+
'getDelay',
|
|
103
|
+
'getDelayFunction',
|
|
104
|
+
'easing',
|
|
105
|
+
'rotate',
|
|
106
|
+
'springify',
|
|
107
|
+
'damping',
|
|
108
|
+
'mass',
|
|
109
|
+
'stiffness',
|
|
110
|
+
'overshootClamping',
|
|
111
|
+
'energyThreshold',
|
|
112
|
+
'restDisplacementThreshold',
|
|
113
|
+
'restSpeedThreshold',
|
|
114
|
+
'withInitialValues',
|
|
115
|
+
'getAnimationAndConfig',
|
|
116
|
+
'easingX',
|
|
117
|
+
'easingY',
|
|
118
|
+
'easingWidth',
|
|
119
|
+
'easingHeight',
|
|
120
|
+
'entering',
|
|
121
|
+
'exiting',
|
|
122
|
+
'reverse',
|
|
123
|
+
])
|
|
124
|
+
|
|
125
|
+
function isGestureObject(exp: any): boolean {
|
|
126
|
+
return (
|
|
127
|
+
exp?.type === 'CallExpression' &&
|
|
128
|
+
exp.callee?.type === 'MemberExpression' &&
|
|
129
|
+
exp.callee.object?.type === 'Identifier' &&
|
|
130
|
+
exp.callee.object.name === 'Gesture' &&
|
|
131
|
+
exp.callee.property?.type === 'Identifier' &&
|
|
132
|
+
GESTURE_KINDS.has(exp.callee.property.name)
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function containsGestureObject(exp: any): boolean {
|
|
137
|
+
if (isGestureObject(exp)) return true
|
|
138
|
+
return (
|
|
139
|
+
exp?.type === 'CallExpression' &&
|
|
140
|
+
exp.callee?.type === 'MemberExpression' &&
|
|
141
|
+
containsGestureObject(exp.callee.object)
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function isLayoutAnimationChain(exp: any): boolean {
|
|
146
|
+
if (exp?.type === 'Identifier' && LAYOUT_ANIMATIONS.has(exp.name)) return true
|
|
147
|
+
if (
|
|
148
|
+
exp?.type === 'NewExpression' &&
|
|
149
|
+
exp.callee?.type === 'Identifier' &&
|
|
150
|
+
LAYOUT_ANIMATIONS.has(exp.callee.name)
|
|
151
|
+
) {
|
|
152
|
+
return true
|
|
153
|
+
}
|
|
154
|
+
return (
|
|
155
|
+
exp?.type === 'CallExpression' &&
|
|
156
|
+
exp.callee?.type === 'MemberExpression' &&
|
|
157
|
+
exp.callee.property?.type === 'Identifier' &&
|
|
158
|
+
LAYOUT_ANIMATION_CHAIN_METHODS.has(exp.callee.property.name) &&
|
|
159
|
+
isLayoutAnimationChain(exp.callee.object)
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// true when every argument of the call should be workletized rather than the
|
|
164
|
+
// indices a named hook declares.
|
|
165
|
+
function isChainedCallback(callee: any): boolean {
|
|
166
|
+
if (callee?.type !== 'MemberExpression' || callee.property?.type !== 'Identifier') {
|
|
167
|
+
return false
|
|
168
|
+
}
|
|
169
|
+
const name = callee.property.name
|
|
170
|
+
if (GESTURE_BUILDER_METHODS.has(name)) return containsGestureObject(callee.object)
|
|
171
|
+
if (name === 'withCallback') return isLayoutAnimationChain(callee.object)
|
|
172
|
+
return false
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// the worklets directives, in the order the runtime expects to find them.
|
|
176
|
+
export const WORKLET_DIRECTIVES = ['worklet', 'no-worklet-closure', 'limit-init-data-hoisting']
|
|
177
|
+
|
|
178
|
+
function statementDirective(stmt: any): string | undefined {
|
|
179
|
+
if (!stmt || stmt.type !== 'ExpressionStatement') return undefined
|
|
180
|
+
if (typeof stmt.directive === 'string') return stmt.directive
|
|
181
|
+
if (stmt.expression?.type === 'Literal' && typeof stmt.expression.value === 'string') {
|
|
182
|
+
return stmt.expression.value
|
|
183
|
+
}
|
|
184
|
+
return undefined
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function hasDirective(fnNode: any, directive: string): boolean {
|
|
188
|
+
if (!fnNode || !fnNode.body) return false
|
|
189
|
+
|
|
190
|
+
// Directives array in Oxc
|
|
191
|
+
if (Array.isArray(fnNode.body.directives)) {
|
|
192
|
+
for (const d of fnNode.body.directives) {
|
|
193
|
+
if (d.directive === directive || d.value === directive) {
|
|
194
|
+
return true
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// leading directive statements in the body
|
|
200
|
+
if (fnNode.body.type === 'BlockStatement' && Array.isArray(fnNode.body.body)) {
|
|
201
|
+
for (const stmt of fnNode.body.body) {
|
|
202
|
+
const found = statementDirective(stmt)
|
|
203
|
+
if (found === undefined) break
|
|
204
|
+
if (found === directive) return true
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return false
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function hasWorkletDirective(fnNode: any): boolean {
|
|
212
|
+
return hasDirective(fnNode, 'worklet')
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Source offset just past every leading worklets directive in the body, so a
|
|
217
|
+
* body slice drops all of them rather than only the first. Returns the offset
|
|
218
|
+
* of the first real statement.
|
|
219
|
+
*/
|
|
220
|
+
export function bodyStartAfterDirectives(fnNode: any, code: string): number {
|
|
221
|
+
let start = fnNode.body.start + 1
|
|
222
|
+
if (fnNode.body.type !== 'BlockStatement' || !Array.isArray(fnNode.body.body)) return start
|
|
223
|
+
for (const stmt of fnNode.body.body) {
|
|
224
|
+
const found = statementDirective(stmt)
|
|
225
|
+
if (found === undefined || !WORKLET_DIRECTIVES.includes(found)) break
|
|
226
|
+
start = stmt.end
|
|
227
|
+
while (start < fnNode.body.end && (code[start] === ';' || /\s/.test(code[start]))) {
|
|
228
|
+
start++
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return start
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function findWorkletCandidates(program: any): WorkletCandidate[] {
|
|
235
|
+
const candidates: WorkletCandidate[] = []
|
|
236
|
+
const seenStarts = new Set<number>()
|
|
237
|
+
|
|
238
|
+
function addCandidate(candidate: WorkletCandidate) {
|
|
239
|
+
const fnStart = candidate.fnNode.start
|
|
240
|
+
if (seenStarts.has(fnStart)) return
|
|
241
|
+
seenStarts.add(fnStart)
|
|
242
|
+
candidates.push(candidate)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function getCalleeName(callee: any): string | null {
|
|
246
|
+
if (!callee) return null
|
|
247
|
+
if (callee.type === 'Identifier') {
|
|
248
|
+
return callee.name
|
|
249
|
+
}
|
|
250
|
+
if (
|
|
251
|
+
callee.type === 'MemberExpression' &&
|
|
252
|
+
!callee.computed &&
|
|
253
|
+
callee.property?.type === 'Identifier'
|
|
254
|
+
) {
|
|
255
|
+
return callee.property.name
|
|
256
|
+
}
|
|
257
|
+
return null
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function walk(node: any, parent: any) {
|
|
261
|
+
if (!node || typeof node !== 'object') return
|
|
262
|
+
|
|
263
|
+
// Check explicit worklets
|
|
264
|
+
if (
|
|
265
|
+
node.type === 'FunctionDeclaration' ||
|
|
266
|
+
node.type === 'FunctionExpression' ||
|
|
267
|
+
node.type === 'ArrowFunctionExpression'
|
|
268
|
+
) {
|
|
269
|
+
if (hasWorkletDirective(node)) {
|
|
270
|
+
let kind: WorkletCandidate['kind'] = 'function_expression'
|
|
271
|
+
if (node.type === 'FunctionDeclaration') kind = 'function_declaration'
|
|
272
|
+
else if (node.type === 'ArrowFunctionExpression') kind = 'arrow_function'
|
|
273
|
+
|
|
274
|
+
let name = node.id?.name
|
|
275
|
+
if (!name && parent?.type === 'VariableDeclarator' && parent.id?.name) {
|
|
276
|
+
name = parent.id.name
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
addCandidate({
|
|
280
|
+
node,
|
|
281
|
+
fnNode: node,
|
|
282
|
+
kind,
|
|
283
|
+
name,
|
|
284
|
+
parent,
|
|
285
|
+
isAutoWorklet: false,
|
|
286
|
+
})
|
|
287
|
+
}
|
|
288
|
+
} else if (
|
|
289
|
+
node.type === 'Property' &&
|
|
290
|
+
(node.method || node.value?.type === 'FunctionExpression' || node.value?.type === 'ArrowFunctionExpression')
|
|
291
|
+
) {
|
|
292
|
+
const fn = node.value
|
|
293
|
+
if (hasWorkletDirective(fn)) {
|
|
294
|
+
addCandidate({
|
|
295
|
+
node: node.method ? node : fn,
|
|
296
|
+
fnNode: fn,
|
|
297
|
+
kind: node.method
|
|
298
|
+
? 'object_method'
|
|
299
|
+
: fn.type === 'ArrowFunctionExpression'
|
|
300
|
+
? 'arrow_function'
|
|
301
|
+
: 'function_expression',
|
|
302
|
+
name: node.key?.name,
|
|
303
|
+
parent,
|
|
304
|
+
isAutoWorklet: false,
|
|
305
|
+
})
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Check auto-workletized calls
|
|
310
|
+
if (node.type === 'CallExpression') {
|
|
311
|
+
const calleeName = getCalleeName(node.callee)
|
|
312
|
+
const argIndices = calleeName ? AUTOWORKLET_FUNCTION_ARGS[calleeName] : undefined
|
|
313
|
+
const chained = !argIndices && isChainedCallback(node.callee)
|
|
314
|
+
const indices =
|
|
315
|
+
argIndices ?? (chained ? node.arguments.map((_: unknown, i: number) => i) : undefined)
|
|
316
|
+
if (indices) {
|
|
317
|
+
for (const idx of indices) {
|
|
318
|
+
const arg = node.arguments[idx]
|
|
319
|
+
if (!arg) continue
|
|
320
|
+
|
|
321
|
+
if (
|
|
322
|
+
arg.type === 'ArrowFunctionExpression' ||
|
|
323
|
+
arg.type === 'FunctionExpression'
|
|
324
|
+
) {
|
|
325
|
+
let name = arg.id?.name
|
|
326
|
+
if (!name && parent?.type === 'VariableDeclarator' && parent.id?.name) {
|
|
327
|
+
name = parent.id.name
|
|
328
|
+
}
|
|
329
|
+
addCandidate({
|
|
330
|
+
node: arg,
|
|
331
|
+
fnNode: arg,
|
|
332
|
+
kind:
|
|
333
|
+
arg.type === 'ArrowFunctionExpression'
|
|
334
|
+
? 'arrow_function'
|
|
335
|
+
: 'function_expression',
|
|
336
|
+
name,
|
|
337
|
+
parent: node,
|
|
338
|
+
isAutoWorklet: true,
|
|
339
|
+
})
|
|
340
|
+
} else if (arg.type === 'ObjectExpression' && Array.isArray(arg.properties)) {
|
|
341
|
+
for (const prop of arg.properties) {
|
|
342
|
+
if (prop.type === 'Property') {
|
|
343
|
+
if (prop.method) {
|
|
344
|
+
addCandidate({
|
|
345
|
+
node: prop,
|
|
346
|
+
fnNode: prop.value,
|
|
347
|
+
kind: 'object_method',
|
|
348
|
+
name: prop.key?.name,
|
|
349
|
+
parent: arg,
|
|
350
|
+
isAutoWorklet: true,
|
|
351
|
+
})
|
|
352
|
+
} else if (
|
|
353
|
+
prop.value?.type === 'ArrowFunctionExpression' ||
|
|
354
|
+
prop.value?.type === 'FunctionExpression'
|
|
355
|
+
) {
|
|
356
|
+
addCandidate({
|
|
357
|
+
node: prop.value,
|
|
358
|
+
fnNode: prop.value,
|
|
359
|
+
kind:
|
|
360
|
+
prop.value.type === 'ArrowFunctionExpression'
|
|
361
|
+
? 'arrow_function'
|
|
362
|
+
: 'function_expression',
|
|
363
|
+
name: prop.key?.name,
|
|
364
|
+
parent: prop,
|
|
365
|
+
isAutoWorklet: true,
|
|
366
|
+
})
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Default traversal
|
|
376
|
+
for (const key of Object.keys(node)) {
|
|
377
|
+
if (key === 'start' || key === 'end' || key === 'loc' || key === 'range') continue
|
|
378
|
+
const child = node[key]
|
|
379
|
+
if (Array.isArray(child)) {
|
|
380
|
+
for (const item of child) {
|
|
381
|
+
walk(item, node)
|
|
382
|
+
}
|
|
383
|
+
} else if (child && typeof child === 'object') {
|
|
384
|
+
walk(child, node)
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
walk(program, null)
|
|
390
|
+
return candidates
|
|
391
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
export const DEFAULT_GLOBALS = new Set([
|
|
2
|
+
// Value properties
|
|
3
|
+
'globalThis',
|
|
4
|
+
'Infinity',
|
|
5
|
+
'NaN',
|
|
6
|
+
'undefined',
|
|
7
|
+
// Function properties
|
|
8
|
+
'eval',
|
|
9
|
+
'isFinite',
|
|
10
|
+
'isNaN',
|
|
11
|
+
'parseFloat',
|
|
12
|
+
'parseInt',
|
|
13
|
+
'decodeURI',
|
|
14
|
+
'decodeURIComponent',
|
|
15
|
+
'encodeURI',
|
|
16
|
+
'encodeURIComponent',
|
|
17
|
+
'escape',
|
|
18
|
+
'unescape',
|
|
19
|
+
// Fundamental objects
|
|
20
|
+
'Object',
|
|
21
|
+
'Function',
|
|
22
|
+
'Boolean',
|
|
23
|
+
'Symbol',
|
|
24
|
+
// Error objects
|
|
25
|
+
'Error',
|
|
26
|
+
'AggregateError',
|
|
27
|
+
'EvalError',
|
|
28
|
+
'RangeError',
|
|
29
|
+
'ReferenceError',
|
|
30
|
+
'SyntaxError',
|
|
31
|
+
'TypeError',
|
|
32
|
+
'URIError',
|
|
33
|
+
'InternalError',
|
|
34
|
+
// Numbers and dates
|
|
35
|
+
'Number',
|
|
36
|
+
'BigInt',
|
|
37
|
+
'Math',
|
|
38
|
+
'Date',
|
|
39
|
+
// Text processing
|
|
40
|
+
'String',
|
|
41
|
+
'RegExp',
|
|
42
|
+
// Indexed collections
|
|
43
|
+
'Array',
|
|
44
|
+
'Int8Array',
|
|
45
|
+
'Uint8Array',
|
|
46
|
+
'Uint8ClampedArray',
|
|
47
|
+
'Int16Array',
|
|
48
|
+
'Uint16Array',
|
|
49
|
+
'Int32Array',
|
|
50
|
+
'Uint32Array',
|
|
51
|
+
'BigInt64Array',
|
|
52
|
+
'BigUint64Array',
|
|
53
|
+
'Float32Array',
|
|
54
|
+
'Float64Array',
|
|
55
|
+
// Keyed collections
|
|
56
|
+
'Map',
|
|
57
|
+
'Set',
|
|
58
|
+
'WeakMap',
|
|
59
|
+
'WeakSet',
|
|
60
|
+
// Structured data
|
|
61
|
+
'ArrayBuffer',
|
|
62
|
+
'SharedArrayBuffer',
|
|
63
|
+
'DataView',
|
|
64
|
+
'Atomics',
|
|
65
|
+
'JSON',
|
|
66
|
+
// Managing memory
|
|
67
|
+
'WeakRef',
|
|
68
|
+
'FinalizationRegistry',
|
|
69
|
+
// Control abstraction objects
|
|
70
|
+
'Iterator',
|
|
71
|
+
'AsyncIterator',
|
|
72
|
+
'Promise',
|
|
73
|
+
'GeneratorFunction',
|
|
74
|
+
'AsyncGeneratorFunction',
|
|
75
|
+
'Generator',
|
|
76
|
+
'AsyncGenerator',
|
|
77
|
+
'AsyncFunction',
|
|
78
|
+
// Reflection
|
|
79
|
+
'Reflect',
|
|
80
|
+
'Proxy',
|
|
81
|
+
// Internationalization
|
|
82
|
+
'Intl',
|
|
83
|
+
// Environment / Runtime
|
|
84
|
+
'null',
|
|
85
|
+
'this',
|
|
86
|
+
'global',
|
|
87
|
+
'window',
|
|
88
|
+
'self',
|
|
89
|
+
'console',
|
|
90
|
+
'performance',
|
|
91
|
+
'arguments',
|
|
92
|
+
'require',
|
|
93
|
+
'fetch',
|
|
94
|
+
'XMLHttpRequest',
|
|
95
|
+
'WebSocket',
|
|
96
|
+
'queueMicrotask',
|
|
97
|
+
'requestAnimationFrame',
|
|
98
|
+
'cancelAnimationFrame',
|
|
99
|
+
'setTimeout',
|
|
100
|
+
'clearTimeout',
|
|
101
|
+
'setImmediate',
|
|
102
|
+
'clearImmediate',
|
|
103
|
+
'setInterval',
|
|
104
|
+
'clearInterval',
|
|
105
|
+
'HermesInternal',
|
|
106
|
+
'_WORKLET',
|
|
107
|
+
])
|
|
108
|
+
|
|
109
|
+
export function createGlobalsSet(
|
|
110
|
+
customGlobals?: string[],
|
|
111
|
+
strictGlobal?: boolean
|
|
112
|
+
): Set<string> {
|
|
113
|
+
const set = new Set<string>()
|
|
114
|
+
if (!strictGlobal) {
|
|
115
|
+
for (const g of DEFAULT_GLOBALS) {
|
|
116
|
+
set.add(g)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (customGlobals) {
|
|
120
|
+
for (const g of customGlobals) {
|
|
121
|
+
set.add(g)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return set
|
|
125
|
+
}
|