@vxrn/compiler 1.26.1 → 1.27.1-1789414853455

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.
@@ -48,30 +48,44 @@ function getCacheDir() {
48
48
  }
49
49
  return cacheDir;
50
50
  }
51
- function getConfigFingerprint() {
51
+ function getConfigFingerprint(userBabelConfigPath) {
52
52
  return (0, import_node_crypto.createHash)("sha1").update(JSON.stringify({
53
53
  compiler: import_configure.configuration.enableCompiler,
54
54
  reanimated: import_configure.configuration.enableReanimated,
55
55
  nativeWorklets: (0, import_configure.isNativeWorkletsEnabled)(),
56
56
  nativewind: import_configure.configuration.enableNativewind,
57
57
  nativeCSS: import_configure.configuration.enableNativeCSS,
58
+ // transform output depends on the user babel config when one applies,
59
+ // so its identity joins the fingerprint. adding, removing, or editing
60
+ // babel.config.* must miss entries written without that change.
61
+ babelConfig: getBabelConfigIdentity(userBabelConfigPath),
58
62
  // bump when the transform engine changes, so entries written by a
59
63
  // previous engine aren't served for the same source
60
64
  engine: "oxc-worklets-hermes-async"
61
65
  })).digest("hex").slice(0, 8);
62
66
  }
63
- function getCacheKey(filePath, environment) {
64
- const hash = (0, import_node_crypto.createHash)("sha1").update(`${environment}:${filePath}:${getConfigFingerprint()}`).digest("hex");
67
+ function getBabelConfigIdentity(configPath) {
68
+ if (!configPath) return null;
69
+ try {
70
+ const mtime = (0, import_node_fs.statSync)(configPath).mtimeMs;
71
+ const hash = (0, import_node_crypto.createHash)("sha1").update((0, import_node_fs.readFileSync)(configPath)).digest("hex").slice(0, 16);
72
+ return `${configPath}:${mtime}:${hash}`;
73
+ } catch {
74
+ return configPath;
75
+ }
76
+ }
77
+ function getCacheKey(filePath, environment, userBabelConfigPath) {
78
+ const hash = (0, import_node_crypto.createHash)("sha1").update(`${environment}:${filePath}:${getConfigFingerprint(userBabelConfigPath)}`).digest("hex");
65
79
  return hash;
66
80
  }
67
81
  function getContentHash(code) {
68
82
  return (0, import_node_crypto.createHash)("sha1").update(code).digest("hex").slice(0, 16);
69
83
  }
70
- function getCachedTransform(filePath, code, environment) {
84
+ function getCachedTransform(filePath, code, environment, userBabelConfigPath) {
71
85
  try {
72
86
  const cleanPath = filePath.startsWith("\0") ? filePath.slice(1) : filePath;
73
87
  const cacheDir = getCacheDir();
74
- const cacheKey = getCacheKey(cleanPath, environment);
88
+ const cacheKey = getCacheKey(cleanPath, environment, userBabelConfigPath);
75
89
  const cachePath = (0, import_node_path.join)(cacheDir, `${cacheKey}.json`);
76
90
  if (!(0, import_node_fs.existsSync)(cachePath)) {
77
91
  stats.misses++;
@@ -98,11 +112,11 @@ function getCachedTransform(filePath, code, environment) {
98
112
  return null;
99
113
  }
100
114
  }
101
- function setCachedTransform(filePath, code, result, environment) {
115
+ function setCachedTransform(filePath, code, result, environment, userBabelConfigPath) {
102
116
  try {
103
117
  const cleanPath = filePath.startsWith("\0") ? filePath.slice(1) : filePath;
104
118
  const cacheDir = getCacheDir();
105
- const cacheKey = getCacheKey(cleanPath, environment);
119
+ const cacheKey = getCacheKey(cleanPath, environment, userBabelConfigPath);
106
120
  const cachePath = (0, import_node_path.join)(cacheDir, `${cacheKey}.json`);
107
121
  const mtime = (0, import_node_fs.statSync)(cleanPath).mtimeMs;
108
122
  const hash = getContentHash(code);
@@ -155,7 +155,8 @@ async function performBabelTransform({
155
155
  });
156
156
  if (babelOptions) {
157
157
  const hasCompilerPlugin = babelOptions.plugins?.some(x => Array.isArray(x) && x[0] === "babel-plugin-react-compiler");
158
- const cached = (0, import_cache.getCachedTransform)(id, code, environment);
158
+ const userBabelConfigPath = typeof babelOptions.configFile === "string" ? babelOptions.configFile : null;
159
+ const cached = (0, import_cache.getCachedTransform)(id, code, environment, userBabelConfigPath);
159
160
  if (cached) {
160
161
  perfStats.babel.byEnvironment[environment].transforms++;
161
162
  if (hasCompilerPlugin && (cached.code.includes("react/compiler-runtime") || cached.code.includes("react-compiler-runtime"))) {
@@ -181,14 +182,18 @@ async function performBabelTransform({
181
182
  let compilerOut = null;
182
183
  const compilerPluginIndex = babelOptions.plugins?.findIndex(x => Array.isArray(x) && x[0] === "babel-plugin-react-compiler") ?? -1;
183
184
  if (compilerPluginIndex !== -1) {
184
- if (useWorklets) {
185
- workletPreparation = (0, import_transformWorklets.prepareWorkletsForReactCompiler)(id, curCode, (0, import_transformSWC.shouldSourceMap)());
186
- if (workletPreparation) curCode = workletPreparation.code;
185
+ const compilerPluginConfig = babelOptions.plugins[compilerPluginIndex];
186
+ const compilerPluginOptions = compilerPluginConfig[1] || {};
187
+ const useBabelCompiler = compilerPluginOptions.compiler === "babel" || process.env.ONE_REACT_COMPILER === "babel" || process.env.VXRN_REACT_COMPILER === "babel";
188
+ if (!useBabelCompiler) {
189
+ if (useWorklets) {
190
+ workletPreparation = (0, import_transformWorklets.prepareWorkletsForReactCompiler)(id, curCode, (0, import_transformSWC.shouldSourceMap)());
191
+ if (workletPreparation) curCode = workletPreparation.code;
192
+ }
193
+ compilerOut = await (0, import_transformBabel.transformOxcReactCompiler)(id, curCode, compilerPluginOptions, (0, import_transformSWC.shouldSourceMap)());
194
+ if (compilerOut) curCode = compilerOut.code;
195
+ babelOptions.plugins.splice(compilerPluginIndex, 1);
187
196
  }
188
- const compilerTarget = babelOptions.plugins[compilerPluginIndex][1]?.target ?? "19";
189
- compilerOut = await (0, import_transformBabel.transformOxcReactCompiler)(id, curCode, compilerTarget, (0, import_transformSWC.shouldSourceMap)());
190
- if (compilerOut) curCode = compilerOut.code;
191
- babelOptions.plugins.splice(compilerPluginIndex, 1);
192
197
  }
193
198
  let workletsOut = null;
194
199
  if (useWorklets) {
@@ -201,7 +206,7 @@ async function performBabelTransform({
201
206
  }
202
207
  const startTime = Date.now();
203
208
  let babelOut = null;
204
- if (babelOptions.plugins?.length === 0) {
209
+ if (babelOptions.plugins?.length === 0 && !babelOptions.configFile && !babelOptions.babelrc) {
205
210
  let finalMap = void 0;
206
211
  if ((0, import_transformSWC.shouldSourceMap)()) {
207
212
  const intermediateMaps = [workletPreparation?.map, compilerOut?.map, workletsOut?.map].filter(Boolean);
@@ -247,7 +252,7 @@ async function performBabelTransform({
247
252
  code: outCode,
248
253
  map: babelOut.map
249
254
  };
250
- (0, import_cache.setCachedTransform)(id, code, result, environment);
255
+ (0, import_cache.setCachedTransform)(id, code, result, environment, userBabelConfigPath);
251
256
  return result;
252
257
  }
253
258
  }
@@ -33,33 +33,71 @@ var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
33
33
  }), mod);
34
34
  var transformBabel_exports = {};
35
35
  __export(transformBabel_exports, {
36
+ findUserBabelConfig: () => findUserBabelConfig,
36
37
  getBabelOptions: () => getBabelOptions,
38
+ stripFlowTypes: () => stripFlowTypes,
37
39
  transformBabel: () => transformBabel,
38
40
  transformOxcReactCompiler: () => transformOxcReactCompiler
39
41
  });
40
42
  module.exports = __toCommonJS(transformBabel_exports);
43
+ var import_node_fs = require("node:fs");
41
44
  var import_node_path = require("node:path");
42
45
  var import_utils = require("@vxrn/utils");
43
46
  var import_vite = require("vite");
44
47
  var import_configure = require("./configure.cjs");
45
48
  var import_constants = require("./constants.cjs");
49
+ const USER_BABEL_CONFIG_FILES = ["babel.config.js", "babel.config.cjs", "babel.config.mjs", "babel.config.json", ".babelrc", ".babelrc.js", ".babelrc.json"];
50
+ const ONE_GENERATED_MARKER = "@one-generated";
51
+ function findUserBabelConfig(projectRoot) {
52
+ if (!projectRoot) return null;
53
+ for (const name of USER_BABEL_CONFIG_FILES) {
54
+ const fullPath = (0, import_node_path.join)(projectRoot, name);
55
+ if ((0, import_node_fs.existsSync)(fullPath)) {
56
+ try {
57
+ const content = (0, import_node_fs.readFileSync)(fullPath, "utf8");
58
+ if (content.includes(ONE_GENERATED_MARKER)) {
59
+ continue;
60
+ }
61
+ } catch {
62
+ continue;
63
+ }
64
+ return fullPath;
65
+ }
66
+ }
67
+ return null;
68
+ }
46
69
  function getBabelOptions(props) {
47
70
  props = {
48
71
  ...props,
49
72
  id: (0, import_vite.normalizePath)(props.id.split("?")[0])
50
73
  };
74
+ const isProjectFile = !props.id.includes("node_modules");
75
+ const userBabelConfig = isProjectFile && props.projectRoot ? findUserBabelConfig(props.projectRoot) : null;
51
76
  if (props.userSetting === "babel") {
52
- return getOptions(props, true);
77
+ return getOptions(props, true, userBabelConfig);
53
78
  }
54
79
  if (typeof props.userSetting === "undefined" || typeof props.userSetting === "object" && props.userSetting.transform === "babel") {
55
80
  if (props.userSetting?.excludeDefaultPlugins) {
56
- return props.userSetting;
81
+ return {
82
+ ...props.userSetting,
83
+ ...(userBabelConfig ? {
84
+ configFile: userBabelConfig,
85
+ babelrc: true
86
+ } : {})
87
+ };
57
88
  }
58
- return getOptions(props);
89
+ return getOptions(props, false, userBabelConfig);
90
+ }
91
+ const userSetting = props.userSetting;
92
+ if (userSetting === "swc" || userSetting === "oxc" || userSetting === false || typeof userSetting === "object" && userSetting !== null && (userSetting.transform === "swc" || userSetting.transform === "oxc")) {
93
+ return null;
94
+ }
95
+ if (userBabelConfig) {
96
+ return getOptions(props, false, userBabelConfig);
59
97
  }
60
98
  return null;
61
99
  }
62
- const getOptions = (props, force = false) => {
100
+ const getOptions = (props, force = false, userBabelConfig = null) => {
63
101
  let plugins = [];
64
102
  if (force || shouldBabelGenerators(props)) {
65
103
  plugins = getBasePlugins(props);
@@ -85,22 +123,35 @@ const getOptions = (props, force = false) => {
85
123
  (0, import_constants.debug)?.(`Using babel react compiler on file`);
86
124
  plugins.push(getBabelReactCompilerPlugin(props));
87
125
  }
88
- if (plugins.length) {
126
+ if (plugins.length || userBabelConfig) {
89
127
  return {
90
- plugins
128
+ plugins,
129
+ ...(userBabelConfig ? {
130
+ configFile: userBabelConfig,
131
+ babelrc: true
132
+ } : {})
91
133
  };
92
134
  }
93
135
  return null;
94
136
  };
95
- async function transformOxcReactCompiler(id, code, target, sourceMap = false) {
137
+ async function transformOxcReactCompiler(id, code, optionsOrTarget = "19", sourceMap = false) {
96
138
  const {
97
139
  transform
98
140
  } = await import("oxc-transform-react");
141
+ const compilerOptions = typeof optionsOrTarget === "string" ? {
142
+ target: optionsOrTarget
143
+ } : optionsOrTarget || {};
144
+ const target = compilerOptions.target ?? "19";
99
145
  const result = await transform(id, code, {
100
146
  jsx: "preserve",
101
147
  sourcemap: sourceMap,
102
148
  reactCompiler: {
103
- target
149
+ target,
150
+ ...compilerOptions,
151
+ environment: {
152
+ enableCustomTypeDefinitionForReanimated: true,
153
+ ...compilerOptions.environment
154
+ }
104
155
  }
105
156
  });
106
157
  if (result.fatal) {
@@ -126,8 +177,8 @@ async function transformBabel(id, code, options) {
126
177
  const babelOptions = {
127
178
  filename: id,
128
179
  compact: false,
129
- babelrc: false,
130
- configFile: false,
180
+ babelrc: options.babelrc ?? false,
181
+ configFile: options.configFile ?? false,
131
182
  sourceMaps: false,
132
183
  minified: false,
133
184
  ...options,
@@ -146,7 +197,7 @@ async function transformBabel(id, code, options) {
146
197
  plugins: [...(isTS ? [] : [[hermesParserPlugin, {
147
198
  parseLangTypes: "flow",
148
199
  reactRuntimeTarget: "19"
149
- }]]), ...(options.plugins || []), ...(isTS ? [] : ["@babel/plugin-transform-flow-strip-types"])]
200
+ }], "babel-plugin-transform-flow-enums"]), ...(options.plugins || []), ...(isTS ? [] : ["@babel/plugin-transform-flow-strip-types"])]
150
201
  };
151
202
  return await new Promise((res, rej) => {
152
203
  babelCore.transform(code, babelOptions, (err, result) => {
@@ -157,6 +208,15 @@ async function transformBabel(id, code, options) {
157
208
  });
158
209
  });
159
210
  }
211
+ async function stripFlowTypes(id, code, sourceMaps = true) {
212
+ const result = await transformBabel(id, code, {
213
+ sourceMaps
214
+ });
215
+ return {
216
+ code: result.code,
217
+ map: result.map
218
+ };
219
+ }
160
220
  const getBasePlugins = ({
161
221
  development
162
222
  }) => [["@babel/plugin-transform-destructuring"], ["@babel/plugin-transform-react-jsx", {
@@ -85,6 +85,23 @@ var import_transformBabel = require("./transformBabel.cjs");
85
85
  (0, import_vitest.expect)(result?.code).toContain("codegenNativeComponent('VirtualView')");
86
86
  (0, import_vitest.expect)(result?.code).not.toContain("HostComponent");
87
87
  });
88
+ (0, import_vitest.it)("preserves React Native Flow enums as runtime values", async () => {
89
+ const result = await (0, import_transformBabel.transformBabel)("/project/VirtualView.js", `
90
+ // @flow strict-local
91
+ export enum VirtualViewRenderState {
92
+ Unknown = 0,
93
+ Rendered = 1,
94
+ None = 2,
95
+ }
96
+
97
+ export const rendered = VirtualViewRenderState.Rendered
98
+ `, {
99
+ plugins: []
100
+ });
101
+ (0, import_vitest.expect)(result?.code).toContain("const VirtualViewRenderState =");
102
+ (0, import_vitest.expect)(result?.code).toContain("Rendered: 1");
103
+ (0, import_vitest.expect)(result?.code).toContain("VirtualViewRenderState.Rendered");
104
+ });
88
105
  (0, import_vitest.it)("rejects a required transform error instead of returning untransformed source", async () => {
89
106
  const negativeControl = () => ({
90
107
  visitor: {
@@ -123,6 +140,24 @@ var import_transformBabel = require("./transformBabel.cjs");
123
140
  const result = await (0, import_transformBabel.transformOxcReactCompiler)("/project/Counter.tsx", componentCode, "18", false);
124
141
  (0, import_vitest.expect)(result.code).toContain("_c(");
125
142
  });
143
+ (0, import_vitest.it)("supports mutating useSharedValue in useEffect via Reanimated environment defaults", async () => {
144
+ const reanimatedCode = `
145
+ import { useEffect } from 'react'
146
+ import { useSharedValue } from 'react-native-reanimated'
147
+
148
+ export function ReanimatedComponent() {
149
+ const val = useSharedValue(0)
150
+ useEffect(() => {
151
+ val.value = 1
152
+ }, [val])
153
+ return <div>{val.value}</div>
154
+ }
155
+ `;
156
+ const result = await (0, import_transformBabel.transformOxcReactCompiler)("/project/ReanimatedComponent.tsx", reanimatedCode, {
157
+ target: "19"
158
+ }, false);
159
+ (0, import_vitest.expect)(result.code).toContain("_c(");
160
+ });
126
161
  });
127
162
  (0, import_vitest.describe)("compiler plugin multi-stage source map composition", () => {
128
163
  (0, import_vitest.it)("composes earlier compiler maps when Babel also runs", async () => {
@@ -357,4 +392,278 @@ export async function* generatorProbe() {
357
392
  });
358
393
  }
359
394
  });
395
+ });
396
+ (0, import_vitest.describe)("findUserBabelConfig and user Babel config respect", () => {
397
+ (0, import_vitest.it)("finds user babel config and ignores generated configs", () => {
398
+ const projectRoot = import_node_fs.default.realpathSync(import_node_fs.default.mkdtempSync(import_node_path.default.join(import_node_os.default.tmpdir(), "vxrn-babel-conf-")));
399
+ try {
400
+ (0, import_vitest.expect)((0, import_transformBabel.findUserBabelConfig)(projectRoot)).toBeNull();
401
+ const generatedFile = import_node_path.default.join(projectRoot, "babel.config.js");
402
+ import_node_fs.default.writeFileSync(generatedFile, "// @one-generated\nmodule.exports = {}");
403
+ (0, import_vitest.expect)((0, import_transformBabel.findUserBabelConfig)(projectRoot)).toBeNull();
404
+ import_node_fs.default.writeFileSync(generatedFile, "module.exports = { plugins: [] }");
405
+ (0, import_vitest.expect)((0, import_transformBabel.findUserBabelConfig)(projectRoot)).toBe(generatedFile);
406
+ } finally {
407
+ import_node_fs.default.rmSync(projectRoot, {
408
+ recursive: true,
409
+ force: true
410
+ });
411
+ }
412
+ });
413
+ (0, import_vitest.it)("includes user configFile and babelrc in getBabelOptions for project files", () => {
414
+ const projectRoot = import_node_fs.default.realpathSync(import_node_fs.default.mkdtempSync(import_node_path.default.join(import_node_os.default.tmpdir(), "vxrn-babel-conf-")));
415
+ const userConfig = import_node_path.default.join(projectRoot, "babel.config.json");
416
+ import_node_fs.default.writeFileSync(userConfig, '{"plugins": []}');
417
+ try {
418
+ const projectFileOptions = (0, import_transformBabel.getBabelOptions)({
419
+ id: import_node_path.default.join(projectRoot, "src", "index.tsx"),
420
+ code: `export const hello = () => 123`,
421
+ projectRoot,
422
+ development: true,
423
+ environment: "client",
424
+ reactForRNVersion: "19"
425
+ });
426
+ (0, import_vitest.expect)(projectFileOptions?.configFile).toBe(userConfig);
427
+ (0, import_vitest.expect)(projectFileOptions?.babelrc).toBe(true);
428
+ const nodeModulesOptions = (0, import_transformBabel.getBabelOptions)({
429
+ id: import_node_path.default.join(projectRoot, "node_modules", "foo", "index.js"),
430
+ code: `export const foo = 1`,
431
+ projectRoot,
432
+ development: true,
433
+ environment: "client",
434
+ reactForRNVersion: "19"
435
+ });
436
+ (0, import_vitest.expect)(nodeModulesOptions).toBeNull();
437
+ } finally {
438
+ import_node_fs.default.rmSync(projectRoot, {
439
+ recursive: true,
440
+ force: true
441
+ });
442
+ }
443
+ });
444
+ (0, import_vitest.it)("runs user babel config transforms via transformBabel", async () => {
445
+ const projectRoot = import_node_fs.default.realpathSync(import_node_fs.default.mkdtempSync(import_node_path.default.join(import_node_os.default.tmpdir(), "vxrn-babel-conf-")));
446
+ const userConfig = import_node_path.default.join(projectRoot, "babel.config.json");
447
+ import_node_fs.default.writeFileSync(userConfig, JSON.stringify({
448
+ comments: false
449
+ }));
450
+ try {
451
+ const code = "/* remove me */ export const x = 1";
452
+ const res = await (0, import_transformBabel.transformBabel)(import_node_path.default.join(projectRoot, "src", "index.ts"), code, {
453
+ configFile: userConfig,
454
+ babelrc: true
455
+ });
456
+ (0, import_vitest.expect)(res.code).not.toContain("remove me");
457
+ (0, import_vitest.expect)(res.code).toContain("export const x = 1");
458
+ } finally {
459
+ import_node_fs.default.rmSync(projectRoot, {
460
+ recursive: true,
461
+ force: true
462
+ });
463
+ }
464
+ });
465
+ });
466
+ (0, import_vitest.describe)("explicit swc/oxc per-file choice with a user babel config", () => {
467
+ (0, import_vitest.it)("returns null for swc/oxc string and object forms", () => {
468
+ const projectRoot = import_node_fs.default.realpathSync(import_node_fs.default.mkdtempSync(import_node_path.default.join(import_node_os.default.tmpdir(), "vxrn-babel-conf-")));
469
+ const userConfig = import_node_path.default.join(projectRoot, "babel.config.js");
470
+ import_node_fs.default.writeFileSync(userConfig, "module.exports = { plugins: [] }");
471
+ try {
472
+ const base = {
473
+ id: import_node_path.default.join(projectRoot, "src", "index.tsx"),
474
+ code: `export const x = 1`,
475
+ projectRoot,
476
+ development: true,
477
+ environment: "client",
478
+ reactForRNVersion: "19"
479
+ };
480
+ (0, import_vitest.expect)((0, import_transformBabel.getBabelOptions)({
481
+ ...base,
482
+ userSetting: "swc"
483
+ })).toBeNull();
484
+ (0, import_vitest.expect)((0, import_transformBabel.getBabelOptions)({
485
+ ...base,
486
+ userSetting: "oxc"
487
+ })).toBeNull();
488
+ (0, import_vitest.expect)((0, import_transformBabel.getBabelOptions)({
489
+ ...base,
490
+ userSetting: {
491
+ transform: "swc"
492
+ }
493
+ })).toBeNull();
494
+ (0, import_vitest.expect)((0, import_transformBabel.getBabelOptions)({
495
+ ...base,
496
+ userSetting: {
497
+ transform: "oxc"
498
+ }
499
+ })).toBeNull();
500
+ (0, import_vitest.expect)((0, import_transformBabel.getBabelOptions)({
501
+ ...base,
502
+ userSetting: "babel"
503
+ })?.configFile).toBe(userConfig);
504
+ (0, import_vitest.expect)((0, import_transformBabel.getBabelOptions)({
505
+ ...base,
506
+ userSetting: {
507
+ transform: "babel"
508
+ }
509
+ })?.configFile).toBe(userConfig);
510
+ (0, import_vitest.expect)((0, import_transformBabel.getBabelOptions)(base)?.configFile).toBe(userConfig);
511
+ } finally {
512
+ import_node_fs.default.rmSync(projectRoot, {
513
+ recursive: true,
514
+ force: true
515
+ });
516
+ }
517
+ });
518
+ (0, import_vitest.it)("skips the transform end-to-end for object-form swc", async () => {
519
+ const {
520
+ createVXRNCompilerPlugin
521
+ } = await import("./index");
522
+ const projectRoot = import_node_fs.default.realpathSync(import_node_fs.default.mkdtempSync(import_node_path.default.join(import_node_os.default.tmpdir(), "vxrn-babel-conf-")));
523
+ const srcDir = import_node_path.default.join(projectRoot, "src");
524
+ import_node_fs.default.mkdirSync(srcDir, {
525
+ recursive: true
526
+ });
527
+ const file = import_node_path.default.join(srcDir, "index.ts");
528
+ const code = `export const x = 1`;
529
+ import_node_fs.default.writeFileSync(file, code);
530
+ import_node_fs.default.writeFileSync(import_node_path.default.join(projectRoot, "babel.config.js"), "module.exports = { plugins: [] }");
531
+ (0, import_configure.configureVXRNCompilerPlugin)({
532
+ enableCompiler: false,
533
+ enableReanimated: false
534
+ });
535
+ try {
536
+ const plugins = await createVXRNCompilerPlugin({
537
+ transform: () => ({
538
+ transform: "swc"
539
+ })
540
+ });
541
+ const plugin = plugins.find(p => p.name === "one:compiler");
542
+ await plugin.configResolved({
543
+ root: projectRoot,
544
+ build: {}
545
+ });
546
+ const hook = plugin.transform.handler || plugin.transform;
547
+ const result = await hook.call({
548
+ environment: {
549
+ name: "client"
550
+ }
551
+ }, code, file);
552
+ (0, import_vitest.expect)(result == null).toBe(true);
553
+ } finally {
554
+ (0, import_configure.configureVXRNCompilerPlugin)({
555
+ enableCompiler: false,
556
+ enableReanimated: false
557
+ });
558
+ import_node_fs.default.rmSync(projectRoot, {
559
+ recursive: true,
560
+ force: true
561
+ });
562
+ }
563
+ });
564
+ });
565
+ (0, import_vitest.describe)("user babel config end-to-end through the compiler plugin", () => {
566
+ (0, import_vitest.it)("runs the user config when default plugins are empty", async () => {
567
+ const {
568
+ createVXRNCompilerPlugin
569
+ } = await import("./index");
570
+ const projectRoot = import_node_fs.default.realpathSync(import_node_fs.default.mkdtempSync(import_node_path.default.join(import_node_os.default.tmpdir(), "vxrn-babel-e2e-")));
571
+ const srcDir = import_node_path.default.join(projectRoot, "src");
572
+ import_node_fs.default.mkdirSync(srcDir, {
573
+ recursive: true
574
+ });
575
+ const file = import_node_path.default.join(srcDir, "index.ts");
576
+ const marker = "babel-e2e-probe";
577
+ const code = `/* ${marker} */ export const x = 1`;
578
+ import_node_fs.default.writeFileSync(file, code);
579
+ import_node_fs.default.writeFileSync(import_node_path.default.join(projectRoot, "babel.config.json"), JSON.stringify({
580
+ comments: false
581
+ }));
582
+ (0, import_configure.configureVXRNCompilerPlugin)({
583
+ enableCompiler: false,
584
+ enableReanimated: false
585
+ });
586
+ try {
587
+ const plugins = await createVXRNCompilerPlugin();
588
+ const plugin = plugins.find(p => p.name === "one:compiler");
589
+ await plugin.configResolved({
590
+ root: projectRoot,
591
+ build: {}
592
+ });
593
+ const hook = plugin.transform.handler || plugin.transform;
594
+ const result = await hook.call({
595
+ environment: {
596
+ name: "client"
597
+ }
598
+ }, code, file);
599
+ (0, import_vitest.expect)(result).toBeDefined();
600
+ (0, import_vitest.expect)(result.code).toContain("export const x = 1");
601
+ (0, import_vitest.expect)(result.code).not.toContain(marker);
602
+ } finally {
603
+ (0, import_configure.configureVXRNCompilerPlugin)({
604
+ enableCompiler: false,
605
+ enableReanimated: false
606
+ });
607
+ import_node_fs.default.rmSync(projectRoot, {
608
+ recursive: true,
609
+ force: true
610
+ });
611
+ }
612
+ });
613
+ (0, import_vitest.it)("invalidates the cache when the user babel config is added or edited", async () => {
614
+ const {
615
+ createVXRNCompilerPlugin
616
+ } = await import("./index");
617
+ const projectRoot = import_node_fs.default.realpathSync(import_node_fs.default.mkdtempSync(import_node_path.default.join(import_node_os.default.tmpdir(), "vxrn-babel-cache-")));
618
+ const srcDir = import_node_path.default.join(projectRoot, "src");
619
+ import_node_fs.default.mkdirSync(srcDir, {
620
+ recursive: true
621
+ });
622
+ const file = import_node_path.default.join(srcDir, "index.ts");
623
+ const marker = "babel-cache-probe";
624
+ const code = `/* ${marker} */ export const x = 1`;
625
+ import_node_fs.default.writeFileSync(file, code);
626
+ const userConfig = import_node_path.default.join(projectRoot, "babel.config.json");
627
+ (0, import_configure.configureVXRNCompilerPlugin)({
628
+ enableCompiler: false,
629
+ enableReanimated: false
630
+ });
631
+ try {
632
+ const plugins = await createVXRNCompilerPlugin({
633
+ transform: () => "babel"
634
+ });
635
+ const plugin = plugins.find(p => p.name === "one:compiler");
636
+ await plugin.configResolved({
637
+ root: projectRoot,
638
+ build: {}
639
+ });
640
+ const hook = plugin.transform.handler || plugin.transform;
641
+ const context = {
642
+ environment: {
643
+ name: "client"
644
+ }
645
+ };
646
+ const res1 = await hook.call(context, code, file);
647
+ (0, import_vitest.expect)(res1.code).toContain(marker);
648
+ import_node_fs.default.writeFileSync(userConfig, JSON.stringify({
649
+ comments: false
650
+ }));
651
+ const res2 = await hook.call(context, code, file);
652
+ (0, import_vitest.expect)(res2.code).not.toContain(marker);
653
+ import_node_fs.default.writeFileSync(userConfig, JSON.stringify({
654
+ comments: true
655
+ }));
656
+ const res3 = await hook.call(context, code, file);
657
+ (0, import_vitest.expect)(res3.code).toContain(marker);
658
+ } finally {
659
+ (0, import_configure.configureVXRNCompilerPlugin)({
660
+ enableCompiler: false,
661
+ enableReanimated: false
662
+ });
663
+ import_node_fs.default.rmSync(projectRoot, {
664
+ recursive: true,
665
+ force: true
666
+ });
667
+ }
668
+ });
360
669
  });
@@ -24,6 +24,7 @@ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
24
24
  }) : target, mod));
25
25
  var import_node_fs = __toESM(require("node:fs"), 1);
26
26
  var import_node_os = __toESM(require("node:os"), 1);
27
+ var import_node_module = require("node:module");
27
28
  var import_node_path = __toESM(require("node:path"), 1);
28
29
  var import_vitest = require("vitest");
29
30
  var import_configure = require("./configure.cjs");
@@ -322,7 +323,8 @@ var ${innerVar[1]} = {`);
322
323
  (0, import_vitest.expect)(typeof scaleFn).toBe("function");
323
324
  (0, import_vitest.expect)(scaleFn.__workletHash).toBeDefined();
324
325
  (0, import_vitest.expect)(typeof scaleFn.__workletHash).toBe("number");
325
- (0, import_vitest.expect)(scaleFn.__pluginVersion).toBe("0.10.1");
326
+ const installedWorklets = (0, import_node_module.createRequire)(import_node_path.default.resolve(process.cwd(), "package.json"))("react-native-worklets/package.json").version;
327
+ (0, import_vitest.expect)(scaleFn.__pluginVersion).toBe(installedWorklets);
326
328
  (0, import_vitest.expect)(scaleFn.__closure).toEqual({
327
329
  multiplier: 5
328
330
  });
@@ -17,30 +17,44 @@ function getCacheDir() {
17
17
  }
18
18
  return cacheDir;
19
19
  }
20
- function getConfigFingerprint() {
20
+ function getConfigFingerprint(userBabelConfigPath) {
21
21
  return createHash("sha1").update(JSON.stringify({
22
22
  compiler: configuration.enableCompiler,
23
23
  reanimated: configuration.enableReanimated,
24
24
  nativeWorklets: isNativeWorkletsEnabled(),
25
25
  nativewind: configuration.enableNativewind,
26
26
  nativeCSS: configuration.enableNativeCSS,
27
+ // transform output depends on the user babel config when one applies,
28
+ // so its identity joins the fingerprint. adding, removing, or editing
29
+ // babel.config.* must miss entries written without that change.
30
+ babelConfig: getBabelConfigIdentity(userBabelConfigPath),
27
31
  // bump when the transform engine changes, so entries written by a
28
32
  // previous engine aren't served for the same source
29
33
  engine: "oxc-worklets-hermes-async"
30
34
  })).digest("hex").slice(0, 8);
31
35
  }
32
- function getCacheKey(filePath, environment) {
33
- const hash = createHash("sha1").update(`${environment}:${filePath}:${getConfigFingerprint()}`).digest("hex");
36
+ function getBabelConfigIdentity(configPath) {
37
+ if (!configPath) return null;
38
+ try {
39
+ const mtime = statSync(configPath).mtimeMs;
40
+ const hash = createHash("sha1").update(readFileSync(configPath)).digest("hex").slice(0, 16);
41
+ return `${configPath}:${mtime}:${hash}`;
42
+ } catch {
43
+ return configPath;
44
+ }
45
+ }
46
+ function getCacheKey(filePath, environment, userBabelConfigPath) {
47
+ const hash = createHash("sha1").update(`${environment}:${filePath}:${getConfigFingerprint(userBabelConfigPath)}`).digest("hex");
34
48
  return hash;
35
49
  }
36
50
  function getContentHash(code) {
37
51
  return createHash("sha1").update(code).digest("hex").slice(0, 16);
38
52
  }
39
- function getCachedTransform(filePath, code, environment) {
53
+ function getCachedTransform(filePath, code, environment, userBabelConfigPath) {
40
54
  try {
41
55
  const cleanPath = filePath.startsWith("\0") ? filePath.slice(1) : filePath;
42
56
  const cacheDir = getCacheDir();
43
- const cacheKey = getCacheKey(cleanPath, environment);
57
+ const cacheKey = getCacheKey(cleanPath, environment, userBabelConfigPath);
44
58
  const cachePath = join(cacheDir, `${cacheKey}.json`);
45
59
  if (!existsSync(cachePath)) {
46
60
  stats.misses++;
@@ -67,11 +81,11 @@ function getCachedTransform(filePath, code, environment) {
67
81
  return null;
68
82
  }
69
83
  }
70
- function setCachedTransform(filePath, code, result, environment) {
84
+ function setCachedTransform(filePath, code, result, environment, userBabelConfigPath) {
71
85
  try {
72
86
  const cleanPath = filePath.startsWith("\0") ? filePath.slice(1) : filePath;
73
87
  const cacheDir = getCacheDir();
74
- const cacheKey = getCacheKey(cleanPath, environment);
88
+ const cacheKey = getCacheKey(cleanPath, environment, userBabelConfigPath);
75
89
  const cachePath = join(cacheDir, `${cacheKey}.json`);
76
90
  const mtime = statSync(cleanPath).mtimeMs;
77
91
  const hash = getContentHash(code);