@rn-org/react-native-thread 0.4.0 → 0.6.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.
@@ -5,6 +5,12 @@ import ReactNativeThread from "./NativeReactNativeThread.js";
5
5
  const _emitter = new NativeEventEmitter(ReactNativeThread);
6
6
  const RN_THREAD_MESSAGE_EVENT = 'RNThreadMessage';
7
7
  const _registry = new Map();
8
+ function _findByName(name) {
9
+ for (const info of _registry.values()) {
10
+ if (info.name === name) return info;
11
+ }
12
+ return undefined;
13
+ }
8
14
  function _register(id, name) {
9
15
  _registry.set(id, {
10
16
  id,
@@ -14,20 +20,52 @@ function _register(id, name) {
14
20
  function _unregister(id) {
15
21
  _registry.delete(id);
16
22
  }
23
+ function _isBytecode(src) {
24
+ return src.includes('[bytecode]') || /^\s*function\s*\(\)\s*\{\s*bytecode\s*\}/.test(src);
25
+ }
26
+ function _serializeValue(value) {
27
+ if (value === undefined) return 'undefined';
28
+ if (value === null) return 'null';
29
+ if (typeof value === 'function') {
30
+ const src = value.toString();
31
+ if (_isBytecode(src)) {
32
+ if (__DEV__) {
33
+ console.warn('[react-native-thread] A function param returned a Hermes bytecode placeholder. ' + "Add the Babel plugin: plugins: ['@rn-org/react-native-thread/babel-plugin']");
34
+ }
35
+ return 'undefined';
36
+ }
37
+ return src;
38
+ }
39
+ if (typeof value === 'string') return JSON.stringify(value);
40
+ if (typeof value === 'number' || typeof value === 'boolean') return String(value);
41
+ if (Array.isArray(value)) return '[' + value.map(_serializeValue).join(',') + ']';
42
+ if (typeof value === 'object') {
43
+ const rec = value;
44
+ if (typeof rec.__rnThreadFn === 'string') {
45
+ return rec.__rnThreadFn;
46
+ }
47
+ const entries = Object.entries(rec).map(([k, v]) => `${JSON.stringify(k)}:${_serializeValue(v)}`).join(',');
48
+ return '{' + entries + '}';
49
+ }
50
+ return String(value);
51
+ }
52
+ function _wrapWithErrorHandler(code) {
53
+ return `try { ${code} } catch(__e__) { resolveThreadMessage(JSON.stringify({ __rnThreadError: true, message: __e__.message || String(__e__), stack: __e__.stack || '' })); }`;
54
+ }
17
55
  function toCode(task, params) {
18
56
  if (typeof task === 'string') {
19
- const paramsJson = params !== undefined ? JSON.stringify(params) : 'undefined';
20
- return `var __params__ = ${paramsJson};\n${task}`;
57
+ const paramsCode = params !== undefined ? _serializeValue(params) : 'undefined';
58
+ return _wrapWithErrorHandler(`var __params__ = ${paramsCode};\n${task}`);
21
59
  }
22
60
  const src = task.toString();
23
- if (src.includes('[bytecode]') || /^\s*function\s*\(\)\s*\{\s*bytecode\s*\}/.test(src)) {
61
+ if (_isBytecode(src)) {
24
62
  if (__DEV__) {
25
63
  console.warn('[react-native-thread] fn.toString() returned a Hermes bytecode placeholder. ' + "Add the Babel plugin: plugins: ['@rn-org/react-native-thread/babel-plugin']");
26
64
  }
27
65
  return '/* bytecode: no-op */';
28
66
  }
29
- const argsStr = params !== undefined ? JSON.stringify(params) : '';
30
- return `(${src})(${argsStr})`;
67
+ const argsStr = params !== undefined ? _serializeValue(params) : '';
68
+ return _wrapWithErrorHandler(`(${src})(${argsStr})`);
31
69
  }
32
70
  const SHARED_THREAD_NAME = 'RNOrgThread';
33
71
  let _sharedThreadId = null;
@@ -42,6 +80,12 @@ export function runOnJS(task, params) {
42
80
  ReactNativeThread.runOnThread(getSharedThread(), toCode(task, params));
43
81
  }
44
82
  export function runOnNewJS(task, params, name) {
83
+ if (name != null) {
84
+ const existing = _findByName(name);
85
+ if (existing) {
86
+ return createThreadHandle(existing.id, existing.name);
87
+ }
88
+ }
45
89
  const id = ReactNativeThread.createThread();
46
90
  const resolvedName = name ?? `RNThread-${id}`;
47
91
  _register(id, resolvedName);
@@ -49,6 +93,12 @@ export function runOnNewJS(task, params, name) {
49
93
  return createThreadHandle(id, resolvedName);
50
94
  }
51
95
  export function createThread(name) {
96
+ if (name != null) {
97
+ const existing = _findByName(name);
98
+ if (existing) {
99
+ return createThreadHandle(existing.id, existing.name);
100
+ }
101
+ }
52
102
  const id = ReactNativeThread.createThread();
53
103
  const resolvedName = name ?? `RNThread-${id}`;
54
104
  _register(id, resolvedName);
@@ -104,6 +154,9 @@ export function onMessage(handler) {
104
154
  });
105
155
  });
106
156
  }
157
+ function _isThreadError(data) {
158
+ return typeof data === 'object' && data !== null && data.__rnThreadError === true;
159
+ }
107
160
  function createThreadHandle(id, name) {
108
161
  return {
109
162
  id,
@@ -114,18 +167,34 @@ function createThreadHandle(id, name) {
114
167
  onMessage: function (handler) {
115
168
  if (handler) {
116
169
  return onMessage((data, threadId) => {
117
- if (threadId === id) handler(data);
170
+ if (threadId === id && !_isThreadError(data)) handler(data);
118
171
  });
119
172
  }
120
- return new Promise(resolve => {
173
+ return new Promise((resolve, reject) => {
121
174
  const unsub = onMessage((data, threadId) => {
122
175
  if (threadId === id) {
123
176
  unsub();
124
- resolve(data);
177
+ if (_isThreadError(data)) {
178
+ const err = new Error(data.message);
179
+ err.stack = data.stack;
180
+ reject(err);
181
+ } else {
182
+ resolve(data);
183
+ }
125
184
  }
126
185
  });
127
186
  });
128
187
  },
188
+ onError(handler) {
189
+ return onMessage((data, threadId) => {
190
+ if (threadId === id && _isThreadError(data)) {
191
+ handler({
192
+ message: data.message,
193
+ stack: data.stack
194
+ });
195
+ }
196
+ });
197
+ },
129
198
  destroy() {
130
199
  destroyThread(id);
131
200
  }
@@ -1 +1 @@
1
- {"version":3,"names":["NativeEventEmitter","ReactNativeThread","_emitter","RN_THREAD_MESSAGE_EVENT","_registry","Map","_register","id","name","set","_unregister","delete","toCode","task","params","paramsJson","undefined","JSON","stringify","src","toString","includes","test","__DEV__","console","warn","argsStr","SHARED_THREAD_NAME","_sharedThreadId","getSharedThread","createThread","runOnJS","runOnThread","runOnNewJS","resolvedName","createThreadHandle","getThreads","Array","from","values","destroyThread","idOrName","targetId","has","info","onMessage","handler","sub","addListener","event","parsed","data","parse","threadId","remove","Promise","resolve","run","unsub","destroy"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["NativeEventEmitter","ReactNativeThread","_emitter","RN_THREAD_MESSAGE_EVENT","_registry","Map","_findByName","name","info","values","undefined","_register","id","set","_unregister","delete","_isBytecode","src","includes","test","_serializeValue","value","toString","__DEV__","console","warn","JSON","stringify","String","Array","isArray","map","join","rec","__rnThreadFn","entries","Object","k","v","_wrapWithErrorHandler","code","toCode","task","params","paramsCode","argsStr","SHARED_THREAD_NAME","_sharedThreadId","getSharedThread","createThread","runOnJS","runOnThread","runOnNewJS","existing","createThreadHandle","resolvedName","getThreads","from","destroyThread","idOrName","targetId","has","onMessage","handler","sub","addListener","event","parsed","data","parse","threadId","remove","Promise","resolve","_isThreadError","__rnThreadError","run","reject","unsub","err","Error","message","stack","onError","destroy"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
@@ -3,12 +3,17 @@ export type ThreadInfo = {
3
3
  readonly id: number;
4
4
  readonly name: string;
5
5
  };
6
+ export type ThreadError = {
7
+ message: string;
8
+ stack: string;
9
+ };
6
10
  export type ThreadHandle = {
7
11
  readonly id: number;
8
12
  readonly name: string;
9
13
  run(task: ThreadTask, params?: unknown): void;
10
14
  onMessage(handler: (data: unknown) => void): () => void;
11
15
  onMessage(): Promise<unknown>;
16
+ onError(handler: (error: ThreadError) => void): () => void;
12
17
  destroy(): void;
13
18
  };
14
19
  export declare function runOnJS(task: ThreadTask, params?: unknown): void;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAGA,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC,GAAG,MAAM,CAAC;AAEzD,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9C,SAAS,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACxD,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,OAAO,IAAI,IAAI,CAAC;CACjB,CAAC;AAmDF,wBAAgB,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAEhE;AAED,wBAAgB,UAAU,CACxB,IAAI,EAAE,UAAU,EAChB,MAAM,CAAC,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE,MAAM,GACZ,YAAY,CAMd;AAED,wBAAgB,YAAY,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,YAAY,CAKxD;AAED,wBAAgB,UAAU,IAAI,UAAU,EAAE,CAEzC;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA0B7D;AAED,wBAAgB,SAAS,CACvB,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GACjD,MAAM,IAAI,CAAC;AACd,wBAAgB,SAAS,IAAI,OAAO,CAAC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAGA,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC,GAAG,MAAM,CAAC;AAEzD,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9C,SAAS,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACxD,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC3D,OAAO,IAAI,IAAI,CAAC;CACjB,CAAC;AAoGF,wBAAgB,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAEhE;AAED,wBAAgB,UAAU,CACxB,IAAI,EAAE,UAAU,EAChB,MAAM,CAAC,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE,MAAM,GACZ,YAAY,CAYd;AAED,wBAAgB,YAAY,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,YAAY,CAWxD;AAED,wBAAgB,UAAU,IAAI,UAAU,EAAE,CAEzC;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA0B7D;AAED,wBAAgB,SAAS,CACvB,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GACjD,MAAM,IAAI,CAAC;AACd,wBAAgB,SAAS,IAAI,OAAO,CAAC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rn-org/react-native-thread",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Run JavaScript on real background threads in React Native — no Workers, no Worklets. Uses JavaScriptCore on iOS and Mozilla Rhino on Android. Built as a New Architecture TurboModule.",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -1,10 +1,299 @@
1
1
  'use strict';
2
2
 
3
+ const nodePath = require('path');
4
+ const fs = require('fs');
5
+
3
6
  const TOP_LEVEL_API = new Set(['runOnJS', 'runOnNewJS']);
4
7
 
5
8
  const METHOD_API = new Set(['run']);
6
9
 
7
- module.exports = function reactNativeThreadPlugin({ types: t }) {
10
+ const SKIP_KEYS = new Set([
11
+ 'type',
12
+ 'start',
13
+ 'end',
14
+ 'loc',
15
+ 'leadingComments',
16
+ 'trailingComments',
17
+ 'innerComments',
18
+ 'extra',
19
+ 'range',
20
+ ]);
21
+
22
+ module.exports = function reactNativeThreadPlugin({ types: t, ...babel }) {
23
+ const parser = (babel.parser && babel.parser) || require('@babel/parser');
24
+
25
+ // ── Cross-module import resolution ─────────────────────────────
26
+
27
+ const RESOLVE_EXTS = ['.ts', '.tsx', '.js', '.jsx', ''];
28
+
29
+ /**
30
+ * Given a relative import source (e.g. './Utility') and the
31
+ * filename of the importing file, try to resolve, read, parse,
32
+ * and extract the source of the named export `exportName`.
33
+ *
34
+ * Returns the raw function source string or null.
35
+ */
36
+ function resolveImportedFnSource(importSource, exportName, state) {
37
+ const currentFile = state.filename || state.file.opts.filename;
38
+ if (!currentFile || !importSource.startsWith('.')) return null;
39
+
40
+ const dir = nodePath.dirname(currentFile);
41
+ let resolvedFile = null;
42
+
43
+ for (const ext of RESOLVE_EXTS) {
44
+ const candidate = nodePath.resolve(dir, importSource + ext);
45
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
46
+ resolvedFile = candidate;
47
+ break;
48
+ }
49
+ }
50
+ if (!resolvedFile) {
51
+ // Try as directory with index file
52
+ for (const ext of RESOLVE_EXTS) {
53
+ const candidate = nodePath.resolve(dir, importSource, 'index' + ext);
54
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
55
+ resolvedFile = candidate;
56
+ break;
57
+ }
58
+ }
59
+ }
60
+ if (!resolvedFile) return null;
61
+
62
+ let source;
63
+ try {
64
+ source = fs.readFileSync(resolvedFile, 'utf8');
65
+ } catch {
66
+ return null;
67
+ }
68
+
69
+ let ast;
70
+ try {
71
+ ast = parser.parse(source, {
72
+ sourceType: 'module',
73
+ plugins: ['typescript', 'jsx'],
74
+ });
75
+ } catch {
76
+ return null;
77
+ }
78
+
79
+ // Walk top-level statements to find the exported function
80
+ for (const stmt of ast.program.body) {
81
+ // export function foo() { ... }
82
+ if (t.isExportNamedDeclaration(stmt) && stmt.declaration) {
83
+ if (
84
+ t.isFunctionDeclaration(stmt.declaration) &&
85
+ stmt.declaration.id &&
86
+ stmt.declaration.id.name === exportName
87
+ ) {
88
+ const raw = source.slice(
89
+ stmt.declaration.start,
90
+ stmt.declaration.end
91
+ );
92
+ return transpileForThread(raw, { filename: resolvedFile });
93
+ }
94
+ // export const foo = () => { ... }
95
+ if (t.isVariableDeclaration(stmt.declaration)) {
96
+ for (const decl of stmt.declaration.declarations) {
97
+ if (
98
+ t.isVariableDeclarator(decl) &&
99
+ t.isIdentifier(decl.id) &&
100
+ decl.id.name === exportName &&
101
+ decl.init &&
102
+ (t.isFunctionExpression(decl.init) ||
103
+ t.isArrowFunctionExpression(decl.init))
104
+ ) {
105
+ const raw = source.slice(decl.init.start, decl.init.end);
106
+ return transpileForThread(raw, { filename: resolvedFile });
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ return null;
114
+ }
115
+
116
+ // ── AST helpers ────────────────────────────────────────────────
117
+
118
+ /** Collect all identifiers that appear in reference position inside `node`. */
119
+ function collectReferencedIds(node) {
120
+ const ids = new Set();
121
+ function walk(n, skipId) {
122
+ if (!n || typeof n !== 'object') return;
123
+ if (Array.isArray(n)) {
124
+ n.forEach((el) => walk(el, false));
125
+ return;
126
+ }
127
+ if (!n.type) return;
128
+ if (t.isIdentifier(n) && !skipId) {
129
+ ids.add(n.name);
130
+ return;
131
+ }
132
+ if (t.isMemberExpression(n)) {
133
+ walk(n.object, false);
134
+ if (n.computed) walk(n.property, false);
135
+ return;
136
+ }
137
+ if (t.isObjectProperty(n)) {
138
+ if (n.computed) walk(n.key, false);
139
+ walk(n.value, false);
140
+ return;
141
+ }
142
+ if (
143
+ t.isFunctionExpression(n) ||
144
+ t.isArrowFunctionExpression(n) ||
145
+ t.isFunctionDeclaration(n)
146
+ ) {
147
+ return;
148
+ }
149
+ for (const key of Object.keys(n)) {
150
+ if (SKIP_KEYS.has(key)) continue;
151
+ walk(n[key], false);
152
+ }
153
+ }
154
+ walk(node, false);
155
+ return ids;
156
+ }
157
+
158
+ /** Collect names declared locally inside a function (params + body). */
159
+ function collectLocalNames(fnNode) {
160
+ const names = new Set();
161
+ if (fnNode.params) {
162
+ for (const p of fnNode.params) {
163
+ if (t.isIdentifier(p)) names.add(p.name);
164
+ if (t.isAssignmentPattern(p) && t.isIdentifier(p.left))
165
+ names.add(p.left.name);
166
+ if (t.isRestElement(p) && t.isIdentifier(p.argument))
167
+ names.add(p.argument.name);
168
+ }
169
+ }
170
+ if (fnNode.id && t.isIdentifier(fnNode.id)) names.add(fnNode.id.name);
171
+ function walk(n) {
172
+ if (!n || typeof n !== 'object') return;
173
+ if (Array.isArray(n)) {
174
+ n.forEach(walk);
175
+ return;
176
+ }
177
+ if (!n.type) return;
178
+ if (t.isVariableDeclarator(n) && t.isIdentifier(n.id))
179
+ names.add(n.id.name);
180
+ if (t.isFunctionDeclaration(n) && n.id) {
181
+ names.add(n.id.name);
182
+ return;
183
+ }
184
+ if (t.isFunctionExpression(n) || t.isArrowFunctionExpression(n)) return;
185
+ for (const key of Object.keys(n)) {
186
+ if (SKIP_KEYS.has(key)) continue;
187
+ walk(n[key]);
188
+ }
189
+ }
190
+ if (fnNode.body) walk(fnNode.body);
191
+ return names;
192
+ }
193
+
194
+ /**
195
+ * For each free variable in `fnNode`, try to resolve it to a
196
+ * compile-time value (literal or function) via scope bindings.
197
+ * Handles transitive closures: if a captured function itself has
198
+ * free variables, those are captured too.
199
+ *
200
+ * Returns an array of source strings like `"var b = 3;"` or
201
+ * `"var add = (x) => x + b;"`.
202
+ */
203
+ function getCaptureDecls(fnNode, scope, state) {
204
+ const captures = [];
205
+ const seen = new Set();
206
+
207
+ function capture(targetFnNode) {
208
+ const referenced = collectReferencedIds(targetFnNode.body);
209
+ const locals = collectLocalNames(targetFnNode);
210
+
211
+ for (const name of referenced) {
212
+ if (locals.has(name) || seen.has(name)) continue;
213
+ seen.add(name);
214
+
215
+ const binding = scope.getBinding(name);
216
+ if (!binding) continue;
217
+
218
+ const bNode = binding.path.node;
219
+
220
+ // const/let/var x = <literal>
221
+ if (
222
+ t.isVariableDeclarator(bNode) &&
223
+ bNode.init &&
224
+ t.isLiteral(bNode.init)
225
+ ) {
226
+ const initSrc = state.file.code.slice(
227
+ bNode.init.start,
228
+ bNode.init.end
229
+ );
230
+ captures.push('var ' + name + ' = ' + initSrc + ';');
231
+ continue;
232
+ }
233
+
234
+ // const/let/var x = <arrow / function expression>
235
+ if (
236
+ t.isVariableDeclarator(bNode) &&
237
+ bNode.init &&
238
+ (t.isFunctionExpression(bNode.init) ||
239
+ t.isArrowFunctionExpression(bNode.init))
240
+ ) {
241
+ // Recursively capture the inner function's free vars first
242
+ capture(bNode.init);
243
+ const initSrc = state.file.code.slice(
244
+ bNode.init.start,
245
+ bNode.init.end
246
+ );
247
+ captures.push(
248
+ 'var ' + name + ' = ' + transpileForThread(initSrc, state) + ';'
249
+ );
250
+ continue;
251
+ }
252
+
253
+ // function declaration
254
+ if (t.isFunctionDeclaration(bNode)) {
255
+ capture(bNode);
256
+ const fnSrc = state.file.code.slice(bNode.start, bNode.end);
257
+ captures.push(transpileForThread(fnSrc, state));
258
+ continue;
259
+ }
260
+ }
261
+ }
262
+
263
+ capture(fnNode);
264
+ return captures;
265
+ }
266
+
267
+ // ── Core transform helpers ─────────────────────────────────────
268
+
269
+ /**
270
+ * Transpile extracted source for the thread engine. Always downlevels
271
+ * to ES5 (for Rhino). Also strips TypeScript when the host file is TS.
272
+ */
273
+ function transpileForThread(code, state) {
274
+ try {
275
+ const babelCore = require('@babel/core');
276
+ const fn =
277
+ state.filename || (state.file.opts && state.file.opts.filename) || '';
278
+ const isTS = /\.tsx?$/.test(fn);
279
+ const plugins = isTS
280
+ ? [['@babel/plugin-transform-typescript', { isTSX: true }]]
281
+ : [];
282
+ const result = babelCore.transformSync(code, {
283
+ presets: [
284
+ ['@babel/preset-env', { targets: { ie: '11' }, modules: false }],
285
+ ],
286
+ plugins,
287
+ configFile: false,
288
+ babelrc: false,
289
+ filename: isTS ? 'thread.tsx' : 'thread.js',
290
+ });
291
+ return ((result && result.code) || code).replace(/;\s*$/, '');
292
+ } catch {
293
+ return code;
294
+ }
295
+ }
296
+
8
297
  function maybeTransformArg(callPath, state, argNode, index) {
9
298
  if (
10
299
  !t.isArrowFunctionExpression(argNode) &&
@@ -14,12 +303,126 @@ module.exports = function reactNativeThreadPlugin({ types: t }) {
14
303
  }
15
304
 
16
305
  const originalCode = state.file.code;
17
- const fnSource = originalCode.slice(argNode.start, argNode.end);
306
+ const fnSource = transpileForThread(
307
+ originalCode.slice(argNode.start, argNode.end),
308
+ state
309
+ );
18
310
  const iife = `(${fnSource})(__params__)`;
19
311
 
20
312
  callPath.node.arguments[index] = t.stringLiteral(iife);
21
313
  }
22
314
 
315
+ /**
316
+ * Resolve a node to its function source **and** any captured free-variable
317
+ * declarations. Returns the ready-to-eval source string, or null.
318
+ */
319
+ function extractFnSource(node, callPath, state) {
320
+ let fnNode = null;
321
+ let fnSource = null;
322
+ let isImported = false;
323
+
324
+ if (t.isFunctionExpression(node) || t.isArrowFunctionExpression(node)) {
325
+ fnNode = node;
326
+ fnSource = state.file.code.slice(node.start, node.end);
327
+ } else if (t.isIdentifier(node)) {
328
+ const binding = callPath.scope.getBinding(node.name);
329
+ if (!binding) return null;
330
+ const bNode = binding.path.node;
331
+ if (t.isFunctionDeclaration(bNode)) {
332
+ fnNode = bNode;
333
+ fnSource = state.file.code.slice(bNode.start, bNode.end);
334
+ } else if (t.isVariableDeclarator(bNode) && bNode.init) {
335
+ if (
336
+ t.isFunctionExpression(bNode.init) ||
337
+ t.isArrowFunctionExpression(bNode.init)
338
+ ) {
339
+ fnNode = bNode.init;
340
+ fnSource = state.file.code.slice(bNode.init.start, bNode.init.end);
341
+ }
342
+ } else if (
343
+ t.isImportSpecifier(bNode) ||
344
+ t.isImportDefaultSpecifier(bNode)
345
+ ) {
346
+ // Cross-module import: resolve the source file and extract the function
347
+ const importDecl = binding.path.parentPath.node;
348
+ const importSource = importDecl.source.value;
349
+ const exportName =
350
+ t.isImportSpecifier(bNode) && bNode.imported
351
+ ? t.isIdentifier(bNode.imported)
352
+ ? bNode.imported.name
353
+ : bNode.imported.value
354
+ : 'default';
355
+ const imported = resolveImportedFnSource(
356
+ importSource,
357
+ exportName,
358
+ state
359
+ );
360
+ if (imported) {
361
+ fnSource = imported;
362
+ isImported = true;
363
+ }
364
+ }
365
+ }
366
+
367
+ if (!fnSource) return null;
368
+
369
+ // For imported functions we already have the full source (no local AST node
370
+ // to walk for captures — the function must be self-contained or only depend
371
+ // on its own module's scope which was already inlined).
372
+ if (isImported) return fnSource;
373
+
374
+ if (!fnNode) return null;
375
+
376
+ fnSource = transpileForThread(fnSource, state);
377
+
378
+ const captures = getCaptureDecls(fnNode, callPath.scope, state);
379
+ if (captures.length > 0) {
380
+ fnSource =
381
+ '(function(){' + captures.join('\n') + '\nreturn ' + fnSource + ';})()';
382
+ }
383
+
384
+ return fnSource;
385
+ }
386
+
387
+ function tagFnValue(source) {
388
+ return t.objectExpression([
389
+ t.objectProperty(t.identifier('__rnThreadFn'), t.stringLiteral(source)),
390
+ ]);
391
+ }
392
+
393
+ function maybeTransformParams(callPath, state, paramsNode) {
394
+ if (!t.isObjectExpression(paramsNode)) return;
395
+ for (const prop of paramsNode.properties) {
396
+ if (!t.isObjectProperty(prop)) continue;
397
+ const fnSrc = extractFnSource(prop.value, callPath, state);
398
+ if (fnSrc) {
399
+ prop.value = tagFnValue(fnSrc);
400
+ prop.shorthand = false;
401
+ } else if (t.isObjectExpression(prop.value)) {
402
+ maybeTransformParams(callPath, state, prop.value);
403
+ } else if (t.isArrayExpression(prop.value)) {
404
+ maybeTransformArrayParams(callPath, state, prop.value);
405
+ }
406
+ }
407
+ }
408
+
409
+ function maybeTransformArrayParams(callPath, state, arrNode) {
410
+ for (let i = 0; i < arrNode.elements.length; i++) {
411
+ const el = arrNode.elements[i];
412
+ if (!el) continue;
413
+ const fnSrc = extractFnSource(el, callPath, state);
414
+ if (fnSrc) {
415
+ arrNode.elements[i] = tagFnValue(fnSrc);
416
+ } else if (t.isObjectExpression(el)) {
417
+ maybeTransformParams(callPath, state, el);
418
+ } else if (t.isArrayExpression(el)) {
419
+ maybeTransformArrayParams(callPath, state, el);
420
+ }
421
+ }
422
+ }
423
+
424
+ // ── Visitor ────────────────────────────────────────────────────
425
+
23
426
  return {
24
427
  name: 'react-native-thread',
25
428
  visitor: {
@@ -30,6 +433,9 @@ module.exports = function reactNativeThreadPlugin({ types: t }) {
30
433
  if (args.length >= 1) {
31
434
  maybeTransformArg(path, state, args[0], 0);
32
435
  }
436
+ if (args.length >= 2) {
437
+ maybeTransformParams(path, state, args[1]);
438
+ }
33
439
  return;
34
440
  }
35
441
 
@@ -42,6 +448,9 @@ module.exports = function reactNativeThreadPlugin({ types: t }) {
42
448
  if (args.length >= 1) {
43
449
  maybeTransformArg(path, state, args[0], 0);
44
450
  }
451
+ if (args.length >= 2) {
452
+ maybeTransformParams(path, state, args[1]);
453
+ }
45
454
  }
46
455
  },
47
456
  },