@depup/react-native-worklets 0.11.4-depup.0 → 0.12.2-depup.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/Common/cpp/worklets/AnimationFrameQueue/AnimationFrameBatchinator.cpp +6 -3
- package/Common/cpp/worklets/Compat/StableApi.cpp +4 -0
- package/Common/cpp/worklets/Compat/StableApi.h +3 -1
- package/Common/cpp/worklets/NativeModules/JSIWorkletsModuleProxy.cpp +26 -43
- package/Common/cpp/worklets/RunLoop/EventLoop.cpp +16 -7
- package/Common/cpp/worklets/RunLoop/EventLoop.h +4 -2
- package/Common/cpp/worklets/SharedItems/MemoryManager.cpp +2 -2
- package/Common/cpp/worklets/SharedItems/Serializable.cpp +17 -1
- package/Common/cpp/worklets/SharedItems/Serializable.h +14 -16
- package/Common/cpp/worklets/Tools/UIScheduler.cpp +10 -0
- package/Common/cpp/worklets/Tools/UIScheduler.h +3 -0
- package/Common/cpp/worklets/Tools/WorkletsJSIUtils.h +23 -0
- package/Common/cpp/worklets/WorkletRuntime/RuntimeManager.cpp +7 -4
- package/Common/cpp/worklets/WorkletRuntime/RuntimeManager.h +2 -1
- package/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.cpp +60 -88
- package/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.h +335 -122
- package/README.md +7 -7
- package/android/build.gradle.kts +20 -6
- package/android/src/main/cpp/worklets/android/AndroidUIScheduler.cpp +10 -8
- package/android/src/main/cpp/worklets/android/JScriptBufferWrapper.cpp +2 -11
- package/android/src/main/cpp/worklets/android/JScriptBufferWrapper.h +1 -4
- package/android/src/main/java/com/swmansion/worklets/AndroidUIScheduler.kt +3 -0
- package/android/src/main/java/com/swmansion/worklets/ScriptBufferWrapper.kt +87 -43
- package/android/src/main/java/com/swmansion/worklets/runloop/AnimationFrameQueue.kt +37 -5
- package/android/src/networking/com/swmansion/worklets/WorkletsModule.kt +2 -3
- package/android/src/no-networking/com/swmansion/worklets/WorkletsModule.kt +2 -3
- package/apple/worklets/apple/IOSUIScheduler.h +3 -0
- package/apple/worklets/apple/IOSUIScheduler.mm +6 -1
- package/apple/worklets/apple/ScriptLoader.h +11 -0
- package/apple/worklets/apple/ScriptLoader.mm +124 -0
- package/apple/worklets/apple/WorkletsModule.h +1 -16
- package/apple/worklets/apple/WorkletsModule.mm +4 -16
- package/changes.json +6 -6
- package/compatibility.json +4 -1
- package/lib/module/WorkletsModule/NativeWorklets.native.js +2 -2
- package/lib/module/WorkletsModule/NativeWorklets.native.js.map +1 -1
- package/lib/module/debug/jsVersion.js +1 -1
- package/lib/module/memory/serializable.native.js +61 -23
- package/lib/module/memory/serializable.native.js.map +1 -1
- package/lib/module/memory/synchronizableUnpacker.native.js +16 -8
- package/lib/module/memory/synchronizableUnpacker.native.js.map +1 -1
- package/lib/module/mock.js +58 -17
- package/lib/module/mock.js.map +1 -1
- package/lib/module/runLoop/uiRuntime/requestAnimationFrame.js +1 -2
- package/lib/module/runLoop/uiRuntime/requestAnimationFrame.js.map +1 -1
- package/lib/module/runLoop/workletRuntime/taskQueue.js +0 -1
- package/lib/module/runLoop/workletRuntime/taskQueue.js.map +1 -1
- package/lib/module/runtimes.native.js +7 -8
- package/lib/module/runtimes.native.js.map +1 -1
- package/lib/module/threads.js +35 -24
- package/lib/module/threads.js.map +1 -1
- package/lib/typescript/WorkletsModule/NativeWorklets.native.d.ts.map +1 -1
- package/lib/typescript/WorkletsModule/workletsModuleProxy.d.ts +1 -1
- package/lib/typescript/WorkletsModule/workletsModuleProxy.d.ts.map +1 -1
- package/lib/typescript/debug/jsVersion.d.ts +1 -1
- package/lib/typescript/memory/serializable.native.d.ts.map +1 -1
- package/lib/typescript/memory/synchronizableUnpacker.native.d.ts.map +1 -1
- package/lib/typescript/runLoop/uiRuntime/requestAnimationFrame.d.ts.map +1 -1
- package/lib/typescript/runLoop/workletRuntime/taskQueue.d.ts.map +1 -1
- package/lib/typescript/runtimes.native.d.ts.map +1 -1
- package/lib/typescript/specs/NativeWorkletsModule.d.ts +1 -1
- package/lib/typescript/threads.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +38 -8
- package/lib/typescript/types.d.ts.map +1 -1
- package/package.json +24 -25
- package/plugin/index.js +1625 -1572
- package/src/WorkletsModule/NativeWorklets.native.ts +4 -2
- package/src/WorkletsModule/workletsModuleProxy.ts +2 -1
- package/src/debug/jsVersion.ts +1 -1
- package/src/memory/serializable.native.ts +108 -38
- package/src/memory/synchronizableUnpacker.native.ts +16 -10
- package/src/mock.ts +85 -23
- package/src/runLoop/uiRuntime/requestAnimationFrame.ts +1 -2
- package/src/runLoop/workletRuntime/taskQueue.ts +0 -1
- package/src/runtimes.native.ts +12 -8
- package/src/threads.ts +38 -26
- package/src/types.ts +42 -8
- package/Common/cpp/worklets/Registries/EventHandlerRegistry.cpp +0 -95
- package/Common/cpp/worklets/Registries/EventHandlerRegistry.h +0 -44
- package/Common/cpp/worklets/Tools/WorkletEventHandler.cpp +0 -34
- package/Common/cpp/worklets/Tools/WorkletEventHandler.h +0 -44
- package/jest/pluginTestUtils.ts +0 -16
|
@@ -25,21 +25,13 @@ jni::local_ref<JScriptBufferWrapper::jhybriddata> JScriptBufferWrapper::initHybr
|
|
|
25
25
|
|
|
26
26
|
jni::local_ref<JScriptBufferWrapper::jhybriddata> JScriptBufferWrapper::initHybridFromFile(
|
|
27
27
|
jni::alias_ref<jhybridobject> jThis, // NOLINT //(performance-unnecessary-value-param)
|
|
28
|
-
const std::string &fileName
|
|
28
|
+
const std::string &fileName,
|
|
29
|
+
const std::string &sourceURL) {
|
|
29
30
|
std::shared_ptr<const ScriptBuffer> script;
|
|
30
31
|
RecoverableError::runRethrowingAsRecoverable<std::system_error>([&fileName, &script]() {
|
|
31
32
|
auto bigString = JSBigFileString::fromPath(fileName);
|
|
32
33
|
script = std::make_shared<ScriptBuffer>(std::move(bigString));
|
|
33
34
|
});
|
|
34
|
-
return makeCxxInstance(std::move(script), fileName);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
jni::local_ref<JScriptBufferWrapper::jhybriddata> JScriptBufferWrapper::initHybridFromString(
|
|
38
|
-
jni::alias_ref<jhybridobject> jThis, // NOLINT //(performance-unnecessary-value-param)
|
|
39
|
-
const std::string &scriptStr,
|
|
40
|
-
const std::string &sourceURL) {
|
|
41
|
-
auto bigString = std::make_shared<JSBigStdString>(scriptStr);
|
|
42
|
-
auto script = std::make_shared<ScriptBuffer>(std::move(bigString));
|
|
43
35
|
return makeCxxInstance(std::move(script), sourceURL);
|
|
44
36
|
}
|
|
45
37
|
|
|
@@ -60,7 +52,6 @@ void JScriptBufferWrapper::registerNatives() {
|
|
|
60
52
|
registerHybrid({
|
|
61
53
|
makeNativeMethod("initHybridFromAssets", JScriptBufferWrapper::initHybridFromAssets),
|
|
62
54
|
makeNativeMethod("initHybridFromFile", JScriptBufferWrapper::initHybridFromFile),
|
|
63
|
-
makeNativeMethod("initHybridFromString", JScriptBufferWrapper::initHybridFromString),
|
|
64
55
|
});
|
|
65
56
|
}
|
|
66
57
|
} // namespace worklets
|
|
@@ -31,11 +31,8 @@ class JScriptBufferWrapper : public jni::HybridClass<JScriptBufferWrapper> {
|
|
|
31
31
|
jni::alias_ref<jhybridobject> jThis,
|
|
32
32
|
jni::alias_ref<JAssetManager::javaobject> assetManager,
|
|
33
33
|
const std::string &assetURL);
|
|
34
|
-
static jni::local_ref<JScriptBufferWrapper::jhybriddata> initHybridFromFile(
|
|
35
|
-
jni::alias_ref<jhybridobject> jThis,
|
|
36
|
-
const std::string &fileName);
|
|
37
34
|
static jni::local_ref<JScriptBufferWrapper::jhybriddata>
|
|
38
|
-
|
|
35
|
+
initHybridFromFile(jni::alias_ref<jhybridobject> jThis, const std::string &fileName, const std::string &sourceURL);
|
|
39
36
|
|
|
40
37
|
friend HybridBase;
|
|
41
38
|
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
package com.swmansion.worklets
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
|
+
import android.content.Context
|
|
4
5
|
import android.content.res.AssetManager
|
|
5
|
-
import android.os.Build
|
|
6
6
|
import com.facebook.jni.HybridData
|
|
7
7
|
import com.facebook.proguard.annotations.DoNotStrip
|
|
8
8
|
import com.facebook.proguard.annotations.DoNotStripAny
|
|
9
|
+
import com.facebook.react.ReactApplication
|
|
10
|
+
import org.json.JSONException
|
|
11
|
+
import org.json.JSONObject
|
|
12
|
+
import java.io.File
|
|
9
13
|
import java.io.IOException
|
|
10
|
-
import java.io.InputStream
|
|
11
14
|
import java.net.HttpURLConnection
|
|
12
15
|
import java.net.URL
|
|
13
16
|
import java.nio.charset.StandardCharsets
|
|
@@ -17,13 +20,18 @@ import java.nio.charset.StandardCharsets
|
|
|
17
20
|
@SuppressLint("MissingNativeLoadLibrary")
|
|
18
21
|
@DoNotStripAny
|
|
19
22
|
class ScriptBufferWrapper(
|
|
20
|
-
uri: String
|
|
21
|
-
|
|
23
|
+
uri: String?,
|
|
24
|
+
context: Context,
|
|
22
25
|
) {
|
|
23
26
|
@field:DoNotStrip
|
|
24
27
|
private val mHybridData: HybridData
|
|
25
28
|
|
|
26
29
|
init {
|
|
30
|
+
checkNotNull(uri) {
|
|
31
|
+
"[Worklets] No script URL provided. Make sure the packager is running or you have " +
|
|
32
|
+
"embedded a JS bundle in your application bundle."
|
|
33
|
+
}
|
|
34
|
+
|
|
27
35
|
val filePrefix = "file://"
|
|
28
36
|
val absPathPrefix = "/"
|
|
29
37
|
val assetsPrefix = "assets://"
|
|
@@ -32,23 +40,20 @@ class ScriptBufferWrapper(
|
|
|
32
40
|
when {
|
|
33
41
|
uri.startsWith(filePrefix) -> {
|
|
34
42
|
val fileName = uri.substring(filePrefix.length)
|
|
35
|
-
initHybridFromFile(fileName)
|
|
43
|
+
initHybridFromFile(fileName, fileName)
|
|
36
44
|
}
|
|
37
45
|
uri.startsWith(assetsPrefix) -> {
|
|
38
46
|
val assetURL = uri.substring(assetsPrefix.length)
|
|
39
|
-
initHybridFromAssets(
|
|
47
|
+
initHybridFromAssets(context.assets, assetURL)
|
|
40
48
|
}
|
|
41
49
|
uri.startsWith(absPathPrefix) -> {
|
|
42
|
-
initHybridFromFile(uri)
|
|
50
|
+
initHybridFromFile(uri, uri)
|
|
43
51
|
}
|
|
44
52
|
else -> {
|
|
45
|
-
val
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
throw RuntimeException(e)
|
|
50
|
-
}
|
|
51
|
-
initHybridFromString(scriptContent, uri)
|
|
53
|
+
val bundleFile =
|
|
54
|
+
reactNativeDownloadedBundleFile(context)
|
|
55
|
+
?: File(context.cacheDir, DEV_BUNDLE_FILE_NAME).also { downloadScriptToFile(uri, it) }
|
|
56
|
+
initHybridFromFile(bundleFile.absolutePath, uri)
|
|
52
57
|
}
|
|
53
58
|
}
|
|
54
59
|
}
|
|
@@ -58,45 +63,84 @@ class ScriptBufferWrapper(
|
|
|
58
63
|
assetURL: String,
|
|
59
64
|
): HybridData
|
|
60
65
|
|
|
61
|
-
private external fun initHybridFromFile(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
script: String,
|
|
65
|
-
url: String,
|
|
66
|
+
private external fun initHybridFromFile(
|
|
67
|
+
fileName: String,
|
|
68
|
+
sourceURL: String,
|
|
66
69
|
): HybridData
|
|
67
70
|
|
|
68
71
|
companion object {
|
|
69
|
-
private
|
|
70
|
-
|
|
71
|
-
val connection = scriptUrl.openConnection() as HttpURLConnection
|
|
72
|
-
try {
|
|
73
|
-
val content: ByteArray
|
|
72
|
+
private const val DEV_BUNDLE_FILE_NAME = "WorkletsDevBundle.js"
|
|
73
|
+
private const val CONNECT_TIMEOUT_MS = 5_000
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
private fun reactNativeDownloadedBundleFile(context: Context): File? {
|
|
76
|
+
val reactApplication = context.applicationContext as? ReactApplication ?: return null
|
|
77
|
+
val bundlePath =
|
|
78
|
+
reactApplication.reactHost
|
|
79
|
+
?.devSupportManager
|
|
80
|
+
?.downloadedJSBundleFile ?: return null
|
|
81
|
+
return File(bundlePath).takeIf { it.exists() }
|
|
82
|
+
}
|
|
80
83
|
|
|
81
|
-
|
|
84
|
+
private fun downloadScriptToFile(
|
|
85
|
+
url: String,
|
|
86
|
+
outputFile: File,
|
|
87
|
+
) {
|
|
88
|
+
val connection = URL(url).openConnection() as HttpURLConnection
|
|
89
|
+
connection.connectTimeout = CONNECT_TIMEOUT_MS
|
|
90
|
+
try {
|
|
91
|
+
val statusCode =
|
|
92
|
+
try {
|
|
93
|
+
connection.responseCode
|
|
94
|
+
} catch (e: IOException) {
|
|
95
|
+
throw RuntimeException("[Worklets] Could not connect to development server.\n", e)
|
|
96
|
+
}
|
|
97
|
+
if (statusCode != 200) {
|
|
98
|
+
throw RuntimeException(serverErrorMessage(url, statusCode, connection))
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
val tmpFile = File(outputFile.path + ".tmp")
|
|
102
|
+
connection.inputStream.use { input ->
|
|
103
|
+
tmpFile.outputStream().use { output -> input.copyTo(output) }
|
|
104
|
+
}
|
|
105
|
+
if (!tmpFile.renameTo(outputFile)) {
|
|
106
|
+
throw IOException("Couldn't rename $tmpFile to $outputFile")
|
|
107
|
+
}
|
|
108
|
+
} catch (e: IOException) {
|
|
109
|
+
throw RuntimeException(
|
|
110
|
+
"[Worklets] Failed to store worklets bundle from URL $url: ${e.message}",
|
|
111
|
+
e,
|
|
112
|
+
)
|
|
113
|
+
}
|
|
82
114
|
} finally {
|
|
83
115
|
connection.disconnect()
|
|
84
116
|
}
|
|
85
117
|
}
|
|
86
118
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
119
|
+
private fun serverErrorMessage(
|
|
120
|
+
url: String,
|
|
121
|
+
statusCode: Int,
|
|
122
|
+
connection: HttpURLConnection,
|
|
123
|
+
): String {
|
|
124
|
+
val body = connection.errorStream?.use { String(it.readBytes(), StandardCharsets.UTF_8) }
|
|
125
|
+
val metroMessage = parseMetroError(body)
|
|
126
|
+
if (metroMessage != null) {
|
|
127
|
+
return "[Worklets] $metroMessage"
|
|
128
|
+
}
|
|
129
|
+
return "[Worklets] The development server returned response error code: $statusCode\n\n" +
|
|
130
|
+
"URL: $url\n\n" +
|
|
131
|
+
"Body:\n$body"
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private fun parseMetroError(body: String?): String? {
|
|
135
|
+
if (body.isNullOrEmpty()) {
|
|
136
|
+
return null
|
|
137
|
+
}
|
|
138
|
+
return try {
|
|
139
|
+
val json = JSONObject(body)
|
|
140
|
+
val fileName = json.getString("filename").substringAfterLast('/')
|
|
141
|
+
"${json.getString("message")}\n at $fileName:${json.getInt("lineNumber")}:${json.getInt("column")}"
|
|
142
|
+
} catch (e: JSONException) {
|
|
143
|
+
null
|
|
100
144
|
}
|
|
101
145
|
}
|
|
102
146
|
}
|
|
@@ -25,7 +25,9 @@ class AnimationFrameQueue(
|
|
|
25
25
|
}
|
|
26
26
|
private val mCallbackPosted = AtomicBoolean()
|
|
27
27
|
private val mPaused = AtomicBoolean()
|
|
28
|
+
private val mInvalidated = AtomicBoolean()
|
|
28
29
|
private val mFrameCallbacks = mutableListOf<AnimationFrameCallback>()
|
|
30
|
+
private val mDispatchLock = Any()
|
|
29
31
|
|
|
30
32
|
fun resume() {
|
|
31
33
|
if (mPaused.getAndSet(false)) {
|
|
@@ -44,8 +46,21 @@ class AnimationFrameQueue(
|
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
|
|
49
|
+
fun invalidate() {
|
|
50
|
+
mInvalidated.set(true)
|
|
51
|
+
removePostedFrameCallback()
|
|
52
|
+
synchronized(mDispatchLock) {
|
|
53
|
+
synchronized(mFrameCallbacks) {
|
|
54
|
+
mFrameCallbacks.clear()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
47
59
|
fun requestAnimationFrame(animationFrameCallback: AnimationFrameCallback) {
|
|
48
60
|
synchronized(mFrameCallbacks) {
|
|
61
|
+
if (mInvalidated.get()) {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
49
64
|
mFrameCallbacks.add(animationFrameCallback)
|
|
50
65
|
}
|
|
51
66
|
scheduleQueueExecution()
|
|
@@ -64,6 +79,9 @@ class AnimationFrameQueue(
|
|
|
64
79
|
|
|
65
80
|
private fun scheduleQueueExecution() {
|
|
66
81
|
synchronized(mPaused) {
|
|
82
|
+
if (mInvalidated.get()) {
|
|
83
|
+
return
|
|
84
|
+
}
|
|
67
85
|
if (!mPaused.get() && !mCallbackPosted.getAndSet(true)) {
|
|
68
86
|
mReactChoreographer.postFrameCallback(
|
|
69
87
|
ReactChoreographer.CallbackType.NATIVE_ANIMATED_MODULE,
|
|
@@ -73,6 +91,15 @@ class AnimationFrameQueue(
|
|
|
73
91
|
}
|
|
74
92
|
}
|
|
75
93
|
|
|
94
|
+
private fun removePostedFrameCallback() {
|
|
95
|
+
if (mCallbackPosted.getAndSet(false)) {
|
|
96
|
+
mReactChoreographer.removeFrameCallback(
|
|
97
|
+
ReactChoreographer.CallbackType.NATIVE_ANIMATED_MODULE,
|
|
98
|
+
mChoreographerCallback,
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
76
103
|
private fun executeQueue(frameTimeNanos: Long) {
|
|
77
104
|
val currentFrameTimeMs = calculateTimestamp(frameTimeNanos)
|
|
78
105
|
if (currentFrameTimeMs <= lastFrameTimeMs) {
|
|
@@ -83,12 +110,17 @@ class AnimationFrameQueue(
|
|
|
83
110
|
return
|
|
84
111
|
}
|
|
85
112
|
|
|
86
|
-
|
|
87
|
-
|
|
113
|
+
synchronized(mDispatchLock) {
|
|
114
|
+
val frameCallbacks = pullCallbacks()
|
|
115
|
+
mCallbackPosted.set(false)
|
|
116
|
+
if (mInvalidated.get()) {
|
|
117
|
+
return
|
|
118
|
+
}
|
|
88
119
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
120
|
+
lastFrameTimeMs = currentFrameTimeMs
|
|
121
|
+
for (callback in frameCallbacks) {
|
|
122
|
+
callback.onAnimationFrame(currentFrameTimeMs)
|
|
123
|
+
}
|
|
92
124
|
}
|
|
93
125
|
}
|
|
94
126
|
|
|
@@ -71,11 +71,9 @@ class WorkletsModule(
|
|
|
71
71
|
val jsContext = checkNotNull(context.javaScriptContextHolder).get()
|
|
72
72
|
val jsCallInvokerHolder = context.jsCallInvokerHolder as CallInvokerHolderImpl
|
|
73
73
|
|
|
74
|
-
val sourceURL = context.sourceURL
|
|
75
|
-
|
|
76
74
|
val scriptBufferWrapper: ScriptBufferWrapper? =
|
|
77
75
|
if (bundleModeEnabled) {
|
|
78
|
-
ScriptBufferWrapper(sourceURL
|
|
76
|
+
ScriptBufferWrapper(context.sourceURL, context)
|
|
79
77
|
} else {
|
|
80
78
|
null
|
|
81
79
|
}
|
|
@@ -173,6 +171,7 @@ class WorkletsModule(
|
|
|
173
171
|
mInvalidated = true
|
|
174
172
|
reactApplicationContext.removeLifecycleEventListener(this)
|
|
175
173
|
}
|
|
174
|
+
mAnimationFrameQueue.invalidate()
|
|
176
175
|
if (mHybridData != null && mHybridData!!.isValid) {
|
|
177
176
|
invalidateCpp()
|
|
178
177
|
}
|
|
@@ -67,11 +67,9 @@ class WorkletsModule(
|
|
|
67
67
|
val jsContext = checkNotNull(context.javaScriptContextHolder).get()
|
|
68
68
|
val jsCallInvokerHolder = context.jsCallInvokerHolder as CallInvokerHolderImpl
|
|
69
69
|
|
|
70
|
-
val sourceURL = context.sourceURL
|
|
71
|
-
|
|
72
70
|
val scriptBufferWrapper: ScriptBufferWrapper? =
|
|
73
71
|
if (bundleModeEnabled) {
|
|
74
|
-
ScriptBufferWrapper(sourceURL
|
|
72
|
+
ScriptBufferWrapper(context.sourceURL, context)
|
|
75
73
|
} else {
|
|
76
74
|
null
|
|
77
75
|
}
|
|
@@ -129,6 +127,7 @@ class WorkletsModule(
|
|
|
129
127
|
mInvalidated = true
|
|
130
128
|
reactApplicationContext.removeLifecycleEventListener(this)
|
|
131
129
|
}
|
|
130
|
+
mAnimationFrameQueue.invalidate()
|
|
132
131
|
if (mHybridData != null && mHybridData!!.isValid) {
|
|
133
132
|
invalidateCpp()
|
|
134
133
|
}
|
|
@@ -9,6 +9,9 @@ using namespace worklets;
|
|
|
9
9
|
class IOSUIScheduler : public UIScheduler, public std::enable_shared_from_this<IOSUIScheduler> {
|
|
10
10
|
public:
|
|
11
11
|
void scheduleOnUI(std::function<void()> job) override;
|
|
12
|
+
|
|
13
|
+
protected:
|
|
14
|
+
bool queryIsOnUIThread() const override;
|
|
12
15
|
};
|
|
13
16
|
|
|
14
17
|
} // namespace worklets
|
|
@@ -5,9 +5,14 @@ namespace worklets {
|
|
|
5
5
|
using namespace facebook;
|
|
6
6
|
using namespace react;
|
|
7
7
|
|
|
8
|
+
bool IOSUIScheduler::queryIsOnUIThread() const
|
|
9
|
+
{
|
|
10
|
+
return [NSThread isMainThread];
|
|
11
|
+
}
|
|
12
|
+
|
|
8
13
|
void IOSUIScheduler::scheduleOnUI(std::function<void()> job)
|
|
9
14
|
{
|
|
10
|
-
if (
|
|
15
|
+
if (isOnUIThread()) {
|
|
11
16
|
job();
|
|
12
17
|
return;
|
|
13
18
|
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#import <worklets/apple/ScriptLoader.h>
|
|
2
|
+
|
|
3
|
+
namespace worklets {
|
|
4
|
+
namespace {
|
|
5
|
+
|
|
6
|
+
class NSDataScript : public JSBigString {
|
|
7
|
+
public:
|
|
8
|
+
explicit NSDataScript(NSData *data) : data_(data), size_([data length]) {}
|
|
9
|
+
|
|
10
|
+
bool isAscii() const override
|
|
11
|
+
{
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const char *c_str() const override
|
|
16
|
+
{
|
|
17
|
+
return static_cast<const char *>(data_.bytes);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
size_t size() const override
|
|
21
|
+
{
|
|
22
|
+
return size_;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private:
|
|
26
|
+
NSData *data_;
|
|
27
|
+
size_t size_;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
NSData *loadScriptFromFile(NSURL *url)
|
|
31
|
+
{
|
|
32
|
+
NSError *error;
|
|
33
|
+
NSData *data = [NSData dataWithContentsOfFile:url.path options:NSDataReadingMappedIfSafe error:&error];
|
|
34
|
+
|
|
35
|
+
if (data == nil) [[unlikely]] {
|
|
36
|
+
NSString *errorMsg = [NSString stringWithFormat:@"[Worklets] Failed to load worklets bundle from file %@: %@",
|
|
37
|
+
url.path,
|
|
38
|
+
error.localizedDescription];
|
|
39
|
+
NSLog(@"%@", errorMsg);
|
|
40
|
+
throw std::runtime_error([errorMsg UTF8String]);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return data;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
NSData *downloadScript(NSURL *url)
|
|
47
|
+
{
|
|
48
|
+
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
|
49
|
+
__block NSData *data = nil;
|
|
50
|
+
__block NSURLResponse *response = nil;
|
|
51
|
+
__block NSError *error = nil;
|
|
52
|
+
|
|
53
|
+
NSURLSessionDataTask *task = [[NSURLSession sharedSession]
|
|
54
|
+
dataTaskWithURL:url
|
|
55
|
+
completionHandler:^(NSData *taskData, NSURLResponse *taskResponse, NSError *taskError) {
|
|
56
|
+
data = taskData;
|
|
57
|
+
response = taskResponse;
|
|
58
|
+
error = taskError;
|
|
59
|
+
dispatch_semaphore_signal(semaphore);
|
|
60
|
+
}];
|
|
61
|
+
[task resume];
|
|
62
|
+
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
|
63
|
+
|
|
64
|
+
if (error != nil) [[unlikely]] {
|
|
65
|
+
NSString *errorMsg;
|
|
66
|
+
if ([error.domain isEqualToString:NSURLErrorDomain]) {
|
|
67
|
+
errorMsg = @"[Worklets] Could not connect to development server.\n";
|
|
68
|
+
} else {
|
|
69
|
+
errorMsg = [NSString stringWithFormat:@"[Worklets] Failed to load worklets bundle from URL %@: %@",
|
|
70
|
+
url,
|
|
71
|
+
error.localizedDescription];
|
|
72
|
+
}
|
|
73
|
+
NSLog(@"%@", errorMsg);
|
|
74
|
+
throw std::runtime_error([errorMsg UTF8String]);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
NSInteger statusCode =
|
|
78
|
+
[response isKindOfClass:[NSHTTPURLResponse class]] ? [(NSHTTPURLResponse *)response statusCode] : 200;
|
|
79
|
+
if (statusCode != 200) [[unlikely]] {
|
|
80
|
+
NSString *description = nil;
|
|
81
|
+
NSDictionary *jsonError = data != nil ? [NSJSONSerialization JSONObjectWithData:data options:0 error:nil] : nil;
|
|
82
|
+
if ([jsonError isKindOfClass:[NSDictionary class]] && [jsonError[@"message"] isKindOfClass:[NSString class]] &&
|
|
83
|
+
[jsonError[@"message"] length] > 0) {
|
|
84
|
+
description = jsonError[@"message"];
|
|
85
|
+
} else {
|
|
86
|
+
description = [NSString stringWithFormat:@"Received status code %ld while fetching the bundle", (long)statusCode];
|
|
87
|
+
}
|
|
88
|
+
NSString *errorMsg =
|
|
89
|
+
[NSString stringWithFormat:@"[Worklets] Failed to load worklets bundle from URL %@: %@", url, description];
|
|
90
|
+
NSLog(@"%@", errorMsg);
|
|
91
|
+
throw std::runtime_error([errorMsg UTF8String]);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
NSString *mimeType = response.MIMEType;
|
|
95
|
+
if (![mimeType isEqualToString:@"application/javascript"] && ![mimeType isEqualToString:@"text/javascript"])
|
|
96
|
+
[[unlikely]] {
|
|
97
|
+
NSString *errorMsg = [NSString
|
|
98
|
+
stringWithFormat:
|
|
99
|
+
@"[Worklets] Expected MIME-Type of the worklets bundle to be 'application/javascript' or 'text/javascript', but got '%@'",
|
|
100
|
+
mimeType];
|
|
101
|
+
NSLog(@"%@", errorMsg);
|
|
102
|
+
throw std::runtime_error([errorMsg UTF8String]);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return data;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
} // namespace
|
|
109
|
+
|
|
110
|
+
std::shared_ptr<const ScriptBuffer> getScript(NSURL *url)
|
|
111
|
+
{
|
|
112
|
+
if (url == nil) [[unlikely]] {
|
|
113
|
+
NSString *errorMsg =
|
|
114
|
+
@"[Worklets] No script URL provided. Make sure the packager is running or you have embedded a "
|
|
115
|
+
@"JS bundle in your application bundle.";
|
|
116
|
+
NSLog(@"%@", errorMsg);
|
|
117
|
+
throw std::runtime_error([errorMsg UTF8String]);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
NSData *data = url.isFileURL ? loadScriptFromFile(url) : downloadScript(url);
|
|
121
|
+
return std::make_shared<const ScriptBuffer>(std::make_shared<const NSDataScript>(data));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
} // namespace worklets
|
|
@@ -6,22 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
#import <worklets/NativeModules/WorkletsModuleProxy.h>
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
// Bundle mode
|
|
11
|
-
#import <React/NSBigStringBuffer.h>
|
|
12
|
-
#import <React/RCTBundleConsumer.h>
|
|
13
|
-
#endif // __has_include(<React/RCTBundleConsumer.h>)
|
|
14
|
-
|
|
15
|
-
@interface WorkletsModule : RCTEventEmitter <
|
|
16
|
-
NativeWorkletsModuleSpec,
|
|
17
|
-
RCTCallInvokerModule,
|
|
18
|
-
RCTInvalidating
|
|
19
|
-
#if __has_include(<React/RCTBundleConsumer.h>)
|
|
20
|
-
// Bundle mode
|
|
21
|
-
,
|
|
22
|
-
RCTBundleConsumer
|
|
23
|
-
#endif // __has_include(<React/RCTBundleConsumer.h>)
|
|
24
|
-
>
|
|
9
|
+
@interface WorkletsModule : RCTEventEmitter <NativeWorkletsModuleSpec, RCTCallInvokerModule, RCTInvalidating>
|
|
25
10
|
|
|
26
11
|
- (std::shared_ptr<worklets::WorkletsModuleProxy>)getWorkletsModuleProxy;
|
|
27
12
|
|
|
@@ -7,8 +7,11 @@
|
|
|
7
7
|
#import <worklets/apple/AssertJavaScriptQueue.h>
|
|
8
8
|
#import <worklets/apple/AssertTurboModuleManagerQueue.h>
|
|
9
9
|
#import <worklets/apple/IOSUIScheduler.h>
|
|
10
|
+
#import <worklets/apple/ScriptLoader.h>
|
|
10
11
|
#import <worklets/apple/WorkletsModule.h>
|
|
11
12
|
|
|
13
|
+
#import <Foundation/Foundation.h>
|
|
14
|
+
|
|
12
15
|
#import <React/RCTBridge+Private.h>
|
|
13
16
|
#import <React/RCTCallInvoker.h>
|
|
14
17
|
|
|
@@ -64,7 +67,7 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(installTurboModule : (BOOL)bundleModeEnab
|
|
|
64
67
|
|
|
65
68
|
if (bundleModeEnabled) {
|
|
66
69
|
NSURL *url = bundleManager_.bundleURL;
|
|
67
|
-
script =
|
|
70
|
+
script = getScript(url);
|
|
68
71
|
sourceURL = [[url absoluteString] UTF8String];
|
|
69
72
|
}
|
|
70
73
|
|
|
@@ -127,21 +130,6 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(toggleSlowAnimationsOnUIRuntime)
|
|
|
127
130
|
return std::make_shared<facebook::react::NativeWorkletsModuleSpecJSI>(params);
|
|
128
131
|
}
|
|
129
132
|
|
|
130
|
-
- (std::shared_ptr<const ScriptBuffer>)getScript:(NSURL *)url
|
|
131
|
-
{
|
|
132
|
-
NSData *data = [NSData dataWithContentsOfURL:url];
|
|
133
|
-
|
|
134
|
-
if (!data) [[unlikely]] {
|
|
135
|
-
NSString *errorMsg = [NSString stringWithFormat:@"[Worklets] Failed to load worklets bundle from URL: %@", url];
|
|
136
|
-
NSLog(@"%@", errorMsg);
|
|
137
|
-
throw std::runtime_error([errorMsg UTF8String]);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
auto str = std::string(reinterpret_cast<const char *>([data bytes]), [data length]);
|
|
141
|
-
auto bigString = std::make_shared<const JSBigStdString>(str);
|
|
142
|
-
return std::make_shared<const ScriptBuffer>(bigString);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
133
|
- (std::shared_ptr<RuntimeBindings>)getRuntimeBindings:(jsi::Runtime &)rnRuntime
|
|
146
134
|
bundleModeEnabled:(BOOL)bundleModeEnabled
|
|
147
135
|
{
|
package/changes.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"bumped": {
|
|
3
3
|
"@babel/generator": {
|
|
4
4
|
"from": "^7.27.1",
|
|
5
|
-
"to": "^8.0.
|
|
5
|
+
"to": "^8.0.5"
|
|
6
6
|
},
|
|
7
7
|
"@babel/plugin-transform-arrow-functions": {
|
|
8
8
|
"from": "^7.27.1",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"@babel/plugin-transform-classes": {
|
|
16
16
|
"from": "^7.28.6",
|
|
17
|
-
"to": "^8.0.
|
|
17
|
+
"to": "^8.0.5"
|
|
18
18
|
},
|
|
19
19
|
"@babel/plugin-transform-nullish-coalescing-operator": {
|
|
20
20
|
"from": "^7.28.6",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"@babel/plugin-transform-optional-chaining": {
|
|
24
24
|
"from": "^7.28.6",
|
|
25
|
-
"to": "^8.0.
|
|
25
|
+
"to": "^8.0.5"
|
|
26
26
|
},
|
|
27
27
|
"@babel/plugin-transform-shorthand-properties": {
|
|
28
28
|
"from": "^7.27.1",
|
|
@@ -42,17 +42,17 @@
|
|
|
42
42
|
},
|
|
43
43
|
"@babel/traverse": {
|
|
44
44
|
"from": "^7.27.1",
|
|
45
|
-
"to": "^8.0.
|
|
45
|
+
"to": "^8.0.5"
|
|
46
46
|
},
|
|
47
47
|
"@babel/types": {
|
|
48
48
|
"from": "^7.27.1",
|
|
49
|
-
"to": "^8.0.
|
|
49
|
+
"to": "^8.0.5"
|
|
50
50
|
},
|
|
51
51
|
"semver": {
|
|
52
52
|
"from": "^7.7.4",
|
|
53
53
|
"to": "^7.8.5"
|
|
54
54
|
}
|
|
55
55
|
},
|
|
56
|
-
"timestamp": "2026-
|
|
56
|
+
"timestamp": "2026-09-13T00:59:44.373Z",
|
|
57
57
|
"totalUpdated": 13
|
|
58
58
|
}
|
package/compatibility.json
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"nightly": {
|
|
3
|
-
"react-native": ["0.83", "0.84", "0.85", "0.86"]
|
|
3
|
+
"react-native": ["0.83", "0.84", "0.85", "0.86", "0.87"]
|
|
4
|
+
},
|
|
5
|
+
"0.12.x": {
|
|
6
|
+
"react-native": ["0.83", "0.84", "0.85", "0.86", "0.87"]
|
|
4
7
|
},
|
|
5
8
|
"0.11.x": {
|
|
6
9
|
"react-native": ["0.83", "0.84", "0.85", "0.86"]
|
|
@@ -124,8 +124,8 @@ See https://docs.swmansion.com/react-native-worklets/docs/guides/troubleshooting
|
|
|
124
124
|
runOnUISync(worklet, scheduleStack) {
|
|
125
125
|
return this.#workletsModuleProxy.runOnUISync(worklet, scheduleStack);
|
|
126
126
|
}
|
|
127
|
-
createWorkletRuntime(name, initializer, useDefaultQueue, customQueue, enableEventLoop) {
|
|
128
|
-
return this.#workletsModuleProxy.createWorkletRuntime(name, initializer, useDefaultQueue, customQueue, enableEventLoop);
|
|
127
|
+
createWorkletRuntime(name, initializer, useDefaultQueue, customQueue, enableEventLoop, enableLocking) {
|
|
128
|
+
return this.#workletsModuleProxy.createWorkletRuntime(name, initializer, useDefaultQueue, customQueue, enableEventLoop, enableLocking);
|
|
129
129
|
}
|
|
130
130
|
scheduleOnRuntime(workletRuntime, serializableWorklet, scheduleStack) {
|
|
131
131
|
return this.#workletsModuleProxy.scheduleOnRuntime(workletRuntime, serializableWorklet, scheduleStack);
|