@vxrn/compiler 1.25.16 → 1.26.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.
Files changed (130) hide show
  1. package/dist/cjs/cache.cjs +4 -2
  2. package/dist/cjs/configure.cjs +9 -1
  3. package/dist/cjs/index.cjs +87 -5
  4. package/dist/cjs/reactNativeCodegen.cjs +140 -0
  5. package/dist/cjs/reactNativeViewConfig.cjs +489 -0
  6. package/dist/cjs/transformBabel.cjs +15 -20
  7. package/dist/cjs/transformBabel.test.cjs +260 -0
  8. package/dist/cjs/transformHermesAsync.cjs +61 -0
  9. package/dist/cjs/transformHermesLoops.cjs +319 -0
  10. package/dist/cjs/transformHermesLoops.test.cjs +257 -0
  11. package/dist/cjs/transformSWC.cjs +110 -138
  12. package/dist/cjs/transformSWC.test.cjs +117 -0
  13. package/dist/cjs/transformWorklets.cjs +123 -0
  14. package/dist/cjs/transformWorklets.test.cjs +604 -0
  15. package/dist/cjs/worklets/autoworklet.cjs +265 -0
  16. package/dist/cjs/worklets/globals.cjs +73 -0
  17. package/dist/cjs/worklets/hash.cjs +38 -0
  18. package/dist/cjs/worklets/scope.cjs +370 -0
  19. package/dist/cjs/worklets/serialize.cjs +101 -0
  20. package/dist/cjs/worklets/transform.cjs +266 -0
  21. package/dist/cjs/worklets/types.cjs +18 -0
  22. package/dist/esm/cache.mjs +5 -3
  23. package/dist/esm/cache.mjs.map +1 -1
  24. package/dist/esm/configure.mjs +8 -1
  25. package/dist/esm/configure.mjs.map +1 -1
  26. package/dist/esm/index.js +75 -6
  27. package/dist/esm/index.js.map +1 -1
  28. package/dist/esm/index.mjs +75 -6
  29. package/dist/esm/index.mjs.map +1 -1
  30. package/dist/esm/reactNativeCodegen.mjs +104 -0
  31. package/dist/esm/reactNativeCodegen.mjs.map +1 -0
  32. package/dist/esm/reactNativeViewConfig.mjs +460 -0
  33. package/dist/esm/reactNativeViewConfig.mjs.map +1 -0
  34. package/dist/esm/transformBabel.mjs +14 -19
  35. package/dist/esm/transformBabel.mjs.map +1 -1
  36. package/dist/esm/transformBabel.test.mjs +261 -1
  37. package/dist/esm/transformBabel.test.mjs.map +1 -1
  38. package/dist/esm/transformHermesAsync.mjs +25 -0
  39. package/dist/esm/transformHermesAsync.mjs.map +1 -0
  40. package/dist/esm/transformHermesLoops.mjs +283 -0
  41. package/dist/esm/transformHermesLoops.mjs.map +1 -0
  42. package/dist/esm/transformHermesLoops.test.mjs +234 -0
  43. package/dist/esm/transformHermesLoops.test.mjs.map +1 -0
  44. package/dist/esm/transformSWC.mjs +96 -139
  45. package/dist/esm/transformSWC.mjs.map +1 -1
  46. package/dist/esm/transformSWC.test.mjs +118 -0
  47. package/dist/esm/transformSWC.test.mjs.map +1 -0
  48. package/dist/esm/transformWorklets.mjs +82 -0
  49. package/dist/esm/transformWorklets.mjs.map +1 -0
  50. package/dist/esm/transformWorklets.test.mjs +581 -0
  51. package/dist/esm/transformWorklets.test.mjs.map +1 -0
  52. package/dist/esm/worklets/autoworklet.mjs +234 -0
  53. package/dist/esm/worklets/autoworklet.mjs.map +1 -0
  54. package/dist/esm/worklets/globals.mjs +46 -0
  55. package/dist/esm/worklets/globals.mjs.map +1 -0
  56. package/dist/esm/worklets/hash.mjs +13 -0
  57. package/dist/esm/worklets/hash.mjs.map +1 -0
  58. package/dist/esm/worklets/scope.mjs +345 -0
  59. package/dist/esm/worklets/scope.mjs.map +1 -0
  60. package/dist/esm/worklets/serialize.mjs +75 -0
  61. package/dist/esm/worklets/serialize.mjs.map +1 -0
  62. package/dist/esm/worklets/transform.mjs +229 -0
  63. package/dist/esm/worklets/transform.mjs.map +1 -0
  64. package/dist/esm/worklets/types.mjs +2 -0
  65. package/dist/esm/worklets/types.mjs.map +1 -0
  66. package/package.json +11 -6
  67. package/src/cache.ts +10 -3
  68. package/src/configure.ts +12 -0
  69. package/src/index.ts +96 -13
  70. package/src/reactNativeCodegen.ts +181 -0
  71. package/src/reactNativeViewConfig.ts +593 -0
  72. package/src/transformBabel.test.ts +268 -1
  73. package/src/transformBabel.ts +21 -31
  74. package/src/transformHermesAsync.ts +30 -0
  75. package/src/transformHermesLoops.test.ts +341 -0
  76. package/src/transformHermesLoops.ts +435 -0
  77. package/src/transformSWC.test.ts +132 -0
  78. package/src/transformSWC.ts +121 -161
  79. package/src/transformWorklets.test.ts +708 -0
  80. package/src/transformWorklets.ts +160 -0
  81. package/src/types.ts +4 -4
  82. package/src/worklets/autoworklet.ts +391 -0
  83. package/src/worklets/globals.ts +129 -0
  84. package/src/worklets/hash.ts +14 -0
  85. package/src/worklets/scope.ts +401 -0
  86. package/src/worklets/serialize.ts +114 -0
  87. package/src/worklets/transform.ts +319 -0
  88. package/src/worklets/types.ts +26 -0
  89. package/types/cache.d.ts +1 -0
  90. package/types/cache.d.ts.map +1 -1
  91. package/types/configure.d.ts +2 -0
  92. package/types/configure.d.ts.map +1 -1
  93. package/types/index.d.ts +6 -0
  94. package/types/index.d.ts.map +1 -1
  95. package/types/reactNativeCodegen.d.ts +5 -0
  96. package/types/reactNativeCodegen.d.ts.map +1 -0
  97. package/types/reactNativeViewConfig.d.ts +35 -0
  98. package/types/reactNativeViewConfig.d.ts.map +1 -0
  99. package/types/transformBabel.d.ts +3 -3
  100. package/types/transformBabel.d.ts.map +1 -1
  101. package/types/transformHermesAsync.d.ts +5 -0
  102. package/types/transformHermesAsync.d.ts.map +1 -0
  103. package/types/transformHermesLoops.d.ts +5 -0
  104. package/types/transformHermesLoops.d.ts.map +1 -0
  105. package/types/transformHermesLoops.test.d.ts +2 -0
  106. package/types/transformHermesLoops.test.d.ts.map +1 -0
  107. package/types/transformSWC.d.ts +13 -3
  108. package/types/transformSWC.d.ts.map +1 -1
  109. package/types/transformSWC.test.d.ts +2 -0
  110. package/types/transformSWC.test.d.ts.map +1 -0
  111. package/types/transformWorklets.d.ts +23 -0
  112. package/types/transformWorklets.d.ts.map +1 -0
  113. package/types/transformWorklets.test.d.ts +2 -0
  114. package/types/transformWorklets.test.d.ts.map +1 -0
  115. package/types/types.d.ts +4 -4
  116. package/types/types.d.ts.map +1 -1
  117. package/types/worklets/autoworklet.d.ts +14 -0
  118. package/types/worklets/autoworklet.d.ts.map +1 -0
  119. package/types/worklets/globals.d.ts +4 -0
  120. package/types/worklets/globals.d.ts.map +1 -0
  121. package/types/worklets/hash.d.ts +5 -0
  122. package/types/worklets/hash.d.ts.map +1 -0
  123. package/types/worklets/scope.d.ts +7 -0
  124. package/types/worklets/scope.d.ts.map +1 -0
  125. package/types/worklets/serialize.d.ts +12 -0
  126. package/types/worklets/serialize.d.ts.map +1 -0
  127. package/types/worklets/transform.d.ts +10 -0
  128. package/types/worklets/transform.d.ts.map +1 -0
  129. package/types/worklets/types.d.ts +21 -0
  130. 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
+ }