@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,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic hash algorithm matching Reanimated / React Native Worklets.
|
|
3
|
+
*/
|
|
4
|
+
export function calculateWorkletHash(str: string): number {
|
|
5
|
+
let i = str.length
|
|
6
|
+
let hash1 = 5381
|
|
7
|
+
let hash2 = 52711
|
|
8
|
+
while (i--) {
|
|
9
|
+
const char = str.charCodeAt(i)
|
|
10
|
+
hash1 = (hash1 * 33) ^ char
|
|
11
|
+
hash2 = (hash2 * 33) ^ char
|
|
12
|
+
}
|
|
13
|
+
return (hash1 >>> 0) * 4096 + (hash2 >>> 0)
|
|
14
|
+
}
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lexical scope analysis and free variable capture for worklets.
|
|
3
|
+
* Builds an authentic lexical scope tree to correctly handle variable shadowing,
|
|
4
|
+
* block scoping (let/const), function hoisting, and catch parameters.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
interface ScopeFrame {
|
|
8
|
+
parent: ScopeFrame | null
|
|
9
|
+
isFunction: boolean
|
|
10
|
+
bindings: Set<string> // block-scoped declarations (let, const, class, params, catch)
|
|
11
|
+
varBindings: Set<string> // function-scoped declarations (var, top-level function declarations)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getClosureVariables(fnNode: any, globals: Set<string>): string[] {
|
|
15
|
+
const rootScope: ScopeFrame = {
|
|
16
|
+
parent: null,
|
|
17
|
+
isFunction: true,
|
|
18
|
+
bindings: new Set<string>(),
|
|
19
|
+
varBindings: new Set<string>(),
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let currentScope: ScopeFrame = rootScope
|
|
23
|
+
const freeVariables = new Set<string>()
|
|
24
|
+
|
|
25
|
+
// Function name itself if named (in recursive worklets, fn can refer to itself)
|
|
26
|
+
if (fnNode.id && fnNode.id.name) {
|
|
27
|
+
rootScope.bindings.add(fnNode.id.name)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function pushScope(isFunction: boolean): ScopeFrame {
|
|
31
|
+
const newScope: ScopeFrame = {
|
|
32
|
+
parent: currentScope,
|
|
33
|
+
isFunction,
|
|
34
|
+
bindings: new Set<string>(),
|
|
35
|
+
varBindings: isFunction ? new Set<string>() : currentScope.varBindings,
|
|
36
|
+
}
|
|
37
|
+
currentScope = newScope
|
|
38
|
+
return newScope
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function popScope() {
|
|
42
|
+
if (currentScope.parent) {
|
|
43
|
+
currentScope = currentScope.parent
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function addBindings(pattern: any, targetSet: Set<string>) {
|
|
48
|
+
if (!pattern) return
|
|
49
|
+
switch (pattern.type) {
|
|
50
|
+
case 'Identifier':
|
|
51
|
+
targetSet.add(pattern.name)
|
|
52
|
+
break
|
|
53
|
+
case 'AssignmentPattern':
|
|
54
|
+
addBindings(pattern.left, targetSet)
|
|
55
|
+
walk(pattern.right)
|
|
56
|
+
break
|
|
57
|
+
case 'RestElement':
|
|
58
|
+
addBindings(pattern.argument, targetSet)
|
|
59
|
+
break
|
|
60
|
+
case 'ArrayPattern':
|
|
61
|
+
for (const el of pattern.elements) {
|
|
62
|
+
if (el) addBindings(el, targetSet)
|
|
63
|
+
}
|
|
64
|
+
break
|
|
65
|
+
case 'ObjectPattern':
|
|
66
|
+
for (const prop of pattern.properties) {
|
|
67
|
+
if (prop.type === 'Property') {
|
|
68
|
+
if (prop.computed) walk(prop.key)
|
|
69
|
+
addBindings(prop.value, targetSet)
|
|
70
|
+
} else if (prop.type === 'RestElement') {
|
|
71
|
+
addBindings(prop.argument, targetSet)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
break
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// same as addBindings but never walks a default value, so hoisting cannot
|
|
79
|
+
// resolve an identifier before the scope's declarations are all known.
|
|
80
|
+
function addBindingsOnly(pattern: any, targetSet: Set<string>) {
|
|
81
|
+
if (!pattern) return
|
|
82
|
+
switch (pattern.type) {
|
|
83
|
+
case 'Identifier':
|
|
84
|
+
targetSet.add(pattern.name)
|
|
85
|
+
break
|
|
86
|
+
case 'AssignmentPattern':
|
|
87
|
+
addBindingsOnly(pattern.left, targetSet)
|
|
88
|
+
break
|
|
89
|
+
case 'RestElement':
|
|
90
|
+
addBindingsOnly(pattern.argument, targetSet)
|
|
91
|
+
break
|
|
92
|
+
case 'ArrayPattern':
|
|
93
|
+
for (const el of pattern.elements) {
|
|
94
|
+
if (el) addBindingsOnly(el, targetSet)
|
|
95
|
+
}
|
|
96
|
+
break
|
|
97
|
+
case 'ObjectPattern':
|
|
98
|
+
for (const prop of pattern.properties) {
|
|
99
|
+
if (prop.type === 'Property') {
|
|
100
|
+
addBindingsOnly(prop.value, targetSet)
|
|
101
|
+
} else if (prop.type === 'RestElement') {
|
|
102
|
+
addBindingsOnly(prop.argument, targetSet)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
break
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Declarations a scope already has before its first statement runs: functions,
|
|
111
|
+
* classes and let/const at this level, plus `var` from anywhere below that is
|
|
112
|
+
* not inside a nested function. Without this a name used before its
|
|
113
|
+
* declaration, which hoisting makes legal, looks like a captured variable and
|
|
114
|
+
* ends up in the worklet's closure, where it does not exist.
|
|
115
|
+
*/
|
|
116
|
+
function hoistDeclarations(node: any, direct: boolean) {
|
|
117
|
+
if (!node || typeof node !== 'object' || typeof node.type !== 'string') return
|
|
118
|
+
switch (node.type) {
|
|
119
|
+
case 'FunctionDeclaration':
|
|
120
|
+
if (node.id?.name) {
|
|
121
|
+
currentScope.bindings.add(node.id.name)
|
|
122
|
+
currentScope.varBindings.add(node.id.name)
|
|
123
|
+
}
|
|
124
|
+
return
|
|
125
|
+
case 'FunctionExpression':
|
|
126
|
+
case 'ArrowFunctionExpression':
|
|
127
|
+
return
|
|
128
|
+
case 'ClassDeclaration':
|
|
129
|
+
if (direct && node.id?.name) currentScope.bindings.add(node.id.name)
|
|
130
|
+
return
|
|
131
|
+
case 'VariableDeclaration':
|
|
132
|
+
if (node.kind === 'var') {
|
|
133
|
+
for (const d of node.declarations) addBindingsOnly(d.id, currentScope.varBindings)
|
|
134
|
+
} else if (direct) {
|
|
135
|
+
for (const d of node.declarations) addBindingsOnly(d.id, currentScope.bindings)
|
|
136
|
+
}
|
|
137
|
+
return
|
|
138
|
+
case 'ExportNamedDeclaration':
|
|
139
|
+
case 'ExportDefaultDeclaration':
|
|
140
|
+
hoistDeclarations(node.declaration, direct)
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
for (const key of Object.keys(node)) {
|
|
144
|
+
if (key === 'start' || key === 'end' || key === 'loc' || key === 'range') continue
|
|
145
|
+
const child = node[key]
|
|
146
|
+
if (Array.isArray(child)) {
|
|
147
|
+
for (const item of child) hoistDeclarations(item, false)
|
|
148
|
+
} else if (child && typeof child === 'object') {
|
|
149
|
+
hoistDeclarations(child, false)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Parameters belong to the root function scope
|
|
155
|
+
if (fnNode.params) {
|
|
156
|
+
for (const param of fnNode.params) {
|
|
157
|
+
addBindings(param, rootScope.bindings)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function isDeclaredInScopes(name: string, fromScope: ScopeFrame): boolean {
|
|
162
|
+
let s: ScopeFrame | null = fromScope
|
|
163
|
+
while (s) {
|
|
164
|
+
if (s.bindings.has(name) || s.varBindings.has(name)) {
|
|
165
|
+
return true
|
|
166
|
+
}
|
|
167
|
+
s = s.parent
|
|
168
|
+
}
|
|
169
|
+
return false
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function resolveIdentifier(name: string) {
|
|
173
|
+
if (!isDeclaredInScopes(name, currentScope) && !globals.has(name)) {
|
|
174
|
+
freeVariables.add(name)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function walk(node: any) {
|
|
179
|
+
if (!node || typeof node !== 'object') return
|
|
180
|
+
|
|
181
|
+
// Skip TypeScript type annotations entirely
|
|
182
|
+
if (
|
|
183
|
+
node.type === 'TSTypeAnnotation' ||
|
|
184
|
+
node.type === 'TSTypeReference' ||
|
|
185
|
+
node.type === 'TSTypeAliasDeclaration' ||
|
|
186
|
+
node.type === 'TSInterfaceDeclaration' ||
|
|
187
|
+
node.type === 'TSTypeParameterDeclaration' ||
|
|
188
|
+
node.type === 'TSTypeParameterInstantiation' ||
|
|
189
|
+
node.type === 'TSTypeQuery' ||
|
|
190
|
+
node.type === 'TSQualifiedName'
|
|
191
|
+
) {
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// TS type wrapper expressions: only walk the runtime expression
|
|
196
|
+
if (
|
|
197
|
+
node.type === 'TSAsExpression' ||
|
|
198
|
+
node.type === 'TSSatisfiesExpression' ||
|
|
199
|
+
node.type === 'TSTypeAssertion' ||
|
|
200
|
+
node.type === 'TSNonNullExpression' ||
|
|
201
|
+
node.type === 'TSInstantiationExpression'
|
|
202
|
+
) {
|
|
203
|
+
walk(node.expression)
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
switch (node.type) {
|
|
208
|
+
case 'BlockStatement': {
|
|
209
|
+
const isRootBody = node === fnNode.body
|
|
210
|
+
if (!isRootBody) {
|
|
211
|
+
pushScope(false)
|
|
212
|
+
}
|
|
213
|
+
for (const stmt of node.body) {
|
|
214
|
+
hoistDeclarations(stmt, true)
|
|
215
|
+
}
|
|
216
|
+
for (const stmt of node.body) {
|
|
217
|
+
walk(stmt)
|
|
218
|
+
}
|
|
219
|
+
if (!isRootBody) {
|
|
220
|
+
popScope()
|
|
221
|
+
}
|
|
222
|
+
return
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
case 'VariableDeclaration': {
|
|
226
|
+
const isVar = node.kind === 'var'
|
|
227
|
+
const targetSet = isVar ? currentScope.varBindings : currentScope.bindings
|
|
228
|
+
for (const decl of node.declarations) {
|
|
229
|
+
addBindings(decl.id, targetSet)
|
|
230
|
+
if (decl.init) walk(decl.init)
|
|
231
|
+
}
|
|
232
|
+
return
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
case 'FunctionDeclaration':
|
|
236
|
+
case 'FunctionExpression': {
|
|
237
|
+
if (node.id && node.id.name) {
|
|
238
|
+
currentScope.bindings.add(node.id.name)
|
|
239
|
+
}
|
|
240
|
+
pushScope(true)
|
|
241
|
+
if (node.params) {
|
|
242
|
+
for (const param of node.params) {
|
|
243
|
+
addBindings(param, currentScope.bindings)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
walk(node.body)
|
|
247
|
+
popScope()
|
|
248
|
+
return
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
case 'ArrowFunctionExpression': {
|
|
252
|
+
pushScope(true)
|
|
253
|
+
if (node.params) {
|
|
254
|
+
for (const param of node.params) {
|
|
255
|
+
addBindings(param, currentScope.bindings)
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
walk(node.body)
|
|
259
|
+
popScope()
|
|
260
|
+
return
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
case 'CatchClause': {
|
|
264
|
+
pushScope(false)
|
|
265
|
+
if (node.param) {
|
|
266
|
+
addBindings(node.param, currentScope.bindings)
|
|
267
|
+
}
|
|
268
|
+
walk(node.body)
|
|
269
|
+
popScope()
|
|
270
|
+
return
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
case 'ForStatement': {
|
|
274
|
+
pushScope(false)
|
|
275
|
+
if (node.init) walk(node.init)
|
|
276
|
+
if (node.test) walk(node.test)
|
|
277
|
+
if (node.update) walk(node.update)
|
|
278
|
+
walk(node.body)
|
|
279
|
+
popScope()
|
|
280
|
+
return
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
case 'ForInStatement':
|
|
284
|
+
case 'ForOfStatement': {
|
|
285
|
+
pushScope(false)
|
|
286
|
+
walk(node.left)
|
|
287
|
+
walk(node.right)
|
|
288
|
+
walk(node.body)
|
|
289
|
+
popScope()
|
|
290
|
+
return
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
case 'ClassDeclaration':
|
|
294
|
+
case 'ClassExpression': {
|
|
295
|
+
if (node.id && node.id.name) {
|
|
296
|
+
currentScope.bindings.add(node.id.name)
|
|
297
|
+
}
|
|
298
|
+
if (node.superClass) walk(node.superClass)
|
|
299
|
+
pushScope(false)
|
|
300
|
+
walk(node.body)
|
|
301
|
+
popScope()
|
|
302
|
+
return
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
case 'MemberExpression': {
|
|
306
|
+
walk(node.object)
|
|
307
|
+
if (node.computed) {
|
|
308
|
+
walk(node.property)
|
|
309
|
+
}
|
|
310
|
+
return
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
case 'Property': {
|
|
314
|
+
if (node.computed) {
|
|
315
|
+
walk(node.key)
|
|
316
|
+
}
|
|
317
|
+
walk(node.value)
|
|
318
|
+
return
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
case 'MethodDefinition': {
|
|
322
|
+
if (node.computed) {
|
|
323
|
+
walk(node.key)
|
|
324
|
+
}
|
|
325
|
+
walk(node.value)
|
|
326
|
+
return
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
case 'Identifier': {
|
|
330
|
+
resolveIdentifier(node.name)
|
|
331
|
+
return
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
case 'JSXElement': {
|
|
335
|
+
if (node.openingElement) {
|
|
336
|
+
const nameNode = node.openingElement.name
|
|
337
|
+
if (nameNode.type === 'JSXIdentifier') {
|
|
338
|
+
if (/^[A-Z]/.test(nameNode.name)) {
|
|
339
|
+
resolveIdentifier(nameNode.name)
|
|
340
|
+
}
|
|
341
|
+
} else if (nameNode.type === 'JSXMemberExpression') {
|
|
342
|
+
let root = nameNode.object
|
|
343
|
+
while (root.type === 'JSXMemberExpression') {
|
|
344
|
+
root = root.object
|
|
345
|
+
}
|
|
346
|
+
if (root.type === 'JSXIdentifier') {
|
|
347
|
+
resolveIdentifier(root.name)
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (node.openingElement.attributes) {
|
|
351
|
+
for (const attr of node.openingElement.attributes) {
|
|
352
|
+
walk(attr)
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (node.children) {
|
|
357
|
+
for (const child of node.children) {
|
|
358
|
+
walk(child)
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
case 'JSXAttribute': {
|
|
365
|
+
if (node.value) {
|
|
366
|
+
walk(node.value)
|
|
367
|
+
}
|
|
368
|
+
return
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
case 'JSXExpressionContainer': {
|
|
372
|
+
walk(node.expression)
|
|
373
|
+
return
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
case 'BreakStatement':
|
|
377
|
+
case 'ContinueStatement':
|
|
378
|
+
case 'LabeledStatement': {
|
|
379
|
+
if (node.body) walk(node.body)
|
|
380
|
+
return
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Default recursive traversal
|
|
385
|
+
for (const key of Object.keys(node)) {
|
|
386
|
+
if (key === 'start' || key === 'end' || key === 'loc' || key === 'range') continue
|
|
387
|
+
const child = node[key]
|
|
388
|
+
if (Array.isArray(child)) {
|
|
389
|
+
for (const item of child) {
|
|
390
|
+
walk(item)
|
|
391
|
+
}
|
|
392
|
+
} else if (child && typeof child === 'object') {
|
|
393
|
+
walk(child)
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
walk(fnNode.body)
|
|
399
|
+
|
|
400
|
+
return Array.from(freeVariables).sort()
|
|
401
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { transformSync } from 'oxc-transform'
|
|
2
|
+
import { bodyStartAfterDirectives } from './autoworklet'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* oxc returns empty code plus errors rather than throwing, and an empty
|
|
6
|
+
* `__initData.code` only fails much later on a worklet runtime with a message
|
|
7
|
+
* that names none of this. So refuse to emit one.
|
|
8
|
+
*/
|
|
9
|
+
function assertTransformed(
|
|
10
|
+
transformed: { code: string; errors?: any[] },
|
|
11
|
+
name: string | undefined,
|
|
12
|
+
what: string
|
|
13
|
+
) {
|
|
14
|
+
const error = transformed.errors?.[0]
|
|
15
|
+
if (error) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
`[worklets] failed to build ${what} code for ${name || 'anonymous worklet'}: ${error.message || error}`
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
if (!transformed.code.trim()) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`[worklets] built empty ${what} code for ${name || 'anonymous worklet'}`
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Serializes a worklet function into a string for the Hermes UI thread (__initData.code).
|
|
29
|
+
* Includes injected unpacker: `const { var1, var2 } = this.__closure ?? this._closure;`
|
|
30
|
+
* Strips TypeScript types and formats via oxc-transform.
|
|
31
|
+
*/
|
|
32
|
+
export function serializeWorkletForUI(
|
|
33
|
+
fnNode: any,
|
|
34
|
+
code: string,
|
|
35
|
+
name: string | undefined,
|
|
36
|
+
closureVars: string[]
|
|
37
|
+
): string {
|
|
38
|
+
let rawParams = ''
|
|
39
|
+
if (fnNode.params && fnNode.params.length > 0) {
|
|
40
|
+
const pStart = fnNode.params[0].start
|
|
41
|
+
const pEnd = fnNode.params[fnNode.params.length - 1].end
|
|
42
|
+
rawParams = code.slice(pStart, pEnd)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let rawBody = ''
|
|
46
|
+
if (fnNode.body.type === 'BlockStatement') {
|
|
47
|
+
const bodyStart = bodyStartAfterDirectives(fnNode, code)
|
|
48
|
+
const bodyEnd = fnNode.body.end - 1
|
|
49
|
+
rawBody = code.slice(bodyStart, bodyEnd)
|
|
50
|
+
} else {
|
|
51
|
+
rawBody = `return ${code.slice(fnNode.body.start, fnNode.body.end)};`
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const unpacker = closureVars.length > 0
|
|
55
|
+
? `const { ${closureVars.join(', ')} } = this.__closure ?? this._closure;\n`
|
|
56
|
+
: ''
|
|
57
|
+
|
|
58
|
+
const asyncPrefix = fnNode.async ? 'async ' : ''
|
|
59
|
+
const genPrefix = fnNode.generator ? '*' : ''
|
|
60
|
+
const fnName = name || '_worklet'
|
|
61
|
+
|
|
62
|
+
const fullFn = `${asyncPrefix}function${genPrefix} ${fnName}(${rawParams}) {\n${unpacker}${rawBody}\n}`
|
|
63
|
+
|
|
64
|
+
const transformed = transformSync('worklet.ts', fullFn)
|
|
65
|
+
assertTransformed(transformed, name, 'serialized')
|
|
66
|
+
return transformed.code.trim()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Builds the local JavaScript function to run on the JS thread inside the worklet IIFE.
|
|
71
|
+
* Strips TypeScript types and the 'worklet' directive.
|
|
72
|
+
*/
|
|
73
|
+
export function buildLocalFunction(
|
|
74
|
+
fnNode: any,
|
|
75
|
+
code: string,
|
|
76
|
+
name: string | undefined
|
|
77
|
+
): string {
|
|
78
|
+
let rawParams = ''
|
|
79
|
+
if (fnNode.params && fnNode.params.length > 0) {
|
|
80
|
+
const pStart = fnNode.params[0].start
|
|
81
|
+
const pEnd = fnNode.params[fnNode.params.length - 1].end
|
|
82
|
+
rawParams = code.slice(pStart, pEnd)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let rawBody = ''
|
|
86
|
+
if (fnNode.body.type === 'BlockStatement') {
|
|
87
|
+
const bodyStart = bodyStartAfterDirectives(fnNode, code)
|
|
88
|
+
const bodyEnd = fnNode.body.end - 1
|
|
89
|
+
rawBody = code.slice(bodyStart, bodyEnd)
|
|
90
|
+
} else {
|
|
91
|
+
rawBody = code.slice(fnNode.body.start, fnNode.body.end)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let rawFn = ''
|
|
95
|
+
const asyncPrefix = fnNode.async ? 'async ' : ''
|
|
96
|
+
const genPrefix = fnNode.generator ? '*' : ''
|
|
97
|
+
|
|
98
|
+
if (fnNode.type === 'ArrowFunctionExpression') {
|
|
99
|
+
if (fnNode.body.type === 'BlockStatement') {
|
|
100
|
+
rawFn = `${asyncPrefix}(${rawParams}) => {\n${rawBody}\n}`
|
|
101
|
+
} else {
|
|
102
|
+
rawFn = `${asyncPrefix}(${rawParams}) => (${rawBody})`
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
// an anonymous `function (x) {}` does not parse on its own, and this is
|
|
106
|
+
// emitted into `var <localName> = ...`, where a name binds only inside the
|
|
107
|
+
// function itself.
|
|
108
|
+
rawFn = `${asyncPrefix}function${genPrefix} ${name || '_worklet'}(${rawParams}) {\n${rawBody}\n}`
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const transformed = transformSync('local.ts', rawFn)
|
|
112
|
+
assertTransformed(transformed, name, 'local')
|
|
113
|
+
return transformed.code.trim().replace(/;$/, '')
|
|
114
|
+
}
|