@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.
- package/README.md +143 -22
- package/lib/module/babel-plugin.js +328 -2
- package/lib/module/babel-plugin.js.map +1 -1
- package/lib/module/index.js +77 -8
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/index.d.ts +5 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/babel-plugin.js +411 -2
- package/src/index.tsx +102 -12
package/lib/module/index.js
CHANGED
|
@@ -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
|
|
20
|
-
return `var __params__ = ${
|
|
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 (
|
|
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 ?
|
|
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
|
-
|
|
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
|
}
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["NativeEventEmitter","ReactNativeThread","_emitter","RN_THREAD_MESSAGE_EVENT","_registry","Map","
|
|
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;
|
|
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.
|
|
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",
|
package/src/babel-plugin.js
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
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
|
},
|