@rn-org/react-native-thread 0.2.0 → 0.3.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/android/src/main/java/com/rnorg/reactnativethread/ReactNativeThreadModule.kt +2 -7
- package/ios/ReactNativeThread.mm +2 -27
- package/lib/module/babel-plugin.js +0 -31
- package/lib/module/babel-plugin.js.map +1 -1
- package/lib/module/index.js +0 -32
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/index.d.ts +0 -25
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/babel-plugin.js +0 -28
- package/src/index.tsx +0 -31
|
@@ -27,11 +27,8 @@ class ReactNativeThreadModule(reactContext: ReactApplicationContext) :
|
|
|
27
27
|
private val threads = ConcurrentHashMap<Long, ThreadEntry>()
|
|
28
28
|
private val nextId = AtomicLong(1L)
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
override fun
|
|
32
|
-
override fun removeListeners(count: Double) { /* no-op */ }
|
|
33
|
-
|
|
34
|
-
// ─── Thread lifecycle ───────────────────────────────────────────────────────
|
|
30
|
+
override fun addListener(eventName: String?) {}
|
|
31
|
+
override fun removeListeners(count: Double) {}
|
|
35
32
|
|
|
36
33
|
override fun createThread(): Double {
|
|
37
34
|
val id = nextId.getAndIncrement()
|
|
@@ -84,8 +81,6 @@ class ReactNativeThreadModule(reactContext: ReactApplicationContext) :
|
|
|
84
81
|
entry.executor.shutdown()
|
|
85
82
|
}
|
|
86
83
|
|
|
87
|
-
// ─── Scope injection ────────────────────────────────────────────────────────
|
|
88
|
-
|
|
89
84
|
private fun injectPostMessage(scope: Scriptable, threadId: Long) {
|
|
90
85
|
val ctx = reactApplicationContext
|
|
91
86
|
ScriptableObject.putProperty(
|
package/ios/ReactNativeThread.mm
CHANGED
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
#import "ReactNativeThread.h"
|
|
2
2
|
#import <JavaScriptCore/JavaScriptCore.h>
|
|
3
3
|
|
|
4
|
-
// One entry per thread: a JSContext running on a dedicated serial queue.
|
|
5
|
-
// Each thread owns its own JSVirtualMachine so execution is fully concurrent
|
|
6
|
-
// and never contends with the main thread's VM lock.
|
|
7
4
|
@interface RNThread : NSObject
|
|
8
5
|
@property (nonatomic, strong) JSVirtualMachine *vm;
|
|
9
6
|
@property (nonatomic, strong) JSContext *context;
|
|
10
7
|
@property (nonatomic, strong) dispatch_queue_t queue;
|
|
11
|
-
// Signals when VM+context initialisation is complete so that runOnThread
|
|
12
|
-
// can be enqueued immediately without blocking the JS thread during init.
|
|
13
8
|
@property (nonatomic) dispatch_semaphore_t ready;
|
|
14
9
|
@end
|
|
15
10
|
|
|
16
11
|
@implementation RNThread
|
|
17
12
|
@end
|
|
18
13
|
|
|
19
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
20
|
-
|
|
21
14
|
@implementation ReactNativeThread {
|
|
22
15
|
NSMutableDictionary<NSNumber *, RNThread *> *_threads;
|
|
23
16
|
NSLock *_lock;
|
|
@@ -33,7 +26,6 @@
|
|
|
33
26
|
return self;
|
|
34
27
|
}
|
|
35
28
|
|
|
36
|
-
// Required by RCTEventEmitter
|
|
37
29
|
- (NSArray<NSString *> *)supportedEvents {
|
|
38
30
|
return @[@"RNThreadMessage"];
|
|
39
31
|
}
|
|
@@ -41,16 +33,12 @@
|
|
|
41
33
|
- (void)addListener:(NSString *)eventName { [super addListener:eventName]; }
|
|
42
34
|
- (void)removeListeners:(double)count { [super removeListeners:count]; }
|
|
43
35
|
|
|
44
|
-
// ─── createThread ─────────────────────────────────────────────────────────────
|
|
45
|
-
|
|
46
36
|
- (NSNumber *)createThread {
|
|
47
37
|
RNThread *t = [RNThread new];
|
|
48
38
|
|
|
49
39
|
NSString *label = [NSString stringWithFormat:@"com.rnorg.reactnativethread.%lu",
|
|
50
40
|
(unsigned long)(NSUInteger)_nextId];
|
|
51
41
|
|
|
52
|
-
// QOS_CLASS_UTILITY keeps background JS work below the main/UI thread in
|
|
53
|
-
// the scheduler, preventing CPU starvation when a tight loop is running.
|
|
54
42
|
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(
|
|
55
43
|
DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0);
|
|
56
44
|
t.queue = dispatch_queue_create(label.UTF8String, attr);
|
|
@@ -58,9 +46,6 @@
|
|
|
58
46
|
|
|
59
47
|
NSUInteger tidForLog = (NSUInteger)_nextId;
|
|
60
48
|
|
|
61
|
-
// Async init: the JS thread is never blocked waiting for VM creation.
|
|
62
|
-
// runOnThread enqueues a semaphore-wait block first so it executes only
|
|
63
|
-
// after init is complete, all on the background queue.
|
|
64
49
|
dispatch_async(t.queue, ^{
|
|
65
50
|
t.vm = [[JSVirtualMachine alloc] init];
|
|
66
51
|
t.context = [[JSContext alloc] initWithVirtualMachine:t.vm];
|
|
@@ -68,7 +53,6 @@
|
|
|
68
53
|
NSLog(@"[RNThread-%lu] JS exception: %@", (unsigned long)tidForLog, exception);
|
|
69
54
|
};
|
|
70
55
|
|
|
71
|
-
// ── console ───────────────────────────────────────────────────────────
|
|
72
56
|
NSString *tag = [NSString stringWithFormat:@"RNThread-%lu", (unsigned long)tidForLog];
|
|
73
57
|
NSMutableDictionary *console = [NSMutableDictionary new];
|
|
74
58
|
console[@"log"] = ^{
|
|
@@ -93,10 +77,9 @@
|
|
|
93
77
|
};
|
|
94
78
|
t.context[@"console"] = console;
|
|
95
79
|
|
|
96
|
-
// ── postMessage ───────────────────────────────────────────────────────
|
|
97
80
|
NSUInteger capturedTid = tidForLog;
|
|
98
81
|
__weak ReactNativeThread *weakSelf = self;
|
|
99
|
-
t.context[@"postMessage"] = ^(JSValue *data) {
|
|
82
|
+
t.context[@"postMessage"] = ^(JSValue *data) {
|
|
100
83
|
JSValue *jsonStr = [data.context[@"JSON"] invokeMethod:@"stringify"
|
|
101
84
|
withArguments:@[data]];
|
|
102
85
|
NSString *serialised = [jsonStr toString];
|
|
@@ -114,7 +97,6 @@
|
|
|
114
97
|
}];
|
|
115
98
|
};
|
|
116
99
|
|
|
117
|
-
// Signal that the thread is ready to accept work.
|
|
118
100
|
dispatch_semaphore_signal(t.ready);
|
|
119
101
|
});
|
|
120
102
|
|
|
@@ -127,8 +109,6 @@
|
|
|
127
109
|
return @(tid);
|
|
128
110
|
}
|
|
129
111
|
|
|
130
|
-
// ─── runOnThread ──────────────────────────────────────────────────────────────
|
|
131
|
-
|
|
132
112
|
- (void)runOnThread:(double)threadId code:(NSString *)code {
|
|
133
113
|
[_lock lock];
|
|
134
114
|
RNThread *t = _threads[@(threadId)];
|
|
@@ -142,15 +122,12 @@
|
|
|
142
122
|
NSString *codeCopy = [code copy];
|
|
143
123
|
dispatch_semaphore_t ready = t.ready;
|
|
144
124
|
dispatch_async(t.queue, ^{
|
|
145
|
-
// Wait for init to finish (no-op after first runOnThread call).
|
|
146
125
|
dispatch_semaphore_wait(ready, DISPATCH_TIME_FOREVER);
|
|
147
|
-
dispatch_semaphore_signal(ready);
|
|
126
|
+
dispatch_semaphore_signal(ready);
|
|
148
127
|
[t.context evaluateScript:codeCopy];
|
|
149
128
|
});
|
|
150
129
|
}
|
|
151
130
|
|
|
152
|
-
// ─── destroyThread ────────────────────────────────────────────────────────────
|
|
153
|
-
|
|
154
131
|
- (void)destroyThread:(double)threadId {
|
|
155
132
|
[_lock lock];
|
|
156
133
|
RNThread *t = _threads[@(threadId)];
|
|
@@ -164,8 +141,6 @@
|
|
|
164
141
|
});
|
|
165
142
|
}
|
|
166
143
|
|
|
167
|
-
// ─── TurboModule boilerplate ──────────────────────────────────────────────────
|
|
168
|
-
|
|
169
144
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
170
145
|
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
171
146
|
{
|
|
@@ -1,37 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Babel plugin for @rn-org/react-native-thread
|
|
3
|
-
*
|
|
4
|
-
* Hermes compiles JS to bytecode at build time, so fn.toString() at runtime
|
|
5
|
-
* returns a placeholder ("bytecode") rather than the original source.
|
|
6
|
-
*
|
|
7
|
-
* This plugin runs at compile time and replaces arrow-function / function-
|
|
8
|
-
* expression arguments passed to the thread API with string literals that
|
|
9
|
-
* contain the source wrapped as an IIFE. The background thread engine
|
|
10
|
-
* (JSC on iOS, Rhino on Android) can then simply eval() the string.
|
|
11
|
-
*
|
|
12
|
-
* Transforms:
|
|
13
|
-
* runOnJS((args) => { ... }) → runOnJS("((args) => { ... })(__params__)")
|
|
14
|
-
* runOnNewJS((args) => { ... }) → runOnNewJS("((args) => { ... })(__params__)")
|
|
15
|
-
* thread.run((args) => { ... }) → thread.run("((args) => { ... })(__params__)")
|
|
16
|
-
*
|
|
17
|
-
* Add to your app's babel.config.js:
|
|
18
|
-
* plugins: ['@rn-org/react-native-thread/babel-plugin']
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
1
|
'use strict';
|
|
22
2
|
|
|
23
|
-
// Top-level function names that take a fn-or-code as their first argument.
|
|
24
3
|
const TOP_LEVEL_API = new Set(['runOnJS', 'runOnNewJS']);
|
|
25
|
-
|
|
26
|
-
// Method names on a ThreadHandle that take a fn-or-code as their first arg.
|
|
27
4
|
const METHOD_API = new Set(['run']);
|
|
28
5
|
module.exports = function reactNativeThreadPlugin({
|
|
29
6
|
types: t
|
|
30
7
|
}) {
|
|
31
|
-
/**
|
|
32
|
-
* If `argNode` is an arrow function or function expression, replace the
|
|
33
|
-
* argument at `index` with a string literal `"(<source>)()"`.
|
|
34
|
-
*/
|
|
35
8
|
function maybeTransformArg(callPath, state, argNode, index) {
|
|
36
9
|
if (!t.isArrowFunctionExpression(argNode) && !t.isFunctionExpression(argNode)) {
|
|
37
10
|
return;
|
|
@@ -49,16 +22,12 @@ module.exports = function reactNativeThreadPlugin({
|
|
|
49
22
|
callee,
|
|
50
23
|
arguments: args
|
|
51
24
|
} = path.node;
|
|
52
|
-
|
|
53
|
-
// runOnJS(() => {}) / runOnNewJS(() => {})
|
|
54
25
|
if (t.isIdentifier(callee) && TOP_LEVEL_API.has(callee.name)) {
|
|
55
26
|
if (args.length >= 1) {
|
|
56
27
|
maybeTransformArg(path, state, args[0], 0);
|
|
57
28
|
}
|
|
58
29
|
return;
|
|
59
30
|
}
|
|
60
|
-
|
|
61
|
-
// someThread.run(() => {})
|
|
62
31
|
if (t.isMemberExpression(callee) && !callee.computed && t.isIdentifier(callee.property) && METHOD_API.has(callee.property.name)) {
|
|
63
32
|
if (args.length >= 1) {
|
|
64
33
|
maybeTransformArg(path, state, args[0], 0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["TOP_LEVEL_API","Set","METHOD_API","module","exports","reactNativeThreadPlugin","types","t","maybeTransformArg","callPath","state","argNode","index","isArrowFunctionExpression","isFunctionExpression","originalCode","file","code","fnSource","slice","start","end","iife","node","arguments","stringLiteral","name","visitor","CallExpression","path","callee","args","isIdentifier","has","length","isMemberExpression","computed","property"],"sourceRoot":"../../src","sources":["babel-plugin.js"],"mappings":"AAAA
|
|
1
|
+
{"version":3,"names":["TOP_LEVEL_API","Set","METHOD_API","module","exports","reactNativeThreadPlugin","types","t","maybeTransformArg","callPath","state","argNode","index","isArrowFunctionExpression","isFunctionExpression","originalCode","file","code","fnSource","slice","start","end","iife","node","arguments","stringLiteral","name","visitor","CallExpression","path","callee","args","isIdentifier","has","length","isMemberExpression","computed","property"],"sourceRoot":"../../src","sources":["babel-plugin.js"],"mappings":"AAAA,YAAY;;AAEZ,MAAMA,aAAa,GAAG,IAAIC,GAAG,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;AAExD,MAAMC,UAAU,GAAG,IAAID,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;AAEnCE,MAAM,CAACC,OAAO,GAAG,SAASC,uBAAuBA,CAAC;EAAEC,KAAK,EAAEC;AAAE,CAAC,EAAE;EAC9D,SAASC,iBAAiBA,CAACC,QAAQ,EAAEC,KAAK,EAAEC,OAAO,EAAEC,KAAK,EAAE;IAC1D,IACE,CAACL,CAAC,CAACM,yBAAyB,CAACF,OAAO,CAAC,IACrC,CAACJ,CAAC,CAACO,oBAAoB,CAACH,OAAO,CAAC,EAChC;MACA;IACF;IAEA,MAAMI,YAAY,GAAGL,KAAK,CAACM,IAAI,CAACC,IAAI;IACpC,MAAMC,QAAQ,GAAGH,YAAY,CAACI,KAAK,CAACR,OAAO,CAACS,KAAK,EAAET,OAAO,CAACU,GAAG,CAAC;IAC/D,MAAMC,IAAI,GAAG,IAAIJ,QAAQ,eAAe;IAExCT,QAAQ,CAACc,IAAI,CAACC,SAAS,CAACZ,KAAK,CAAC,GAAGL,CAAC,CAACkB,aAAa,CAACH,IAAI,CAAC;EACxD;EAEA,OAAO;IACLI,IAAI,EAAE,qBAAqB;IAC3BC,OAAO,EAAE;MACPC,cAAcA,CAACC,IAAI,EAAEnB,KAAK,EAAE;QAC1B,MAAM;UAAEoB,MAAM;UAAEN,SAAS,EAAEO;QAAK,CAAC,GAAGF,IAAI,CAACN,IAAI;QAE7C,IAAIhB,CAAC,CAACyB,YAAY,CAACF,MAAM,CAAC,IAAI9B,aAAa,CAACiC,GAAG,CAACH,MAAM,CAACJ,IAAI,CAAC,EAAE;UAC5D,IAAIK,IAAI,CAACG,MAAM,IAAI,CAAC,EAAE;YACpB1B,iBAAiB,CAACqB,IAAI,EAAEnB,KAAK,EAAEqB,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;UAC5C;UACA;QACF;QAEA,IACExB,CAAC,CAAC4B,kBAAkB,CAACL,MAAM,CAAC,IAC5B,CAACA,MAAM,CAACM,QAAQ,IAChB7B,CAAC,CAACyB,YAAY,CAACF,MAAM,CAACO,QAAQ,CAAC,IAC/BnC,UAAU,CAAC+B,GAAG,CAACH,MAAM,CAACO,QAAQ,CAACX,IAAI,CAAC,EACpC;UACA,IAAIK,IAAI,CAACG,MAAM,IAAI,CAAC,EAAE;YACpB1B,iBAAiB,CAACqB,IAAI,EAAEnB,KAAK,EAAEqB,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;UAC5C;QACF;MACF;IACF;EACF,CAAC;AACH,CAAC","ignoreList":[]}
|
package/lib/module/index.js
CHANGED
|
@@ -2,13 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { NativeEventEmitter } from 'react-native';
|
|
4
4
|
import ReactNativeThread from "./NativeReactNativeThread.js";
|
|
5
|
-
|
|
6
|
-
/** Snapshot of a single live thread. */
|
|
7
|
-
|
|
8
5
|
const _emitter = new NativeEventEmitter(ReactNativeThread);
|
|
9
6
|
const RN_THREAD_MESSAGE_EVENT = 'RNThreadMessage';
|
|
10
|
-
|
|
11
|
-
// ─── Thread registry (JS-layer; names are a JS concept) ──────────────────────
|
|
12
7
|
const _registry = new Map();
|
|
13
8
|
function _register(id, name) {
|
|
14
9
|
_registry.set(id, {
|
|
@@ -21,9 +16,6 @@ function _unregister(id) {
|
|
|
21
16
|
}
|
|
22
17
|
function toCode(task, params) {
|
|
23
18
|
if (typeof task === 'string') {
|
|
24
|
-
// Always declare __params__ so Babel-transformed strings can pass it as
|
|
25
|
-
// an argument to the function, and raw code strings that reference
|
|
26
|
-
// __params__ directly continue to work.
|
|
27
19
|
const paramsJson = params !== undefined ? JSON.stringify(params) : 'undefined';
|
|
28
20
|
return `var __params__ = ${paramsJson};\n${task}`;
|
|
29
21
|
}
|
|
@@ -37,9 +29,6 @@ function toCode(task, params) {
|
|
|
37
29
|
const argsStr = params !== undefined ? JSON.stringify(params) : '';
|
|
38
30
|
return `(${src})(${argsStr})`;
|
|
39
31
|
}
|
|
40
|
-
|
|
41
|
-
// ─── Shared thread (runOnJS) ──────────────────────────────────────────────────
|
|
42
|
-
|
|
43
32
|
const SHARED_THREAD_NAME = 'RNOrgThread';
|
|
44
33
|
let _sharedThreadId = null;
|
|
45
34
|
function getSharedThread() {
|
|
@@ -52,11 +41,6 @@ function getSharedThread() {
|
|
|
52
41
|
export function runOnJS(task, params) {
|
|
53
42
|
ReactNativeThread.runOnThread(getSharedThread(), toCode(task, params));
|
|
54
43
|
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Runs a task on a brand-new isolated thread and returns a handle to it.
|
|
58
|
-
* @param name - Optional display name (default: `RNThread-<id>`).
|
|
59
|
-
*/
|
|
60
44
|
export function runOnNewJS(task, params, name) {
|
|
61
45
|
const id = ReactNativeThread.createThread();
|
|
62
46
|
const resolvedName = name ?? `RNThread-${id}`;
|
|
@@ -64,31 +48,15 @@ export function runOnNewJS(task, params, name) {
|
|
|
64
48
|
ReactNativeThread.runOnThread(id, toCode(task, params));
|
|
65
49
|
return createThreadHandle(id, resolvedName);
|
|
66
50
|
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Creates a persistent thread. Optionally give it a name.
|
|
70
|
-
* @param name - Display name (default: `RNThread-<id>`).
|
|
71
|
-
*/
|
|
72
51
|
export function createThread(name) {
|
|
73
52
|
const id = ReactNativeThread.createThread();
|
|
74
53
|
const resolvedName = name ?? `RNThread-${id}`;
|
|
75
54
|
_register(id, resolvedName);
|
|
76
55
|
return createThreadHandle(id, resolvedName);
|
|
77
56
|
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Returns info about every live thread currently managed by this library,
|
|
81
|
-
* including the shared `runOnJS` thread once it has been started.
|
|
82
|
-
*/
|
|
83
57
|
export function getThreads() {
|
|
84
58
|
return Array.from(_registry.values());
|
|
85
59
|
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Destroy a thread by its numeric ID **or** by its name.
|
|
89
|
-
* When a name is given, the first matching thread is destroyed.
|
|
90
|
-
* This is also what `ThreadHandle.destroy()` calls internally.
|
|
91
|
-
*/
|
|
92
60
|
export function destroyThread(idOrName) {
|
|
93
61
|
let targetId;
|
|
94
62
|
if (typeof idOrName === 'number') {
|
package/lib/module/index.js.map
CHANGED
|
@@ -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":";;AAAA,SAASA,kBAAkB,QAAQ,cAAc;AACjD,OAAOC,iBAAiB,MAAM,8BAA2B
|
|
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,5 +1,4 @@
|
|
|
1
1
|
export type ThreadTask = ((args?: any) => void) | string;
|
|
2
|
-
/** Snapshot of a single live thread. */
|
|
3
2
|
export type ThreadInfo = {
|
|
4
3
|
readonly id: number;
|
|
5
4
|
readonly name: string;
|
|
@@ -7,39 +6,15 @@ export type ThreadInfo = {
|
|
|
7
6
|
export type ThreadHandle = {
|
|
8
7
|
readonly id: number;
|
|
9
8
|
readonly name: string;
|
|
10
|
-
/**
|
|
11
|
-
* Run a task on this thread.
|
|
12
|
-
* @param params - Optional JSON-serialisable value passed as the first
|
|
13
|
-
* argument to the thread function.
|
|
14
|
-
*/
|
|
15
9
|
run(task: ThreadTask, params?: unknown): void;
|
|
16
|
-
/** Subscribe to messages from this thread. Returns an unsubscribe function. */
|
|
17
10
|
onMessage(handler: (data: unknown) => void): () => void;
|
|
18
|
-
/** Returns a Promise that resolves with the next message from this thread. */
|
|
19
11
|
onMessage(): Promise<unknown>;
|
|
20
12
|
destroy(): void;
|
|
21
13
|
};
|
|
22
14
|
export declare function runOnJS(task: ThreadTask, params?: unknown): void;
|
|
23
|
-
/**
|
|
24
|
-
* Runs a task on a brand-new isolated thread and returns a handle to it.
|
|
25
|
-
* @param name - Optional display name (default: `RNThread-<id>`).
|
|
26
|
-
*/
|
|
27
15
|
export declare function runOnNewJS(task: ThreadTask, params?: unknown, name?: string): ThreadHandle;
|
|
28
|
-
/**
|
|
29
|
-
* Creates a persistent thread. Optionally give it a name.
|
|
30
|
-
* @param name - Display name (default: `RNThread-<id>`).
|
|
31
|
-
*/
|
|
32
16
|
export declare function createThread(name?: string): ThreadHandle;
|
|
33
|
-
/**
|
|
34
|
-
* Returns info about every live thread currently managed by this library,
|
|
35
|
-
* including the shared `runOnJS` thread once it has been started.
|
|
36
|
-
*/
|
|
37
17
|
export declare function getThreads(): ThreadInfo[];
|
|
38
|
-
/**
|
|
39
|
-
* Destroy a thread by its numeric ID **or** by its name.
|
|
40
|
-
* When a name is given, the first matching thread is destroyed.
|
|
41
|
-
* This is also what `ThreadHandle.destroy()` calls internally.
|
|
42
|
-
*/
|
|
43
18
|
export declare function destroyThread(idOrName: number | string): void;
|
|
44
19
|
export declare function onMessage(handler: (data: unknown, threadId: number) => void): () => void;
|
|
45
20
|
export declare function onMessage(): Promise<{
|
|
@@ -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,
|
|
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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rn-org/react-native-thread",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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,36 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Babel plugin for @rn-org/react-native-thread
|
|
3
|
-
*
|
|
4
|
-
* Hermes compiles JS to bytecode at build time, so fn.toString() at runtime
|
|
5
|
-
* returns a placeholder ("bytecode") rather than the original source.
|
|
6
|
-
*
|
|
7
|
-
* This plugin runs at compile time and replaces arrow-function / function-
|
|
8
|
-
* expression arguments passed to the thread API with string literals that
|
|
9
|
-
* contain the source wrapped as an IIFE. The background thread engine
|
|
10
|
-
* (JSC on iOS, Rhino on Android) can then simply eval() the string.
|
|
11
|
-
*
|
|
12
|
-
* Transforms:
|
|
13
|
-
* runOnJS((args) => { ... }) → runOnJS("((args) => { ... })(__params__)")
|
|
14
|
-
* runOnNewJS((args) => { ... }) → runOnNewJS("((args) => { ... })(__params__)")
|
|
15
|
-
* thread.run((args) => { ... }) → thread.run("((args) => { ... })(__params__)")
|
|
16
|
-
*
|
|
17
|
-
* Add to your app's babel.config.js:
|
|
18
|
-
* plugins: ['@rn-org/react-native-thread/babel-plugin']
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
1
|
'use strict';
|
|
22
2
|
|
|
23
|
-
// Top-level function names that take a fn-or-code as their first argument.
|
|
24
3
|
const TOP_LEVEL_API = new Set(['runOnJS', 'runOnNewJS']);
|
|
25
4
|
|
|
26
|
-
// Method names on a ThreadHandle that take a fn-or-code as their first arg.
|
|
27
5
|
const METHOD_API = new Set(['run']);
|
|
28
6
|
|
|
29
7
|
module.exports = function reactNativeThreadPlugin({ types: t }) {
|
|
30
|
-
/**
|
|
31
|
-
* If `argNode` is an arrow function or function expression, replace the
|
|
32
|
-
* argument at `index` with a string literal `"(<source>)()"`.
|
|
33
|
-
*/
|
|
34
8
|
function maybeTransformArg(callPath, state, argNode, index) {
|
|
35
9
|
if (
|
|
36
10
|
!t.isArrowFunctionExpression(argNode) &&
|
|
@@ -52,7 +26,6 @@ module.exports = function reactNativeThreadPlugin({ types: t }) {
|
|
|
52
26
|
CallExpression(path, state) {
|
|
53
27
|
const { callee, arguments: args } = path.node;
|
|
54
28
|
|
|
55
|
-
// runOnJS(() => {}) / runOnNewJS(() => {})
|
|
56
29
|
if (t.isIdentifier(callee) && TOP_LEVEL_API.has(callee.name)) {
|
|
57
30
|
if (args.length >= 1) {
|
|
58
31
|
maybeTransformArg(path, state, args[0], 0);
|
|
@@ -60,7 +33,6 @@ module.exports = function reactNativeThreadPlugin({ types: t }) {
|
|
|
60
33
|
return;
|
|
61
34
|
}
|
|
62
35
|
|
|
63
|
-
// someThread.run(() => {})
|
|
64
36
|
if (
|
|
65
37
|
t.isMemberExpression(callee) &&
|
|
66
38
|
!callee.computed &&
|
package/src/index.tsx
CHANGED
|
@@ -3,7 +3,6 @@ import ReactNativeThread from './NativeReactNativeThread';
|
|
|
3
3
|
|
|
4
4
|
export type ThreadTask = ((args?: any) => void) | string;
|
|
5
5
|
|
|
6
|
-
/** Snapshot of a single live thread. */
|
|
7
6
|
export type ThreadInfo = {
|
|
8
7
|
readonly id: number;
|
|
9
8
|
readonly name: string;
|
|
@@ -12,15 +11,8 @@ export type ThreadInfo = {
|
|
|
12
11
|
export type ThreadHandle = {
|
|
13
12
|
readonly id: number;
|
|
14
13
|
readonly name: string;
|
|
15
|
-
/**
|
|
16
|
-
* Run a task on this thread.
|
|
17
|
-
* @param params - Optional JSON-serialisable value passed as the first
|
|
18
|
-
* argument to the thread function.
|
|
19
|
-
*/
|
|
20
14
|
run(task: ThreadTask, params?: unknown): void;
|
|
21
|
-
/** Subscribe to messages from this thread. Returns an unsubscribe function. */
|
|
22
15
|
onMessage(handler: (data: unknown) => void): () => void;
|
|
23
|
-
/** Returns a Promise that resolves with the next message from this thread. */
|
|
24
16
|
onMessage(): Promise<unknown>;
|
|
25
17
|
destroy(): void;
|
|
26
18
|
};
|
|
@@ -28,7 +20,6 @@ export type ThreadHandle = {
|
|
|
28
20
|
const _emitter = new NativeEventEmitter(ReactNativeThread);
|
|
29
21
|
const RN_THREAD_MESSAGE_EVENT = 'RNThreadMessage';
|
|
30
22
|
|
|
31
|
-
// ─── Thread registry (JS-layer; names are a JS concept) ──────────────────────
|
|
32
23
|
const _registry = new Map<number, ThreadInfo>();
|
|
33
24
|
|
|
34
25
|
function _register(id: number, name: string): void {
|
|
@@ -41,9 +32,6 @@ function _unregister(id: number): void {
|
|
|
41
32
|
|
|
42
33
|
function toCode(task: ThreadTask, params?: unknown): string {
|
|
43
34
|
if (typeof task === 'string') {
|
|
44
|
-
// Always declare __params__ so Babel-transformed strings can pass it as
|
|
45
|
-
// an argument to the function, and raw code strings that reference
|
|
46
|
-
// __params__ directly continue to work.
|
|
47
35
|
const paramsJson =
|
|
48
36
|
params !== undefined ? JSON.stringify(params) : 'undefined';
|
|
49
37
|
return `var __params__ = ${paramsJson};\n${task}`;
|
|
@@ -67,8 +55,6 @@ function toCode(task: ThreadTask, params?: unknown): string {
|
|
|
67
55
|
return `(${src})(${argsStr})`;
|
|
68
56
|
}
|
|
69
57
|
|
|
70
|
-
// ─── Shared thread (runOnJS) ──────────────────────────────────────────────────
|
|
71
|
-
|
|
72
58
|
const SHARED_THREAD_NAME = 'RNOrgThread';
|
|
73
59
|
let _sharedThreadId: number | null = null;
|
|
74
60
|
|
|
@@ -84,10 +70,6 @@ export function runOnJS(task: ThreadTask, params?: unknown): void {
|
|
|
84
70
|
ReactNativeThread.runOnThread(getSharedThread(), toCode(task, params));
|
|
85
71
|
}
|
|
86
72
|
|
|
87
|
-
/**
|
|
88
|
-
* Runs a task on a brand-new isolated thread and returns a handle to it.
|
|
89
|
-
* @param name - Optional display name (default: `RNThread-<id>`).
|
|
90
|
-
*/
|
|
91
73
|
export function runOnNewJS(
|
|
92
74
|
task: ThreadTask,
|
|
93
75
|
params?: unknown,
|
|
@@ -100,10 +82,6 @@ export function runOnNewJS(
|
|
|
100
82
|
return createThreadHandle(id, resolvedName);
|
|
101
83
|
}
|
|
102
84
|
|
|
103
|
-
/**
|
|
104
|
-
* Creates a persistent thread. Optionally give it a name.
|
|
105
|
-
* @param name - Display name (default: `RNThread-<id>`).
|
|
106
|
-
*/
|
|
107
85
|
export function createThread(name?: string): ThreadHandle {
|
|
108
86
|
const id = ReactNativeThread.createThread();
|
|
109
87
|
const resolvedName = name ?? `RNThread-${id}`;
|
|
@@ -111,19 +89,10 @@ export function createThread(name?: string): ThreadHandle {
|
|
|
111
89
|
return createThreadHandle(id, resolvedName);
|
|
112
90
|
}
|
|
113
91
|
|
|
114
|
-
/**
|
|
115
|
-
* Returns info about every live thread currently managed by this library,
|
|
116
|
-
* including the shared `runOnJS` thread once it has been started.
|
|
117
|
-
*/
|
|
118
92
|
export function getThreads(): ThreadInfo[] {
|
|
119
93
|
return Array.from(_registry.values());
|
|
120
94
|
}
|
|
121
95
|
|
|
122
|
-
/**
|
|
123
|
-
* Destroy a thread by its numeric ID **or** by its name.
|
|
124
|
-
* When a name is given, the first matching thread is destroyed.
|
|
125
|
-
* This is also what `ThreadHandle.destroy()` calls internally.
|
|
126
|
-
*/
|
|
127
96
|
export function destroyThread(idOrName: number | string): void {
|
|
128
97
|
let targetId: number | undefined;
|
|
129
98
|
|