@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
@@ -3,7 +3,11 @@ import os from 'node:os'
3
3
  import path from 'node:path'
4
4
  import { afterEach, describe, expect, it } from 'vitest'
5
5
  import { configureVXRNCompilerPlugin } from './configure'
6
- import { getBabelOptions, transformBabel } from './transformBabel'
6
+ import {
7
+ getBabelOptions,
8
+ transformBabel,
9
+ transformOxcReactCompiler,
10
+ } from './transformBabel'
7
11
 
8
12
  afterEach(() => {
9
13
  configureVXRNCompilerPlugin({ enableReanimated: false })
@@ -79,3 +83,266 @@ describe('transformBabel Flow parsing', () => {
79
83
  ).rejects.toThrow('NATIVE_TRANSFORM_NEGATIVE_CONTROL')
80
84
  })
81
85
  })
86
+
87
+ describe('transformOxcReactCompiler', () => {
88
+ const componentCode = `
89
+ import { useState } from 'react'
90
+
91
+ export function Counter() {
92
+ const [count, setCount] = useState(0)
93
+ return <button onClick={() => setCount(count + 1)}>{count}</button>
94
+ }
95
+ `
96
+
97
+ it('compiles and memoizes React components with Rust React Compiler', async () => {
98
+ const result = await transformOxcReactCompiler(
99
+ '/project/Counter.tsx',
100
+ componentCode,
101
+ '19',
102
+ false
103
+ )
104
+
105
+ expect(result.code).toContain('_c(')
106
+ expect(result.code).toContain('react/compiler-runtime')
107
+ expect(result.map).toBeUndefined()
108
+ })
109
+
110
+ it('generates source maps when requested', async () => {
111
+ const result = await transformOxcReactCompiler(
112
+ '/project/Counter.tsx',
113
+ componentCode,
114
+ '19',
115
+ true
116
+ )
117
+
118
+ expect(result.code).toContain('_c(')
119
+ expect(result.map).toBeDefined()
120
+ expect(result.map?.mappings).toBeDefined()
121
+ })
122
+
123
+ it('supports target 18', async () => {
124
+ const result = await transformOxcReactCompiler(
125
+ '/project/Counter.tsx',
126
+ componentCode,
127
+ '18',
128
+ false
129
+ )
130
+
131
+ expect(result.code).toContain('_c(')
132
+ })
133
+ })
134
+
135
+ describe('compiler plugin multi-stage source map composition', () => {
136
+ it('composes earlier compiler maps when Babel also runs', async () => {
137
+ const { createVXRNCompilerPlugin } = await import('./index')
138
+ const { TraceMap, originalPositionFor } = await import('@jridgewell/trace-mapping')
139
+
140
+ const origSourceMap = process.env.VXRN_ENABLE_SOURCE_MAP
141
+ const origBabelGen = process.env.VXRN_USE_BABEL_FOR_GENERATORS
142
+ process.env.VXRN_ENABLE_SOURCE_MAP = '1'
143
+ process.env.VXRN_USE_BABEL_FOR_GENERATORS = '1'
144
+ configureVXRNCompilerPlugin({ enableCompiler: true })
145
+
146
+ const tempFile = path.join(os.tmpdir(), `Counter-${Date.now()}.tsx`)
147
+ fs.writeFileSync(
148
+ tempFile,
149
+ `import { useState } from 'react'
150
+
151
+ export function Counter() {
152
+ const [count, setCount] = useState(0)
153
+ return <button onClick={() => setCount(count + 1)}>{count}</button>
154
+ }
155
+
156
+ export async function* generatorProbe() {
157
+ throw new Error("MARKER")
158
+ }
159
+ `
160
+ )
161
+
162
+ try {
163
+ const plugins = await createVXRNCompilerPlugin()
164
+ const plugin = plugins.find((p: any) => p.name === 'one:compiler') as any
165
+ await plugin.configResolved({ root: os.tmpdir(), build: {} })
166
+ const hook = plugin.transform.handler || plugin.transform
167
+
168
+ const code = fs.readFileSync(tempFile, 'utf8')
169
+ const context = { environment: { name: 'client' } }
170
+ const result = await hook.call(context, code, tempFile)
171
+
172
+ expect(result).toBeDefined()
173
+ expect(result.map).toBeDefined()
174
+
175
+ const tracer = new TraceMap(result.map)
176
+ const lines = result.code.split('\n')
177
+ const markerLineIndex = lines.findIndex((l: string) => l.includes('MARKER'))
178
+ expect(markerLineIndex).toBeGreaterThan(0)
179
+ const markerCol = lines[markerLineIndex].indexOf('throw')
180
+
181
+ const pos = originalPositionFor(tracer, {
182
+ line: markerLineIndex + 1,
183
+ column: markerCol,
184
+ })
185
+
186
+ expect(pos.source).toBe(tempFile)
187
+ expect(pos.line).toBe(9)
188
+ } finally {
189
+ if (origSourceMap !== undefined) {
190
+ process.env.VXRN_ENABLE_SOURCE_MAP = origSourceMap
191
+ } else {
192
+ delete process.env.VXRN_ENABLE_SOURCE_MAP
193
+ }
194
+ if (origBabelGen !== undefined) {
195
+ process.env.VXRN_USE_BABEL_FOR_GENERATORS = origBabelGen
196
+ } else {
197
+ delete process.env.VXRN_USE_BABEL_FOR_GENERATORS
198
+ }
199
+ configureVXRNCompilerPlugin({ enableCompiler: false })
200
+ fs.rmSync(tempFile, { force: true })
201
+ }
202
+ })
203
+ })
204
+
205
+ describe('shared compiler worklets backend selection', () => {
206
+ it('retains Babel for worklets by default when enableNativeWorklets is false', async () => {
207
+ const { vi } = await import('vitest')
208
+ const { createVXRNCompilerPlugin } = await import('./index')
209
+ const workletsModule = await import('./transformWorklets')
210
+ const workletSpy = vi.spyOn(workletsModule, 'transformWorklets')
211
+
212
+ configureVXRNCompilerPlugin({
213
+ enableReanimated: true,
214
+ enableNativeWorklets: false,
215
+ })
216
+
217
+ const tempFile = path.join(process.cwd(), `test-default-worklet-${Date.now()}.tsx`)
218
+ const inputCode = `export function fn() { 'worklet'; return 1 }`
219
+ fs.writeFileSync(tempFile, inputCode)
220
+
221
+ try {
222
+ const plugins = await createVXRNCompilerPlugin()
223
+ const plugin = plugins.find((p: any) => p.name === 'one:compiler') as any
224
+ await plugin.configResolved({ root: process.cwd(), build: {} })
225
+ const hook = plugin.transform.handler || plugin.transform
226
+ const context = { environment: { name: 'ios' } }
227
+ const result = await hook.call(context, inputCode, tempFile)
228
+ expect(result).toBeDefined()
229
+ // With enableNativeWorklets: false, transformWorklets (SWC) is NOT called
230
+ expect(workletSpy).not.toHaveBeenCalled()
231
+ } finally {
232
+ workletSpy.mockRestore()
233
+ configureVXRNCompilerPlugin({
234
+ enableReanimated: false,
235
+ enableNativeWorklets: false,
236
+ })
237
+ fs.rmSync(tempFile, { force: true })
238
+ }
239
+ })
240
+
241
+ it('uses native SWC for worklets when enableNativeWorklets is true', async () => {
242
+ const compiler = await import('./index')
243
+ const { vi } = await import('vitest')
244
+ const workletsModule = await import('./transformWorklets')
245
+ const workletSpy = vi.spyOn(workletsModule, 'transformWorklets')
246
+
247
+ configureVXRNCompilerPlugin({
248
+ enableReanimated: true,
249
+ enableNativeWorklets: true,
250
+ })
251
+
252
+ const tempFile = path.join(process.cwd(), `test-native-worklet-${Date.now()}.tsx`)
253
+ const inputCode = `export function fn() { 'worklet'; return 1 }`
254
+ fs.writeFileSync(tempFile, inputCode)
255
+
256
+ try {
257
+ const plugins = await compiler.createVXRNCompilerPlugin()
258
+ const plugin = plugins.find((p: any) => p.name === 'one:compiler') as any
259
+ await plugin.configResolved({ root: process.cwd(), build: {} })
260
+ const hook = plugin.transform.handler || plugin.transform
261
+ const context = { environment: { name: 'ios' } }
262
+ const result = await hook.call(context, inputCode, tempFile)
263
+ expect(result).toBeDefined()
264
+ // With enableNativeWorklets: true, transformWorklets (SWC) IS called
265
+ expect(workletSpy).toHaveBeenCalled()
266
+ } finally {
267
+ workletSpy.mockRestore()
268
+ configureVXRNCompilerPlugin({
269
+ enableReanimated: false,
270
+ enableNativeWorklets: false,
271
+ })
272
+ fs.rmSync(tempFile, { force: true })
273
+ }
274
+ })
275
+
276
+ it('invalidates cache when toggling worklets backend on the same unchanged file', async () => {
277
+ const compiler = await import('./index')
278
+ const { vi } = await import('vitest')
279
+ const workletsModule = await import('./transformWorklets')
280
+ const workletSpy = vi.spyOn(workletsModule, 'transformWorklets')
281
+
282
+ const tempFile = path.join(process.cwd(), `test-cache-toggle-${Date.now()}.tsx`)
283
+ const inputCode = `export function fn() { 'worklet'; return 1 }`
284
+ fs.writeFileSync(tempFile, inputCode)
285
+
286
+ try {
287
+ // Step 1: Run with Babel backend (enableNativeWorklets: false)
288
+ configureVXRNCompilerPlugin({
289
+ enableReanimated: true,
290
+ enableNativeWorklets: false,
291
+ })
292
+
293
+ const plugins1 = await compiler.createVXRNCompilerPlugin()
294
+ const plugin1 = plugins1.find((p: any) => p.name === 'one:compiler') as any
295
+ await plugin1.configResolved({ root: process.cwd(), build: {} })
296
+ const hook1 = plugin1.transform.handler || plugin1.transform
297
+ const context = { environment: { name: 'ios' } }
298
+
299
+ const res1 = await hook1.call(context, inputCode, tempFile)
300
+ expect(res1).toBeDefined()
301
+ expect(workletSpy).toHaveBeenCalledTimes(0)
302
+
303
+ // Step 2: Toggle to native SWC backend on the SAME file without changes
304
+ configureVXRNCompilerPlugin({
305
+ enableReanimated: true,
306
+ enableNativeWorklets: true,
307
+ })
308
+
309
+ const plugins2 = await compiler.createVXRNCompilerPlugin()
310
+ const plugin2 = plugins2.find((p: any) => p.name === 'one:compiler') as any
311
+ await plugin2.configResolved({ root: process.cwd(), build: {} })
312
+ const hook2 = plugin2.transform.handler || plugin2.transform
313
+
314
+ const res2 = await hook2.call(context, inputCode, tempFile)
315
+ expect(res2).toBeDefined()
316
+ // Cache must NOT serve the Babel entry; native transformWorklets must be invoked
317
+ expect(workletSpy).toHaveBeenCalledTimes(1)
318
+
319
+ // Step 3: Call again with native SWC to verify caching works for the native backend
320
+ const res3 = await hook2.call(context, inputCode, tempFile)
321
+ expect(res3).toBeDefined()
322
+ expect(workletSpy).toHaveBeenCalledTimes(1)
323
+
324
+ // Step 4: Toggle back to Babel backend; cache must not serve SWC entry
325
+ configureVXRNCompilerPlugin({
326
+ enableReanimated: true,
327
+ enableNativeWorklets: false,
328
+ })
329
+
330
+ const plugins3 = await compiler.createVXRNCompilerPlugin()
331
+ const plugin3 = plugins3.find((p: any) => p.name === 'one:compiler') as any
332
+ await plugin3.configResolved({ root: process.cwd(), build: {} })
333
+ const hook3 = plugin3.transform.handler || plugin3.transform
334
+
335
+ const res4 = await hook3.call(context, inputCode, tempFile)
336
+ expect(res4).toBeDefined()
337
+ // workletSpy should still have only been called once
338
+ expect(workletSpy).toHaveBeenCalledTimes(1)
339
+ } finally {
340
+ workletSpy.mockRestore()
341
+ configureVXRNCompilerPlugin({
342
+ enableReanimated: false,
343
+ enableNativeWorklets: false,
344
+ })
345
+ fs.rmSync(tempFile, { force: true })
346
+ }
347
+ })
348
+ })
@@ -1,7 +1,9 @@
1
1
  import { extname, relative } from 'node:path'
