@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.
- package/dist/cjs/cache.cjs +4 -2
- package/dist/cjs/configure.cjs +9 -1
- package/dist/cjs/index.cjs +87 -5
- 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/transformHermesAsync.cjs +61 -0
- package/dist/cjs/transformHermesLoops.cjs +319 -0
- package/dist/cjs/transformHermesLoops.test.cjs +257 -0
- package/dist/cjs/transformSWC.cjs +110 -138
- package/dist/cjs/transformSWC.test.cjs +117 -0
- package/dist/cjs/transformWorklets.cjs +123 -0
- package/dist/cjs/transformWorklets.test.cjs +604 -0
- package/dist/cjs/worklets/autoworklet.cjs +265 -0
- package/dist/cjs/worklets/globals.cjs +73 -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 +266 -0
- package/dist/cjs/worklets/types.cjs +18 -0
- package/dist/esm/cache.mjs +5 -3
- 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 +75 -6
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.mjs +75 -6
- 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/transformHermesAsync.mjs +25 -0
- package/dist/esm/transformHermesAsync.mjs.map +1 -0
- package/dist/esm/transformHermesLoops.mjs +283 -0
- package/dist/esm/transformHermesLoops.mjs.map +1 -0
- package/dist/esm/transformHermesLoops.test.mjs +234 -0
- package/dist/esm/transformHermesLoops.test.mjs.map +1 -0
- package/dist/esm/transformSWC.mjs +96 -139
- package/dist/esm/transformSWC.mjs.map +1 -1
- package/dist/esm/transformSWC.test.mjs +118 -0
- package/dist/esm/transformSWC.test.mjs.map +1 -0
- package/dist/esm/transformWorklets.mjs +82 -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 +46 -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 +229 -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 +11 -6
- package/src/cache.ts +10 -3
- package/src/configure.ts +12 -0
- package/src/index.ts +96 -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/transformHermesAsync.ts +30 -0
- package/src/transformHermesLoops.test.ts +341 -0
- package/src/transformHermesLoops.ts +435 -0
- package/src/transformSWC.test.ts +132 -0
- package/src/transformSWC.ts +121 -161
- 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 +129 -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 +319 -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 +6 -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/transformHermesAsync.d.ts +5 -0
- package/types/transformHermesAsync.d.ts.map +1 -0
- 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 +13 -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 +23 -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 +4 -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 +10 -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
|
@@ -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 {
|
|
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
|
+
})
|
package/src/transformBabel.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { extname, relative } from 'node:path'
|
|
2
|
-
|
|
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
|
-
|
|
59
|
-
|
|
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
|
-
|
|
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
|
+
}
|