vxrn 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 (72) hide show
  1. package/dist/index.js +2 -0
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +2 -0
  4. package/dist/index.mjs.map +1 -1
  5. package/dist/index.native.js +2 -0
  6. package/dist/index.native.js.map +1 -1
  7. package/dist/patches/builtInDepPatches.mjs +21 -0
  8. package/dist/patches/builtInDepPatches.mjs.map +1 -1
  9. package/dist/patches/builtInDepPatches.native.js +21 -0
  10. package/dist/patches/builtInDepPatches.native.js.map +1 -1
  11. package/dist/plugins/reactNativeDevServer.mjs +5 -0
  12. package/dist/plugins/reactNativeDevServer.mjs.map +1 -1
  13. package/dist/plugins/reactNativeDevServer.native.js +5 -0
  14. package/dist/plugins/reactNativeDevServer.native.js.map +1 -1
  15. package/dist/plugins/rnCodegenPlugin.mjs +16 -0
  16. package/dist/plugins/rnCodegenPlugin.mjs.map +1 -0
  17. package/dist/plugins/rnCodegenPlugin.native.js +16 -0
  18. package/dist/plugins/rnCodegenPlugin.native.js.map +1 -0
  19. package/dist/plugins/rnCodegenPlugin.test.mjs +239 -0
  20. package/dist/plugins/rnCodegenPlugin.test.mjs.map +1 -0
  21. package/dist/plugins/rnCodegenPlugin.test.native.js +242 -0
  22. package/dist/plugins/rnCodegenPlugin.test.native.js.map +1 -0
  23. package/dist/plugins/workletImportsPlugin.mjs +160 -0
  24. package/dist/plugins/workletImportsPlugin.mjs.map +1 -0
  25. package/dist/plugins/workletImportsPlugin.native.js +230 -0
  26. package/dist/plugins/workletImportsPlugin.native.js.map +1 -0
  27. package/dist/utils/createNativeDevEngine.mjs +198 -92
  28. package/dist/utils/createNativeDevEngine.mjs.map +1 -1
  29. package/dist/utils/createNativeDevEngine.native.js +252 -99
  30. package/dist/utils/createNativeDevEngine.native.js.map +1 -1
  31. package/dist/utils/createNativeDevEngine.test.mjs +775 -4
  32. package/dist/utils/createNativeDevEngine.test.mjs.map +1 -1
  33. package/dist/utils/createNativeDevEngine.test.native.js +836 -4
  34. package/dist/utils/createNativeDevEngine.test.native.js.map +1 -1
  35. package/dist/utils/patches.mjs +12 -5
  36. package/dist/utils/patches.mjs.map +1 -1
  37. package/dist/utils/patches.native.js +13 -5
  38. package/dist/utils/patches.native.js.map +1 -1
  39. package/dist/utils/patches.test.mjs +28 -0
  40. package/dist/utils/patches.test.mjs.map +1 -1
  41. package/dist/utils/patches.test.native.js +47 -0
  42. package/dist/utils/patches.test.native.js.map +1 -1
  43. package/dist/utils/scanDepsToOptimize.mjs +5 -1
  44. package/dist/utils/scanDepsToOptimize.mjs.map +1 -1
  45. package/dist/utils/scanDepsToOptimize.native.js +5 -1
  46. package/dist/utils/scanDepsToOptimize.native.js.map +1 -1
  47. package/package.json +19 -12
  48. package/src/index.ts +2 -0
  49. package/src/patches/builtInDepPatches.ts +25 -0
  50. package/src/plugins/reactNativeDevServer.ts +12 -0
  51. package/src/plugins/rnCodegenPlugin.test.ts +313 -0
  52. package/src/plugins/rnCodegenPlugin.ts +20 -0
  53. package/src/plugins/workletImportsPlugin.ts +226 -0
  54. package/src/utils/createNativeDevEngine.test.ts +877 -3
  55. package/src/utils/createNativeDevEngine.ts +259 -107
  56. package/src/utils/patches.test.ts +33 -0
  57. package/src/utils/patches.ts +17 -6
  58. package/src/utils/scanDepsToOptimize.ts +9 -0
  59. package/types/index.d.ts +2 -0
  60. package/types/index.d.ts.map +1 -1
  61. package/types/patches/builtInDepPatches.d.ts.map +1 -1
  62. package/types/plugins/reactNativeDevServer.d.ts.map +1 -1
  63. package/types/plugins/rnCodegenPlugin.d.ts +6 -0
  64. package/types/plugins/rnCodegenPlugin.d.ts.map +1 -0
  65. package/types/plugins/rnCodegenPlugin.test.d.ts +2 -0
  66. package/types/plugins/rnCodegenPlugin.test.d.ts.map +1 -0
  67. package/types/plugins/workletImportsPlugin.d.ts +8 -0
  68. package/types/plugins/workletImportsPlugin.d.ts.map +1 -0
  69. package/types/utils/createNativeDevEngine.d.ts +13 -0
  70. package/types/utils/createNativeDevEngine.d.ts.map +1 -1
  71. package/types/utils/patches.d.ts.map +1 -1
  72. package/types/utils/scanDepsToOptimize.d.ts.map +1 -1
@@ -2,12 +2,13 @@ import { existsSync } from "fs";
2
2
  import { mkdir, mkdtemp, rm, writeFile } from "fs/promises";
3
3
  import { tmpdir } from "os";
4
4
  import { join } from "path";
5
- import { runInNewContext } from "vm";
5
+ import { createContext, runInContext, runInNewContext } from "vm";
6
6
  import { rolldown } from "rolldown";
7
7
  import { dev } from "rolldown/experimental";
8
- import { describe, expect, it } from "vitest";
8
+ import { describe, expect, it, vi } from "vitest";
9
9
  import { getNativePrelude } from "../runtime/native-prelude.native.js";