2
- import babel from '@babel/core'
2
+ // type-only, so that importing this module does not drag babel in. every metro
3
+ // worker loads it through the package index, and on the native transform path
4
+ // babel is never called at all: loading it there is pure startup cost.
5
+ import type * as babel from '@babel/core'
3
6
  import { resolvePath } from '@vxrn/utils'
4
- import hermesParserPlugin from 'babel-plugin-syntax-hermes-parser'
5
7
  import { normalizePath } from 'vite'
6
8
  import { configuration } from './configure'
7
9
  import { asyncGeneratorRegex, debug } from './constants'
@@ -55,8 +57,14 @@ const getOptions = (props: Props, force = false): babel.TransformOptions | null
55
57
  }
56
58
 
57
59
  if (enableNativewind || shouldBabelReanimated(props)) {
58
- debug?.(`Using babel worklets on file ${props.id}`)
59
- plugins.push(resolvePath('react-native-worklets/plugin', props.projectRoot))
60
+ try {
61
+ const workletsPlugin = resolvePath(
62
+ 'react-native-worklets/plugin',
63
+ props.projectRoot
64
+ )
65
+ debug?.(`Using babel worklets on file ${props.id}`)
66
+ plugins.push(workletsPlugin)
67
+ } catch {}
60
68
  }
61
69
 
62
70
  if (shouldBabelReactCompiler(props)) {
@@ -64,11 +72,6 @@ const getOptions = (props: Props, force = false): babel.TransformOptions | null
64
72
  plugins.push(getBabelReactCompilerPlugin(props))
65
73
  }
66
74
 
67
- if (shouldBabelReactNativeCodegen(props)) {
68
- debug?.(`Using babel @react-native/babel-plugin-codegen on file`)
69
- plugins.push('@react-native/babel-plugin-codegen')
70
- }
71
-
72
75
  if (plugins.length) {
73
76
  return { plugins }
74
77
  }
@@ -88,11 +91,13 @@ const getOptions = (props: Props, force = false): babel.TransformOptions | null
88
91
  export async function transformOxcReactCompiler(
89
92
  id: string,
90
93
  code: string,
91
- target: '18' | '19'
94
+ target: '18' | '19',
95
+ sourceMap = false
92
96
  ) {
93
97
  const { transform } = await import('oxc-transform-react')
94
98
  const result = await transform(id, code, {
95
99
  jsx: 'preserve',
100
+ sourcemap: sourceMap,
96
101
  reactCompiler: { target },
97
102
  })
98
103
 
@@ -114,7 +119,7 @@ export async function transformOxcReactCompiler(
114
119
  )
115
120
  }