10
- import { buildNativeBundle, createNativeDevAssetRegistry, getHermesSWCIncludes, getHmrRuntimeSource, getNativeAssetData, getNativeTransformConfig, hermesCompatSWCPlugin, hmrClientNoopPlugin, nativeAnimatedGuardPlugin, normalizeNativeCommonJSInterop, vxrnCompilerPlugin, wrapNativeBundleModuleScope } from "./createNativeDevEngine.native.js";
10
+ import { workletImportsPlugin } from "../plugins/workletImportsPlugin.native.js";
11
+ import { buildNativeBundle, createNativeDevAssetRegistry, createNativeDevEngine, getHermesSWCIncludes, getHmrRuntimeSource, getNativeAssetData, getNativeTransformConfig, hermesCompatSWCPlugin, hmrClientNoopPlugin, nativeAnimatedGuardPlugin, normalizeNativeCommonJSInterop, vxrnCompilerPlugin, wrapNativeBundleModuleScope } from "./createNativeDevEngine.native.js";
11
12
  function _instanceof(left, right) {
12
13
  if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) return !!right[Symbol.hasInstance](left);else return left instanceof right;
13
14
  }
@@ -22,6 +23,265 @@ export const transformProbe = () => {
22
23
  return 'transformed'
23
24
  }