116
121
 
117
- return { code: result.code, map: undefined }
122
+ return { code: result.code, map: sourceMap ? (result.map as any) : undefined }
118
123
  }
119
124
 
120
125
  /**
@@ -125,6 +130,10 @@ export async function transformBabel(
125
130
  code: string,
126
131
  options: babel.TransformOptions
127
132
  ) {
133
+ const [{ default: babelCore }, { default: hermesParserPlugin }] = await Promise.all([
134
+ import('@babel/core'),
135
+ import('babel-plugin-syntax-hermes-parser'),
136
+ ])
128
137
  const extension = extname(id)
129
138
  const isTSX = extension === '.tsx'
130
139
  const isTS = isTSX || extension === '.ts'
@@ -166,7 +175,7 @@ export async function transformBabel(
166
175
  }
167
176
 
168
177
  return await new Promise<babel.BabelFileResult>((res, rej) => {
169
- babel.transform(code, babelOptions, (err: unknown, result) => {
178
+ babelCore.transform(code, babelOptions, (err: unknown, result) => {
170
179
  if (!result || err) {
171
180
  return rej(err || new Error(`[vxrn:compiler] babel returned no result for ${id}`))
172
181
  }
@@ -191,25 +200,6 @@ const getBasePlugins = ({ development }: Props) =>
191
200
  ],
192
201
  ] satisfies babel.PluginItem[]
193
202
 
194
- /**
195
- * ----- react native codegen ----
196
- */
197
-
198
- // Codegen specification files need to go through the react-native codegen babel plugin.
199
- // See:
200
- // * https://reactnative.dev/docs/fabric-native-components-introduction#1-define-specification-for-codegen
201
- // * https://reactnative.dev/docs/turbo-native-modules-introduction#1-declare-typed-specification
202
-
203
- const NATIVE_COMPONENT_RE = /NativeComponent\.[jt]sx?$/
204
- const SPEC_FILE_RE = /[/\\]specs?[/\\]/
205
-
206
- const shouldBabelReactNativeCodegen = ({ id, environment }: Props) => {
207
- return (
208
- (environment === 'ios' || environment === 'android') &&
209
- (NATIVE_COMPONENT_RE.test(id) || SPEC_FILE_RE.test(id))
210
- )
211
- }
212
-
213
203
  /**
214
204
  * ----- react compiler -----
215
205
  */
@@ -0,0 +1,30 @@
1
+ // hermes v1 can lose an awaited value in async arrows with default parameters.
2
+ // lower async after worklets, keeping bigint and the earlier class-field semantics.
3
+ // metro also emits commonjs here so it does not need a second esbuild pass.
4
+ export async function transformHermesAsync(
5
+ code: string,
6
+ filename: string,
7
+ sourceMaps = false,
8
+ format?: 'cjs'
9
+ ): Promise<{ code: string; map?: any } | undefined> {
10
+ if (!format && !code.includes('async')) return
11
+
12
+ const { transformSync } = await import('esbuild')
13
+ const cleanId = filename.split('?')[0]
14
+ const result = transformSync(code, {
15
+ loader: /\.[cm]?ts$/.test(cleanId) ? 'ts' : cleanId.endsWith('.tsx') ? 'tsx' : 'jsx',
16
+ jsx: 'preserve',
17
+ format,
18
+ supported: {
19
+ 'async-await': false,
20
+ 'async-generator': false,
21
+ 'for-await': false,
22
+ },
23
+ sourcefile: filename,
24
+ sourcemap: sourceMaps ? 'external' : false,
25
+ })
26
+ return {
27
+ code: result.code,
28
+ map: result.map ? JSON.parse(result.map) : undefined,
29
+ }
30
+ }