24
25
  `;
26
+ describe.each(["rolldown-dev", "rolldown-build", "metro-module", "metro-script"])("native async through %s", function (pipeline) {
27
+ it("preserves defaults, lexical bindings, bigint, rejection, and iterator cleanup", async function () {
28
+ var id = "/project/native-async.js";
29
+ var source = `
30
+ globalThis.result = (function(parentArgument) {
31
+ const events = [];
32
+ const run = async(value = 2n, fail = false) => {
33
+ try {
34
+ const awaited = await (fail ? Promise.reject(new Error('rejected')) : Promise.resolve(3n));
35
+ events.push('after-await');
36
+ return [String(value + awaited), this.base, arguments[0]];
37
+ } catch (error) { return error.message; }
38
+ finally { events.push('finally'); }
39
+ };
40
+ const badDefault = async(value = (() => { throw new Error('default'); })()) => value;
41
+ let synchronousThrow = false;
42
+ let rejectedDefault;
43
+ try { rejectedDefault = badDefault().catch(error => error.message); }
44
+ catch { synchronousThrow = true; }
45
+ async function* values() {
46
+ try { yield 1n; yield 2n; }
47
+ finally { events.push('closed'); }
48
+ }
49
+ const first = async() => { for await (const value of values()) return String(value); };
50
+ return Promise.all([run(), run(4n), run(undefined, true), rejectedDefault, first()])
51
+ .then(values => ({ values, events, synchronousThrow }));
52
+ }).call({ base: 7 }, 'lexical');
53
+ `;
54
+ var output;
55
+ if (pipeline.startsWith("metro")) {
56
+ var {
57
+ transform
58
+ } = await import("@vxrn/vite-plugin-metro/metroNativeWorker");
59
+ var data = (await transform({}, "/project", id, Buffer.from(source), {
60
+ dev: false,
61
+ platform: "ios",
62
+ type: pipeline === "metro-script" ? "script" : "module"
63
+ })).output[0].data;
64
+ var {
65
+ fromRawMappings
66
+ } = await import("metro-source-map");
67
+ output = {
68
+ code: data.code,
69
+ map: fromRawMappings([{
70
+ code: data.code,
71
+ path: id,
72
+ source,
73
+ map: data.map
74
+ }]).toMap()
75
+ };
76
+ } else {
77
+ var plugin = hermesCompatSWCPlugin(pipeline === "rolldown-dev", true);
78
+ output = await Reflect.apply(plugin.transform, void 0, [source, id]);
79
+ }
80
+ var context = {};
81
+ context.__d = function (factory) {
82
+ var module = {
83
+ exports: {}
84
+ };
85
+ factory(context, function (name) {
86
+ throw new Error(`unexpected dependency ${name}`);
87
+ }, function () {}, function () {}, module, module.exports, []);
88
+ };
89
+ runInNewContext(output.code, context);
90
+ var result1 = await context.result;
91
+ expect(result1.values).toEqual([["5", 7, "lexical"], ["7", 7, "lexical"], "rejected", "default", "1"]);
92
+ expect(result1.synchronousThrow).toBe(false);
93
+ expect(result1.events.filter(function (value) {
94
+ return value === "finally";
95
+ })).toHaveLength(3);
96
+ expect(result1.events.filter(function (value) {
97
+ return value === "closed";
98
+ })).toHaveLength(1);
99
+ var {
100
+ originalPositionFor,
101
+ TraceMap
102
+ } = await import("@jridgewell/trace-mapping");
103
+ var lines = output.code.split("\n");
104
+ var line = lines.findIndex(function (value) {
105
+ return value.includes("after-await");
106
+ });
107
+ var position = originalPositionFor(new TraceMap(output.map), {
108
+ line: line + 1,
109
+ column: lines[line].indexOf("events.push")
110
+ });
111
+ expect(position.source).toBe(id);
112
+ expect(position.line).toBe(source.split("\n").findIndex(function (value) {
113
+ return value.includes("after-await");
114
+ }) + 1);
115
+ });
116
+ });
117
+ describe.each(["shared", "rolldown", "metro"])("React Compiler worklets through %s", function (pipeline) {
118
+ it.each([["block", "useDerivedValue(() => { return value + 8 }, [value])", 18], ["expression", "useDerivedValue(() => value + 8, [value])", 18], ["object", "useDerivedValue(() => ({ lift: value + 8 }), [value])", {
119
+ lift: 18
120
+ }], ["nested", "useDerivedValue(() => runOnUI(() => value + 8), [value])", 18], ["gesture", "useDerivedValue(() => Gesture.Pan().onUpdate(() => value + 8), [value])", 18]])("preserves %s callbacks on the UI runtime and memoizes stable inputs", async function (_name, callback, expected) {
121
+ var compiler = await import("@vxrn/compiler");
122
+ var {
123
+ parseSync
124
+ } = await import("oxc-parser");
125
+ var {
126
+ default: MagicString
127
+ } = await import("magic-string");
128
+ var {
129
+ TraceMap,
130
+ originalPositionFor
131
+ } = await import("@jridgewell/trace-mapping");
132
+ var projectRoot = await createWorkletsProject(false);
133
+ var sourceMaps = process.env.VXRN_ENABLE_SOURCE_MAP;
134
+ process.env.VXRN_ENABLE_SOURCE_MAP = "1";
135
+ compiler.configureVXRNCompilerPlugin({
136
+ enableCompiler: true,
137
+ enableReanimated: true,
138
+ enableNativeWorklets: true
139
+ });
140
+ try {
141
+ var _first___initData;
142
+ var id = join(projectRoot, "useProbe.ts");
143
+ var source = `export function useProbe(value) {
144
+ return ${callback};
145
+ }
146
+ `;
147
+ await writeFile(id, source);
148
+ var result;
149
+ if (pipeline === "shared") {
150
+ var plugin = (await compiler.createVXRNCompilerPlugin()).find(function (p) {
151
+ return p.name === "one:compiler";
152
+ });
153
+ await plugin.configResolved({
154
+ root: projectRoot,
155
+ build: {}
156
+ });
157
+ result = await (plugin.transform.handler || plugin.transform).call({
158
+ environment: {
159
+ name: "ios"
160
+ }
161
+ }, source, id);
162
+ } else if (pipeline === "metro") {
163
+ var {
164
+ transform
165
+ } = await import("@vxrn/vite-plugin-metro/metroNativeWorker");
166
+ var data = (await transform({}, projectRoot, id, Buffer.from(source), {
167
+ dev: false,
168
+ platform: "ios",
169
+ type: "module",
170
+ customTransformOptions: {
171
+ reactCompiler: true,
172
+ worklets: true
173
+ }
174
+ })).output[0].data;
175
+ var msm = await import("metro-source-map");
176
+ result = {
177
+ code: data.code,
178
+ map: msm.fromRawMappings([{
179
+ code: data.code,
180
+ path: id,
181
+ source,
182
+ map: data.map
183
+ }]).toMap()
184
+ };
185
+ } else {
186
+ var plugin1 = vxrnCompilerPlugin("ios", false, projectRoot, true);
187
+ result = await Reflect.apply(plugin1.transform, void 0, [source, id]);
188
+ }
189
+ var runnable = new MagicString(result.code);
190
+ var _iteratorNormalCompletion = true,
191
+ _didIteratorError = false,
192
+ _iteratorError = void 0;
193
+ try {
194
+ for (var _iterator = parseSync("probe.js", result.code).program.body[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
195
+ var node = _step.value;
196
+ if (node.type === "ImportDeclaration") runnable.remove(node.start, node.end);
197
+ if (node.type === "ExportNamedDeclaration" && node.declaration) runnable.remove(node.start, node.declaration.start);
198
+ }
199
+ } catch (err) {
200
+ _didIteratorError = true;
201
+ _iteratorError = err;
202
+ } finally {
203
+ try {
204
+ if (!_iteratorNormalCompletion && _iterator.return != null) _iterator.return();
205
+ } finally {
206
+ if (_didIteratorError) throw _iteratorError;
207
+ }
208
+ }
209
+ var cache;
210
+ var cacheCalls = 0;
211
+ var memo = function (size) {
212
+ cacheCalls++;
213
+ return cache !== null && cache !== void 0 ? cache : cache = Array(size).fill(/* @__PURE__ */Symbol.for("react.memo_cache_sentinel"));
214
+ };
215
+ var module = {
216
+ exports: {}
217
+ };
218
+ var probe = new Function("_c", "useDerivedValue", "runOnUI", "Gesture", "__d", "module", `${runnable}
219
+ return ${pipeline === "metro" ? "module.exports.useProbe" : "useProbe"}`)(memo, function (fn2) {
220
+ return fn2;
221
+ }, function (fn2) {
222
+ return fn2;
223
+ }, {
224
+ Pan: function () {
225
+ return {
226
+ onUpdate: function (fn2) {
227
+ return fn2;
228
+ }
229
+ };
230
+ }
231
+ }, function (factory) {
232
+ return factory(globalThis, function () {
233
+ return {
234
+ c: memo
235
+ };
236
+ }, function () {}, function () {}, module, module.exports, []);
237
+ }, module);
238
+ var first = probe(10);
239
+ expect((_first___initData = first.__initData) === null || _first___initData === void 0 ? void 0 : _first___initData.code).toBeTypeOf("string");
240
+ for (var _i = 0, _iter = [false, true]; _i < _iter.length; _i++) {
241
+ var onUI = _iter[_i];
242
+ var fn = first;
243
+ var value = void 0;
244
+ for (var depth = 0; depth < 2; depth++) {
245
+ var _fn___initData;
246
+ expect((_fn___initData = fn.__initData) === null || _fn___initData === void 0 ? void 0 : _fn___initData.code).toBeTypeOf("string");
247
+ value = onUI ? new Function("runOnUI", `return (${fn.__initData.code})`)(function (fn2) {
248
+ return fn2;
249
+ }).call({
250
+ __closure: fn.__closure
251
+ }) : fn();
252
+ if (typeof value !== "function") break;
253
+ fn = value;
254
+ }
255
+ expect(value).toEqual(expected);
256
+ }
257
+ expect(probe(10)).toBe(first);
258
+ expect(cacheCalls).toBe(2);
259
+ expect(probe(20)).not.toBe(first);
260
+ var lines = result.code.split("\n");
261
+ var line = lines.findIndex(function (text) {
262
+ return text.includes("function useProbe(");
263
+ });
264
+ expect(line).toBeGreaterThanOrEqual(0);
265
+ var position = originalPositionFor(new TraceMap(result.map), {
266
+ line: line + 1,
267
+ column: lines[line].indexOf("function")
268
+ });
269
+ expect(position.source).toBe(id);
270
+ expect(position.line).toBe(1);
271
+ } finally {
272
+ if (sourceMaps === void 0) delete process.env.VXRN_ENABLE_SOURCE_MAP;else process.env.VXRN_ENABLE_SOURCE_MAP = sourceMaps;
273
+ compiler.configureVXRNCompilerPlugin({
274
+ enableCompiler: false,
275
+ enableReanimated: false,
276
+ enableNativeWorklets: false
277
+ });
278
+ await rm(projectRoot, {
279
+ recursive: true,
280
+ force: true
281
+ });
282
+ }
283
+ });
284
+ });
25
285
  async function createWorkletsProject() {
26
286
  var throwOnTransform = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : true;
27
287
  var testRoot = await mkdtemp(join(tmpdir(), "vxrn-native-transform-failure-"));
@@ -30,7 +290,8 @@ async function createWorkletsProject() {
30
290
  recursive: true
31
291
  });
32
292
  await writeFile(join(packageRoot, "package.json"), JSON.stringify({
33
- name: "react-native-worklets"
293
+ name: "react-native-worklets",
294
+ version: "0.10.1"
34
295
  }));
35
296
  await writeFile(join(packageRoot, "plugin.js"), throwOnTransform ? `module.exports = () => ({
36
297
  visitor: {
@@ -68,7 +329,258 @@ describe("native prelude", function () {
68
329
  expect(context.removeEventListener).toBe(removeEventListener);
69
330
  });
70
331
  });
332
+ describe("native pure-function worklet imports", function () {
333
+ it("keeps live module versions isolated across a transitive HMR update", async function () {
334
+ var root2 = await createWorkletsProject(false);
335
+ var packageRoot = join(root2, "node_modules/pure-math");
336
+ await mkdir(packageRoot, {
337
+ recursive: true
338
+ });
339
+ await writeFile(join(packageRoot, "package.json"), JSON.stringify({
340
+ type: "module",
341
+ exports: "./index.js"
342
+ }));
343
+ var dependency = join(packageRoot, "amount.ios.js");
344
+ await writeFile(dependency, "export const amount = 2");
345
+ await writeFile(join(packageRoot, "amount.android.js"), "export const amount = 99");
346
+ await writeFile(join(packageRoot, "index.js"), `
347
+ import { amount } from './amount'
348
+ const instance = globalThis.moduleInitializations = (globalThis.moduleInitializations || 0) + 1
349
+ export default function calculate(value) { return value + amount }
350
+ export function reads() { return instance }
351
+ `);
352
+ await writeFile(join(root2, "entry.mjs"), `
353
+ import calculate, { reads } from 'pure-math'
354
+ globalThis.calculate = calculate
355
+ globalThis.reads = reads
356
+ if (import.meta.hot) import.meta.hot.accept(() => {})
357
+ `);
358
+ var compiler = await import("@vxrn/compiler");
359
+ compiler.configureVXRNCompilerPlugin({
360
+ enableReanimated: true,
361
+ enableNativeWorklets: true
362
+ });
363
+ var resolveUpdate;
364
+ var updated = new Promise(function (resolve) {
365
+ resolveUpdate = resolve;
366
+ });
367
+ var native = await createNativeDevEngine({
368
+ root: root2,
369
+ port: 0,
370
+ platform: "ios",
371
+ plugins: [workletImportsPlugin({
372
+ "pure-math": ["default", "reads"]
373
+ }), {
374
+ name: "worklet-import-fixture",
375
+ transform(_code, id) {
376
+ if (id.endsWith("/__virtual-native-entry.tsx")) return `import './entry.mjs'`;
377
+ }
378
+ }],
379
+ onHmrUpdate: resolveUpdate
380
+ });
381
+ try {
382
+ var initial = await native.getBundle();
383
+ var rn = {
384
+ console,
385
+ setTimeout,
386
+ clearTimeout
387
+ };
388
+ runInNewContext(initial.code, rn);
389
+ expect(rn.calculate(10)).toBe(12);
390
+ expect(rn.reads()).toBe(1);
391
+ var ui = createContext({});
392
+ var compiled = /* @__PURE__ */new Map();
393
+ var reconstruct = function (worklet) {
394
+ if (!worklet.__initData || !worklet.__workletHash) throw new Error("remote function");
395
+ var fn = compiled.get(worklet.__workletHash);
396
+ if (!fn) {
397
+ fn = runInContext(`(${worklet.__initData.code})`, ui);
398
+ compiled.set(worklet.__workletHash, fn);
399
+ }
400
+ var closure = Object.fromEntries(Object.entries(worklet.__closure).map(function (param) {
401
+ var [name, value] = param;
402
+ return [name, typeof value === "function" ? reconstruct(value) : value];
403
+ }));
404
+ return fn.bind({
405
+ __closure: closure
406
+ });
407
+ };
408
+ expect(function () {
409
+ return reconstruct(function (value) {
410
+ return value + 2;
411
+ });
412
+ }).toThrow("remote function");
413
+ var old = reconstruct(rn.calculate);
414
+ var oldReads = reconstruct(rn.reads);
415
+ expect(old(10)).toBe(12);
416
+ expect(reconstruct(rn.calculate)(11)).toBe(13);
417
+ expect(oldReads()).toBe(1);
418
+ expect(rn.reads()).toBe(1);
419
+ var runtime = rn.__rolldown_runtime__;
420
+ await native.engine.registerClient(runtime.clientId);
421
+ await writeFile(dependency, "export const amount = 3");
422
+ var update = await updated;
423
+ expect(update.type).toBe("hmr:update");
424
+ expect(runtime.applyHmrUpdate(update.code, update.changedIds, update.seq)).toBe(true);
425
+ expect(rn.calculate(10)).toBe(13);
426
+ var next = reconstruct(rn.calculate);
427
+ expect([old(10), next(10), old(10), next(10)]).toEqual([12, 13, 12, 13]);
428
+ var nextReads = reconstruct(rn.reads);
429
+ expect([oldReads(), nextReads(), oldReads(), nextReads()]).toEqual([1, 2, 1, 2]);
430
+ expect(ui.moduleInitializations).toBe(2);
431
+ expect(rn.reads()).toBe(2);
432
+ await writeFile(join(root2, "production.mjs"), `import calculate, { reads } from 'pure-math'; globalThis.calculate = calculate; globalThis.reads = reads`);
433
+ var production = await buildNativeBundle({
434
+ root: root2,
435
+ platform: "android",
436
+ entryFile: "production.mjs",
437
+ plugins: [workletImportsPlugin({
438
+ "pure-math": ["default", "reads"]
439
+ })]
440
+ });
441
+ var productionRN = {
442
+ console
443
+ };
444
+ runInNewContext(production.code, productionRN);
445
+ expect(productionRN.calculate(10)).toBe(109);
446
+ expect(reconstruct(productionRN.calculate)(10)).toBe(109);
447
+ expect(reconstruct(productionRN.reads)()).toBe(3);
448
+ expect(productionRN.reads()).toBe(1);
449
+ var sharedPlugin = workletImportsPlugin({
450
+ "pure-math": ["default", "reads"]
451
+ });
452
+ expect(await Promise.all(["ios", "android"].map(async function (platform) {
453
+ var output = await buildNativeBundle({
454
+ root: root2,
455
+ platform,
456
+ entryFile: "production.mjs",
457
+ plugins: [sharedPlugin]
458
+ });
459
+ var context = {
460
+ console
461
+ };
462
+ runInNewContext(output.code, context);
463
+ return context.calculate(10);
464
+ }))).toEqual([13, 109]);
465
+ } finally {
466
+ await native.close();
467
+ compiler.configureVXRNCompilerPlugin({
468
+ enableReanimated: false,
469
+ enableNativeWorklets: false
470
+ });
471
+ await rm(root2, {
472
+ recursive: true,
473
+ force: true
474
+ });
475
+ }
476
+ });
477
+ it.each([["unselected", "export default function value() { return 1 }; export const other = 2", `import { other } from 'pure-math'; globalThis.value = other`, /unselected export other/], ["missing", "export const other = 2", `import value from 'pure-math'; globalThis.value = value`, /default.*not exported|MISSING_EXPORT/], ["builtin", `import fs from 'node:fs'; export default function value() { return fs.readFileSync('x') }`, `import value from 'pure-math'; globalThis.value = value`, /runtime dependency node:fs/], ["global", "export default function value() { return process.pid }", `import value from 'pure-math'; globalThis.value = value`, /unsupported runtime globals: process/], ["dynamic", `export default function value() { return import('./other.js') }`, `import value from 'pure-math'; globalThis.value = value`, /dynamic imports are unsupported/]])("rejects %s through the native engine", async function (_name, source, entry, expected) {
478
+ var root2 = await createWorkletsProject(false);
479
+ var packageRoot = join(root2, "node_modules/pure-math");
480
+ await mkdir(packageRoot, {
481
+ recursive: true
482
+ });
483
+ await writeFile(join(packageRoot, "package.json"), JSON.stringify({
484
+ type: "module",
485
+ exports: "./index.js"
486
+ }));
487
+ await writeFile(join(packageRoot, "index.js"), source);
488
+ await writeFile(join(root2, "entry.mjs"), entry);
489
+ var compiler = await import("@vxrn/compiler");
490
+ compiler.configureVXRNCompilerPlugin({
491
+ enableReanimated: true,
492
+ enableNativeWorklets: true
493
+ });
494
+ var native = await createNativeDevEngine({
495
+ root: root2,
496
+ port: 0,
497
+ platform: "ios",
498
+ plugins: [workletImportsPlugin({
499
+ "pure-math": ["default"]
500
+ }), {
501
+ name: "worklet-import-negative-fixture",
502
+ transform(_code, id) {
503
+ if (id.endsWith("/__virtual-native-entry.tsx")) return `import './entry.mjs'`;
504
+ }
505
+ }]
506
+ });
507
+ try {
508
+ await expect(native.getBundle()).rejects.toThrow(expected);
509
+ } finally {
510
+ await native.close();
511
+ compiler.configureVXRNCompilerPlugin({
512
+ enableReanimated: false,
513
+ enableNativeWorklets: false
514
+ });
515
+ await rm(root2, {
516
+ recursive: true,
517
+ force: true
518
+ });
519
+ }
520
+ });
521
+ });
71
522
  describe("native Rolldown HMR runtime", function () {
523
+ it.each([true, false])("keeps CommonJS default components callable through the native HMR wire path (Babel=%s)", async function (babel) {
524
+ var root2 = await mkdtemp(join(tmpdir(), "vxrn-native-hmr-interop-"));
525
+ var entry = join(root2, "entry.mjs");
526
+ var packageRoot = join(root2, "node_modules/component");
527
+ await mkdir(packageRoot, {
528
+ recursive: true
529
+ });
530
+ await writeFile(join(packageRoot, "package.json"), JSON.stringify({
531
+ main: "./component(view).js"
532
+ }));
533
+ await writeFile(join(packageRoot, "component(view).js"), babel ? `Object.defineProperty(exports, '__esModule', { value: true }); exports.default = function Component() { return 42 }` : `module.exports = function Component() { return 42 }`);
534
+ var source = function (version) {
535
+ return `
536
+ import Component from 'component'
537
+ globalThis.renderComponent = Component
538
+ export const version = ${version}
539
+ if (import.meta.hot) import.meta.hot.accept(() => {})
540
+ `;
541
+ };
542
+ await writeFile(entry, source(1));
543
+ var resolveUpdate;
544
+ var updateReceived = new Promise(function (resolve) {
545
+ resolveUpdate = resolve;
546
+ });
547
+ var native = await createNativeDevEngine({
548
+ root: root2,
549
+ port: 0,
550
+ platform: "ios",
551
+ plugins: [{
552
+ name: "native-hmr-fixture-entry",
553
+ transform(_code, id) {
554
+ if (id.endsWith("/__virtual-native-entry.tsx")) return `import './entry.mjs'`;
555
+ }
556
+ }],
557
+ onHmrUpdate: resolveUpdate
558
+ });
559
+ try {
560
+ var initial = await native.getBundle();
561
+ var context = {
562
+ console,
563
+ setTimeout,
564
+ clearTimeout
565
+ };
566
+ runInNewContext(initial.code, context);
567
+ expect(context.renderComponent()).toBe(42);
568
+ var runtime = context.__rolldown_runtime__;
569
+ await native.engine.registerClient(runtime.clientId);
570
+ await writeFile(entry, source(2));
571
+ var update = await updateReceived;
572
+ expect(update.type).toBe("hmr:update");
573
+ expect(update.clientId).toBe(runtime.clientId);
574
+ expect(runtime.applyHmrUpdate(update.code, update.changedIds, update.seq)).toBe(true);
575
+ expect(context.renderComponent()).toBe(42);
576
+ } finally {
577
+ await native.close();
578
+ await rm(root2, {
579
+ recursive: true,
580
+ force: true
581
+ });
582
+ }
583
+ });
72
584
  it("registers a Rolldown 1.2 client and applies a self-accepted patch", {
73
585
  timeout: 3e4
74
586
  }, async function () {
@@ -390,6 +902,27 @@ describe("getHermesSWCIncludes", function () {
390
902
  expect(result.code).not.toContain("async function*");
391
903
  expect(result.code).not.toContain("async function *");
392
904
  });
905
+ it("downlevels private fields, public class fields, and static blocks for Hermes", async function () {
906
+ var plugin = hermesCompatSWCPlugin(true);
907
+ if (typeof plugin.transform !== "function") throw new Error("Hermes compatibility transform hook is not callable");
908
+ var result = await Reflect.apply(plugin.transform, void 0, [`
909
+ class TransformProbe {
910
+ #privateField = 1
911
+ publicField = 2
912
+ static {
913
+ TransformProbe.initialized = true
914
+ }
915
+
916
+ getSecret() {
917
+ return this.#privateField
918
+ }
919
+ }
920
+ `, "/project/TransformProbe.ts"]);
921
+ expect(result.code).not.toContain("#privateField");
922
+ expect(result.code).not.toMatch(/^\s*publicField\s*=/m);
923
+ expect(result.code).not.toContain("static {");
924
+ expect(result.code).toContain("this.publicField = 2");
925
+ });
393
926
  it("preserves per-iteration bindings used by lazy method getters", async function () {
394
927
  var plugin = hermesCompatSWCPlugin(true);
395
928
  if (typeof plugin.transform !== "function") throw new Error("Hermes compatibility transform hook is not callable");
@@ -505,6 +1038,7 @@ describe("native required transform failures", function () {
505
1038
  it.each([true, false])("rejects valid worklet source when its required compiler transform fails (dev=%s)", async function (dev2) {
506
1039
  var testRoot = await createWorkletsProject();
507
1040
  var compiler = await import("@vxrn/compiler");
1041
+ var spy = vi.spyOn(compiler, "transformWorklets").mockRejectedValue(/* @__PURE__ */new Error("NATIVE_TRANSFORM_NEGATIVE_CONTROL"));
508
1042
  compiler.configureVXRNCompilerPlugin({
509
1043
  enableReanimated: true
510
1044
  });
@@ -513,6 +1047,7 @@ describe("native required transform failures", function () {
513
1047
  if (typeof plugin.transform !== "function") throw new Error("vxrn compiler transform hook is not callable");
514
1048
  await expect(Reflect.apply(plugin.transform, void 0, [nativeTransformProbe, join(testRoot, "entry.ts")])).rejects.toThrow("NATIVE_TRANSFORM_NEGATIVE_CONTROL");
515
1049
  } finally {
1050
+ spy.mockRestore();
516
1051
  compiler.configureVXRNCompilerPlugin({
517
1052
  enableReanimated: false
518
1053
  });
@@ -525,6 +1060,7 @@ describe("native required transform failures", function () {
525
1060
  it("returns a dev build error and no output when the required compiler transform fails", async function () {
526
1061
  var testRoot = await createWorkletsProject();
527
1062
  var compiler = await import("@vxrn/compiler");
1063
+ var spy = vi.spyOn(compiler, "transformWorklets").mockRejectedValue(/* @__PURE__ */new Error("NATIVE_TRANSFORM_NEGATIVE_CONTROL"));
528
1064
  compiler.configureVXRNCompilerPlugin({
529
1065
  enableReanimated: true
530
1066
  });
@@ -547,6 +1083,7 @@ describe("native required transform failures", function () {
547
1083
  expect(result).toBeInstanceOf(Error);
548
1084
  expect(String(result)).toContain("NATIVE_TRANSFORM_NEGATIVE_CONTROL");
549
1085
  } finally {
1086
+ spy.mockRestore();
550
1087
  await engine.close();
551
1088
  compiler.configureVXRNCompilerPlugin({
552
1089
  enableReanimated: false
@@ -560,6 +1097,7 @@ describe("native required transform failures", function () {
560
1097
  it("emits no production bundle when the required compiler transform fails", async function () {
561
1098
  var testRoot = await createWorkletsProject();
562
1099
  var compiler = await import("@vxrn/compiler");
1100
+ var spy = vi.spyOn(compiler, "transformWorklets").mockRejectedValue(/* @__PURE__ */new Error("NATIVE_TRANSFORM_NEGATIVE_CONTROL"));
563
1101
  compiler.configureVXRNCompilerPlugin({
564
1102
  enableReanimated: true
565
1103
  });
@@ -570,6 +1108,7 @@ describe("native required transform failures", function () {
570
1108
  entryFile: "entry.ts"
571
1109
  })).rejects.toThrow("NATIVE_TRANSFORM_NEGATIVE_CONTROL");
572
1110
  } finally {
1111
+ spy.mockRestore();
573
1112
  compiler.configureVXRNCompilerPlugin({
574
1113
  enableReanimated: false
575
1114
  });
@@ -607,6 +1146,238 @@ describe("native required transform failures", function () {
607
1146
  });
608
1147
  }
609
1148
  });
1149
+ it("composes source maps across React Compiler, Fast Refresh, and module wrapper back to original source lines", async function () {
1150
+ var {
1151
+ TraceMap,
1152
+ originalPositionFor
1153
+ } = await import("@jridgewell/trace-mapping");
1154
+ var compiler = await import("@vxrn/compiler");
1155
+ compiler.configureVXRNCompilerPlugin({
1156
+ enableCompiler: true
1157
+ });
1158
+ try {
1159
+ var plugin = vxrnCompilerPlugin("ios", true, "/project", true);
1160
+ if (typeof plugin.transform !== "function") throw new Error("transform hook not callable");
1161
+ var inputCode = `import { useState } from 'react'
1162
+
1163
+ export function Counter() {
1164
+ const [count, setCount] = useState(0)
1165
+ return <button onClick={() => setCount(count + 1)}>{count}</button>
1166
+ }
1167
+ `;
1168
+ var id = "/project/Counter.tsx";
1169
+ var result = await Reflect.apply(plugin.transform, void 0, [inputCode, id]);
1170
+ expect(result).toBeDefined();
1171
+ expect(result.map).toBeDefined();
1172
+ var tracer = new TraceMap(result.map);
1173
+ var counterLineIndex = result.code.split("\n").findIndex(function (l) {
1174
+ return l.includes("function Counter");
1175
+ });
1176
+ expect(counterLineIndex).toBeGreaterThan(0);
1177
+ var pos = originalPositionFor(tracer, {
1178
+ line: counterLineIndex + 1,
1179
+ column: 16
1180
+ });
1181
+ expect(pos.source).toBe(id);
1182
+ expect(pos.line).toBe(3);
1183
+ } finally {
1184
+ compiler.configureVXRNCompilerPlugin({
1185
+ enableCompiler: false
1186
+ });
1187
+ }
1188
+ });
1189
+ it("composes source maps accurately across mixed path (React Compiler + Babel + Fast Refresh + module wrapper)", async function () {
1190
+ var {
1191
+ TraceMap,
1192
+ originalPositionFor
1193
+ } = await import("@jridgewell/trace-mapping");
1194
+ var compiler = await import("@vxrn/compiler");
1195
+ var origEnv = process.env.VXRN_USE_BABEL_FOR_GENERATORS;
1196
+ process.env.VXRN_USE_BABEL_FOR_GENERATORS = "1";
1197
+ compiler.configureVXRNCompilerPlugin({
1198
+ enableCompiler: true,
1199
+ enableReanimated: false
1200
+ });
1201
+ try {
1202
+ var plugin = vxrnCompilerPlugin("ios", true, "/project", true);
1203
+ if (typeof plugin.transform !== "function") throw new Error("transform hook not callable");
1204
+ var inputCode = `export function Counter() { return <View />; }
1205
+
1206
+
1207
+
1208
+
1209
+
1210
+
1211
+
1212
+ export async function* probe() { throw new Error("MARKER"); }
1213
+ `;
1214
+ var id = "/project/Counter.tsx";
1215
+ var result = await Reflect.apply(plugin.transform, void 0, [inputCode, id]);
1216
+ expect(result).toBeDefined();
1217
+ expect(result.map).toBeDefined();
1218
+ expect(result.code.includes("_wrapAsyncGenerator") || result.code.includes("regeneratorRuntime")).toBe(true);
1219
+ var tracer = new TraceMap(result.map);
1220
+ var lines = result.code.split("\n");
1221
+ var markerLineIndex = lines.findIndex(function (l) {
1222
+ return l.includes("MARKER");
1223
+ });
1224
+ expect(markerLineIndex).toBeGreaterThan(0);
1225
+ var markerCol = lines[markerLineIndex].indexOf("throw");
1226
+ var pos = originalPositionFor(tracer, {
1227
+ line: markerLineIndex + 1,
1228
+ column: markerCol
1229
+ });
1230
+ expect(pos.source).toBe(id);
1231
+ expect(pos.line).toBe(9);
1232
+ expect(pos.column).toBe(33);
1233
+ var compilerOut = await compiler.transformOxcReactCompiler(id, inputCode, "19", true);
1234
+ var remapping = (await import("@jridgewell/remapping")).default;
1235
+ var badPos = originalPositionFor(new TraceMap(remapping([result.map, compilerOut.map], function () {
1236
+ return null;
1237
+ })), {
1238
+ line: markerLineIndex + 1,
1239
+ column: markerCol
1240
+ });
1241
+ expect(badPos.source).toBeNull();
1242
+ expect(badPos.line).toBeNull();
1243
+ } finally {
1244
+ compiler.configureVXRNCompilerPlugin({
1245
+ enableCompiler: false
1246
+ });
1247
+ if (origEnv !== void 0) process.env.VXRN_USE_BABEL_FOR_GENERATORS = origEnv;else delete process.env.VXRN_USE_BABEL_FOR_GENERATORS;
1248
+ }
1249
+ });
1250
+ it("transforms worklets via native Rust/Wasm SWC with zero Babel execution and accurate sourcemaps", async function () {
1251
+ var {
1252
+ TraceMap,
1253
+ originalPositionFor
1254
+ } = await import("@jridgewell/trace-mapping");
1255
+ var compiler = await import("@vxrn/compiler");
1256
+ var {
1257
+ vi: vi2
1258
+ } = await import("vitest");
1259
+ var babelSpy = vi2.spyOn(compiler, "transformBabel");
1260
+ compiler.configureVXRNCompilerPlugin({
1261
+ enableReanimated: true,
1262
+ enableNativeWorklets: true,
1263
+ enableCompiler: false
1264
+ });
1265
+ try {
1266
+ var projectRoot = process.cwd();
1267
+ var plugin = vxrnCompilerPlugin("ios", true, projectRoot, true);
1268
+ if (typeof plugin.transform !== "function") throw new Error("transform hook not callable");
1269
+ var inputCode = `import { useAnimatedStyle } from 'react-native-reanimated'
1270
+
1271
+ export function Box() {
1272
+ const animatedStyle = useAnimatedStyle(() => {
1273
+ return { opacity: 1 }
1274
+ })
1275
+ return <div style={animatedStyle} />
1276
+ }
1277
+ `;
1278
+ var id = join(projectRoot, "Box.tsx");
1279
+ var result = await Reflect.apply(plugin.transform, void 0, [inputCode, id]);
1280
+ expect(result).toBeDefined();
1281
+ expect(result.code).toContain("__workletHash");
1282
+ expect(result.code).toContain("__closure");
1283
+ expect(babelSpy).not.toHaveBeenCalled();
1284
+ expect(result.map).toBeDefined();
1285
+ var tracer = new TraceMap(result.map);
1286
+ var boxLineIndex = result.code.split("\n").findIndex(function (l) {
1287
+ return l.includes("export function Box");
1288
+ });
1289
+ expect(boxLineIndex).toBeGreaterThan(0);
1290
+ var pos = originalPositionFor(tracer, {
1291
+ line: boxLineIndex + 1,
1292
+ column: 16
1293
+ });
1294
+ expect(pos.source).toBe(id);
1295
+ expect(pos.line).toBe(3);
1296
+ } finally {
1297
+ babelSpy.mockRestore();
1298
+ compiler.configureVXRNCompilerPlugin({
1299
+ enableReanimated: false,
1300
+ enableNativeWorklets: false
1301
+ });
1302
+ }
1303
+ });
1304
+ it("retains the existing Babel backend by default when enableNativeWorklets is false for auto-detected Reanimated and ordinary configured worklets plugin", async function () {
1305
+ var compiler = await import("@vxrn/compiler");
1306
+ var {
1307
+ vi: vi2
1308
+ } = await import("vitest");
1309
+ var babelSpy = vi2.spyOn(compiler, "transformBabel").mockResolvedValue({
1310
+ code: "/* babel transformed */"
1311
+ });
1312
+ var workletSpy = vi2.spyOn(compiler, "transformWorklets");
1313
+ compiler.configureVXRNCompilerPlugin({
1314
+ enableReanimated: true,
1315
+ enableNativeWorklets: false
1316
+ });
1317
+ var getBabelOptionsSpy = vi2.spyOn(compiler, "getBabelOptions").mockReturnValue({
1318
+ plugins: ["react-native-reanimated/plugin"]
1319
+ });
1320
+ try {
1321
+ var projectRoot = process.cwd();
1322
+ var plugin = vxrnCompilerPlugin("ios", true, projectRoot, false);
1323
+ if (typeof plugin.transform !== "function") throw new Error("transform hook not callable");
1324
+ var inputCode = `export function fn() { 'worklet'; return 1 }`;
1325
+ var id = join(projectRoot, "DefaultWorklet.tsx");
1326
+ expect(await Reflect.apply(plugin.transform, void 0, [inputCode, id])).toBeDefined();
1327
+ expect(babelSpy).toHaveBeenCalledWith(id, inputCode, expect.objectContaining({
1328
+ plugins: expect.arrayContaining(["react-native-reanimated/plugin"])
1329
+ }));
1330
+ expect(workletSpy).not.toHaveBeenCalled();
1331
+ } finally {
1332
+ babelSpy.mockRestore();
1333
+ workletSpy.mockRestore();
1334
+ getBabelOptionsSpy.mockRestore();
1335
+ compiler.configureVXRNCompilerPlugin({
1336
+ enableReanimated: false,
1337
+ enableNativeWorklets: false
1338
+ });
1339
+ }
1340
+ });
1341
+ it("intercepts tuple worklet plugins with options, executes native SWC with options, and skips Babel when enableNativeWorklets is true", async function () {
1342
+ var compiler = await import("@vxrn/compiler");
1343
+ var {
1344
+ vi: vi2
1345
+ } = await import("vitest");
1346
+ var babelSpy = vi2.spyOn(compiler, "transformBabel");
1347
+ var workletSpy = vi2.spyOn(compiler, "transformWorklets");
1348
+ compiler.configureVXRNCompilerPlugin({
1349
+ enableReanimated: true,
1350
+ enableNativeWorklets: true
1351
+ });
1352
+ var getBabelOptionsSpy = vi2.spyOn(compiler, "getBabelOptions").mockReturnValue({
1353
+ plugins: [["react-native-reanimated/plugin", {
1354
+ globals: ["customGlobal"]
1355
+ }]]
1356
+ });
1357
+ try {
1358
+ var projectRoot = process.cwd();
1359
+ var plugin = vxrnCompilerPlugin("ios", true, projectRoot, false);
1360
+ if (typeof plugin.transform !== "function") throw new Error("transform hook not callable");
1361
+ var inputCode = `export function fn() { 'worklet'; return customGlobal * 2 }`;
1362
+ var id = join(projectRoot, "TupleWorklet.tsx");
1363
+ var result = await Reflect.apply(plugin.transform, void 0, [inputCode, id]);
1364
+ expect(result).toBeDefined();
1365
+ expect(result.code).toContain("__workletHash");
1366
+ expect(workletSpy).toHaveBeenCalledWith(id, expect.any(String), false, expect.objectContaining({
1367
+ globals: ["customGlobal"],
1368
+ projectRoot
1369
+ }));
1370
+ expect(babelSpy).not.toHaveBeenCalled();
1371
+ } finally {
1372
+ babelSpy.mockRestore();
1373
+ workletSpy.mockRestore();
1374
+ getBabelOptionsSpy.mockRestore();
1375
+ compiler.configureVXRNCompilerPlugin({
1376
+ enableReanimated: false,
1377
+ enableNativeWorklets: false
1378
+ });
1379
+ }
1380
+ });
610
1381
  });
611
1382
  describe("native production assets", function () {
612
1383
  it("registers scale siblings and keeps monorepo assets inside assetsDest", async function () {
@@ -811,4 +1582,65 @@ globalThis.__vxrnConditionalExportProbe = helper()
811
1582
  }
812
1583
  });
813
1584
  });
1585
+ describe("native Flow sources", function () {
1586
+ it.each(["", `/* ${"license text ".repeat(150)} */
1587
+ `])("strips third-party Flow following a license header (%#)", async function (license) {
1588
+ var testRoot = await mkdtemp(join(tmpdir(), "vxrn-native-flow-"));
1589
+ var packageRoot = join(testRoot, "node_modules/@flowy-scope/flowy-lib");
1590
+ await mkdir(packageRoot, {
1591
+ recursive: true
1592
+ });
1593
+ await writeFile(join(packageRoot, "package.json"), JSON.stringify({
1594
+ name: "@flowy-scope/flowy-lib",
1595
+ main: "./js/Flowy.js"
1596
+ }));
1597
+ await mkdir(join(packageRoot, "js"), {
1598
+ recursive: true
1599
+ });
1600
+ await writeFile(join(packageRoot, "js/Flowy.js"), `${license}/**
1601
+ * @flow
1602
+ * @format
1603
+ */
1604
+
1605
+ type FlowyProps = {|
1606
+ +value: number,
1607
+ |}
1608
+
1609
+ export default class Flowy {
1610
+ _seen: boolean = false
1611
+
1612
+ measure(props: FlowyProps): number {
1613
+ const { value, ...rest }: FlowyProps = props
1614
+ this._seen = true
1615
+ return ((value: any): number) + Object.keys(rest).length
1616
+ }
1617
+ }
1618
+ `);
1619
+ await writeFile(join(testRoot, "entry.js"), `import Flowy from '@flowy-scope/flowy-lib'
1620
+ globalThis.__vxrnFlowProbe = new Flowy().measure({ value: 41 }) + 1
1621
+ `);
1622
+ try {
1623
+ var result = await buildNativeBundle({
1624
+ root: testRoot,
1625
+ platform: "ios",
1626
+ entryFile: "entry.js"
1627
+ });
1628
+ var context = {
1629
+ clearTimeout,
1630
+ console,
1631
+ process: {
1632
+ env: {}
1633
+ },
1634
+ setTimeout
1635
+ };
1636
+ runInNewContext(result.code, context);
1637
+ expect(Reflect.get(context, "__vxrnFlowProbe")).toBe(42);
1638
+ } finally {
1639
+ await rm(testRoot, {
1640
+ recursive: true,
1641
+ force: true
1642
+ });
1643
+ }
1644
+ });
1645
+ });
814
1646
  //# sourceMappingURL=createNativeDevEngine.test.native.js.map