reanimated-pause-state 0.1.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 +276 -0
- package/dist/babel.d.ts +21 -0
- package/dist/babel.d.ts.map +1 -0
- package/dist/babel.js +315 -0
- package/dist/babel.js.map +1 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +454 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +144 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +54 -0
- package/src/babel.ts +486 -0
- package/src/global.d.ts +11 -0
- package/src/index.ts +553 -0
- package/src/types.ts +156 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "reanimated-pause-state",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Pause Reanimated animations and inspect their state at any frame",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./babel": {
|
|
13
|
+
"types": "./dist/babel.d.ts",
|
|
14
|
+
"default": "./dist/babel.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"src",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc",
|
|
24
|
+
"prepublishOnly": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"react-native",
|
|
28
|
+
"reanimated",
|
|
29
|
+
"animation",
|
|
30
|
+
"debug",
|
|
31
|
+
"pause",
|
|
32
|
+
"state",
|
|
33
|
+
"snapshot",
|
|
34
|
+
"babel-plugin"
|
|
35
|
+
],
|
|
36
|
+
"author": "",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/skylarbarrera/reanimated-pause-state.git"
|
|
41
|
+
},
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/skylarbarrera/reanimated-pause-state/issues"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/skylarbarrera/reanimated-pause-state#readme",
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"react-native-reanimated": ">=3.0.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@babel/core": "^7.24.0",
|
|
51
|
+
"@types/babel__core": "^7.20.5",
|
|
52
|
+
"typescript": "^5.4.0"
|
|
53
|
+
}
|
|
54
|
+
}
|
package/src/babel.ts
ADDED
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* reanimated-pause-plugin/babel
|
|
3
|
+
*
|
|
4
|
+
* Babel plugin that instruments Reanimated animation factories
|
|
5
|
+
* to track callsite information and animation metadata for snapshots.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: This plugin must run BEFORE react-native-reanimated/plugin
|
|
8
|
+
* or react-native-worklets/plugin in your babel config.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { PluginObj, NodePath, types as t } from '@babel/core';
|
|
12
|
+
|
|
13
|
+
const PLUGIN_VERSION = '0.1.0';
|
|
14
|
+
|
|
15
|
+
// Animation factories to instrument
|
|
16
|
+
const INSTRUMENTED_FACTORIES = new Map([
|
|
17
|
+
['withTiming', 'timing'],
|
|
18
|
+
['withSpring', 'spring'],
|
|
19
|
+
['withDecay', 'decay'],
|
|
20
|
+
['withRepeat', 'repeat'],
|
|
21
|
+
['withSequence', 'sequence'],
|
|
22
|
+
['withDelay', 'delay'],
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Extract a literal value from an AST node (numbers, strings, booleans)
|
|
27
|
+
* Returns undefined for non-literal/dynamic values
|
|
28
|
+
*/
|
|
29
|
+
function extractLiteralValue(
|
|
30
|
+
node: t.Node | undefined,
|
|
31
|
+
t: typeof import('@babel/core').types
|
|
32
|
+
): number | string | boolean | undefined {
|
|
33
|
+
if (!node) return undefined;
|
|
34
|
+
if (t.isNumericLiteral(node)) return node.value;
|
|
35
|
+
if (t.isStringLiteral(node)) return node.value;
|
|
36
|
+
if (t.isBooleanLiteral(node)) return node.value;
|
|
37
|
+
if (t.isUnaryExpression(node) && node.operator === '-' && t.isNumericLiteral(node.argument)) {
|
|
38
|
+
return -node.argument.value;
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Extract config properties from an ObjectExpression
|
|
45
|
+
*/
|
|
46
|
+
function extractObjectConfig(
|
|
47
|
+
node: t.Node | undefined,
|
|
48
|
+
t: typeof import('@babel/core').types
|
|
49
|
+
): Array<t.ObjectProperty> {
|
|
50
|
+
const properties: Array<t.ObjectProperty> = [];
|
|
51
|
+
|
|
52
|
+
if (!node || !t.isObjectExpression(node)) {
|
|
53
|
+
return properties;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
for (const prop of node.properties) {
|
|
57
|
+
if (t.isObjectProperty(prop) && t.isIdentifier(prop.key)) {
|
|
58
|
+
const value = extractLiteralValue(prop.value, t);
|
|
59
|
+
if (value !== undefined) {
|
|
60
|
+
if (typeof value === 'number') {
|
|
61
|
+
properties.push(t.objectProperty(prop.key, t.numericLiteral(value)));
|
|
62
|
+
} else if (typeof value === 'string') {
|
|
63
|
+
properties.push(t.objectProperty(prop.key, t.stringLiteral(value)));
|
|
64
|
+
} else if (typeof value === 'boolean') {
|
|
65
|
+
properties.push(t.objectProperty(prop.key, t.booleanLiteral(value)));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return properties;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Build config metadata based on animation type and arguments
|
|
76
|
+
*/
|
|
77
|
+
function buildConfigMetadata(
|
|
78
|
+
animationType: string,
|
|
79
|
+
args: Array<t.Node>,
|
|
80
|
+
t: typeof import('@babel/core').types
|
|
81
|
+
): t.ObjectExpression {
|
|
82
|
+
const configProps: Array<t.ObjectProperty> = [];
|
|
83
|
+
|
|
84
|
+
switch (animationType) {
|
|
85
|
+
case 'timing': {
|
|
86
|
+
// withTiming(toValue, config?)
|
|
87
|
+
const toValue = extractLiteralValue(args[0], t);
|
|
88
|
+
if (toValue !== undefined) {
|
|
89
|
+
configProps.push(t.objectProperty(t.identifier('toValue'), t.numericLiteral(toValue as number)));
|
|
90
|
+
}
|
|
91
|
+
// Extract duration, easing from config object
|
|
92
|
+
const configObjProps = extractObjectConfig(args[1], t);
|
|
93
|
+
configProps.push(...configObjProps);
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
case 'spring': {
|
|
98
|
+
// withSpring(toValue, config?)
|
|
99
|
+
const toValue = extractLiteralValue(args[0], t);
|
|
100
|
+
if (toValue !== undefined) {
|
|
101
|
+
configProps.push(t.objectProperty(t.identifier('toValue'), t.numericLiteral(toValue as number)));
|
|
102
|
+
}
|
|
103
|
+
// Extract spring config (damping, mass, stiffness, etc.)
|
|
104
|
+
const configObjProps = extractObjectConfig(args[1], t);
|
|
105
|
+
configProps.push(...configObjProps);
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
case 'decay': {
|
|
110
|
+
// withDecay(config)
|
|
111
|
+
const configObjProps = extractObjectConfig(args[0], t);
|
|
112
|
+
configProps.push(...configObjProps);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
case 'repeat': {
|
|
117
|
+
// withRepeat(animation, numberOfReps?, reverse?)
|
|
118
|
+
const numberOfReps = extractLiteralValue(args[1], t);
|
|
119
|
+
if (numberOfReps !== undefined) {
|
|
120
|
+
configProps.push(t.objectProperty(t.identifier('numberOfReps'), t.numericLiteral(numberOfReps as number)));
|
|
121
|
+
}
|
|
122
|
+
const reverse = extractLiteralValue(args[2], t);
|
|
123
|
+
if (reverse !== undefined) {
|
|
124
|
+
configProps.push(t.objectProperty(t.identifier('reverse'), t.booleanLiteral(reverse as boolean)));
|
|
125
|
+
}
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
case 'delay': {
|
|
130
|
+
// withDelay(delayMs, animation)
|
|
131
|
+
const delayMs = extractLiteralValue(args[0], t);
|
|
132
|
+
if (delayMs !== undefined) {
|
|
133
|
+
configProps.push(t.objectProperty(t.identifier('delayMs'), t.numericLiteral(delayMs as number)));
|
|
134
|
+
}
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
case 'sequence': {
|
|
139
|
+
// withSequence(...animations)
|
|
140
|
+
configProps.push(t.objectProperty(t.identifier('count'), t.numericLiteral(args.length)));
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return t.objectExpression(configProps);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
interface PluginState {
|
|
149
|
+
filename: string;
|
|
150
|
+
markerInjected: boolean;
|
|
151
|
+
registryInjected: boolean;
|
|
152
|
+
factoryBindings: Map<string, string>; // local name -> original name
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export default function reanimatedPausePlugin({
|
|
156
|
+
types: t,
|
|
157
|
+
}: {
|
|
158
|
+
types: typeof import('@babel/core').types;
|
|
159
|
+
}): PluginObj<PluginState> {
|
|
160
|
+
return {
|
|
161
|
+
name: 'reanimated-pause-plugin',
|
|
162
|
+
|
|
163
|
+
pre(file) {
|
|
164
|
+
this.filename = file.opts.filename || 'unknown';
|
|
165
|
+
this.markerInjected = false;
|
|
166
|
+
this.registryInjected = false;
|
|
167
|
+
this.factoryBindings = new Map();
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
visitor: {
|
|
171
|
+
Program: {
|
|
172
|
+
enter(path, state) {
|
|
173
|
+
// Inject marker at the top of the program
|
|
174
|
+
if (!state.markerInjected) {
|
|
175
|
+
const marker = t.expressionStatement(
|
|
176
|
+
t.assignmentExpression(
|
|
177
|
+
'=',
|
|
178
|
+
t.memberExpression(
|
|
179
|
+
t.identifier('globalThis'),
|
|
180
|
+
t.identifier('__RPP_INSTALLED__')
|
|
181
|
+
),
|
|
182
|
+
t.objectExpression([
|
|
183
|
+
t.objectProperty(
|
|
184
|
+
t.identifier('installed'),
|
|
185
|
+
t.booleanLiteral(true)
|
|
186
|
+
),
|
|
187
|
+
t.objectProperty(
|
|
188
|
+
t.identifier('version'),
|
|
189
|
+
t.stringLiteral(PLUGIN_VERSION)
|
|
190
|
+
),
|
|
191
|
+
])
|
|
192
|
+
)
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
// Check if marker already exists
|
|
196
|
+
const hasMarker = path.node.body.some(
|
|
197
|
+
(node) =>
|
|
198
|
+
t.isExpressionStatement(node) &&
|
|
199
|
+
t.isAssignmentExpression(node.expression) &&
|
|
200
|
+
t.isMemberExpression(node.expression.left) &&
|
|
201
|
+
t.isIdentifier(node.expression.left.property, {
|
|
202
|
+
name: '__RPP_INSTALLED__',
|
|
203
|
+
})
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
if (!hasMarker) {
|
|
207
|
+
path.unshiftContainer('body', marker);
|
|
208
|
+
}
|
|
209
|
+
state.markerInjected = true;
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
// Track imports from react-native-reanimated
|
|
215
|
+
ImportDeclaration(path, state) {
|
|
216
|
+
const source = path.node.source.value;
|
|
217
|
+
if (source !== 'react-native-reanimated') {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
for (const specifier of path.node.specifiers) {
|
|
222
|
+
if (t.isImportSpecifier(specifier)) {
|
|
223
|
+
const imported = t.isIdentifier(specifier.imported)
|
|
224
|
+
? specifier.imported.name
|
|
225
|
+
: specifier.imported.value;
|
|
226
|
+
const local = specifier.local.name;
|
|
227
|
+
|
|
228
|
+
if (INSTRUMENTED_FACTORIES.has(imported)) {
|
|
229
|
+
state.factoryBindings.set(local, imported);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
// Transform animation factory calls to track them
|
|
236
|
+
// Only track TOP-LEVEL animations (assigned to .value) to avoid noise
|
|
237
|
+
// Nested animations (withRepeat(withTiming(...))) share the same SharedValue
|
|
238
|
+
CallExpression: {
|
|
239
|
+
exit(path, state) {
|
|
240
|
+
const callee = path.node.callee;
|
|
241
|
+
|
|
242
|
+
if (!t.isIdentifier(callee)) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const localName = callee.name;
|
|
247
|
+
const originalName = state.factoryBindings.get(localName);
|
|
248
|
+
|
|
249
|
+
if (!originalName || !INSTRUMENTED_FACTORIES.has(originalName)) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Check if this is a top-level animation being assigned to a SharedValue
|
|
254
|
+
// Pattern: sharedValue.value = withAnimation(...)
|
|
255
|
+
// Only track these - nested animations are just configuration
|
|
256
|
+
let sharedValueRef: t.Expression | null = null;
|
|
257
|
+
let sharedValueName: string | null = null;
|
|
258
|
+
const parentAssignment = path.findParent((p) => p.isAssignmentExpression());
|
|
259
|
+
|
|
260
|
+
if (parentAssignment && t.isAssignmentExpression(parentAssignment.node)) {
|
|
261
|
+
const left = parentAssignment.node.left;
|
|
262
|
+
// Check if left side is X.value (MemberExpression with property 'value')
|
|
263
|
+
if (
|
|
264
|
+
t.isMemberExpression(left) &&
|
|
265
|
+
t.isIdentifier(left.property, { name: 'value' })
|
|
266
|
+
) {
|
|
267
|
+
// Check if this call is the direct right-hand side of the assignment
|
|
268
|
+
// (not a nested animation inside another)
|
|
269
|
+
if (parentAssignment.node.right === path.node) {
|
|
270
|
+
sharedValueRef = left.object as t.Expression;
|
|
271
|
+
// Try to get the variable name for better descriptions
|
|
272
|
+
if (t.isIdentifier(left.object)) {
|
|
273
|
+
sharedValueName = left.object.name;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Only track if this is assigned to a SharedValue (top-level)
|
|
280
|
+
if (!sharedValueRef) {
|
|
281
|
+
return; // Skip nested animations - they don't need separate tracking
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const animationType = INSTRUMENTED_FACTORIES.get(originalName)!;
|
|
285
|
+
const loc = path.node.loc;
|
|
286
|
+
|
|
287
|
+
// Get relative filename
|
|
288
|
+
const filename = state.filename
|
|
289
|
+
.replace(/^.*\/node_modules\//, 'node_modules/')
|
|
290
|
+
.replace(/^.*\/packages\//, 'packages/');
|
|
291
|
+
|
|
292
|
+
// Extract animation config from arguments (recursively for nested animations)
|
|
293
|
+
const configMeta = buildConfigMetadata(
|
|
294
|
+
animationType,
|
|
295
|
+
path.node.arguments as t.Node[],
|
|
296
|
+
t
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
// Create metadata object
|
|
300
|
+
const metadataProperties: t.ObjectProperty[] = [
|
|
301
|
+
t.objectProperty(
|
|
302
|
+
t.identifier('__rpp_type'),
|
|
303
|
+
t.stringLiteral(animationType)
|
|
304
|
+
),
|
|
305
|
+
t.objectProperty(
|
|
306
|
+
t.identifier('__rpp_file'),
|
|
307
|
+
t.stringLiteral(filename)
|
|
308
|
+
),
|
|
309
|
+
t.objectProperty(
|
|
310
|
+
t.identifier('__rpp_line'),
|
|
311
|
+
t.numericLiteral(loc?.start.line || 0)
|
|
312
|
+
),
|
|
313
|
+
t.objectProperty(
|
|
314
|
+
t.identifier('__rpp_column'),
|
|
315
|
+
t.numericLiteral(loc?.start.column || 0)
|
|
316
|
+
),
|
|
317
|
+
t.objectProperty(
|
|
318
|
+
t.identifier('__rpp_id'),
|
|
319
|
+
t.callExpression(
|
|
320
|
+
t.memberExpression(t.identifier('Math'), t.identifier('random')),
|
|
321
|
+
[]
|
|
322
|
+
)
|
|
323
|
+
),
|
|
324
|
+
t.objectProperty(
|
|
325
|
+
t.identifier('__rpp_config'),
|
|
326
|
+
configMeta
|
|
327
|
+
),
|
|
328
|
+
t.objectProperty(
|
|
329
|
+
t.identifier('__rpp_sharedValue'),
|
|
330
|
+
sharedValueRef
|
|
331
|
+
),
|
|
332
|
+
];
|
|
333
|
+
|
|
334
|
+
// Add SharedValue variable name if we captured it
|
|
335
|
+
if (sharedValueName) {
|
|
336
|
+
metadataProperties.push(
|
|
337
|
+
t.objectProperty(
|
|
338
|
+
t.identifier('__rpp_sharedValueName'),
|
|
339
|
+
t.stringLiteral(sharedValueName)
|
|
340
|
+
)
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const metadata = t.objectExpression(metadataProperties);
|
|
345
|
+
|
|
346
|
+
// Wrap the call: __rppTrack(withTiming(...), metadata)
|
|
347
|
+
const wrappedCall = t.callExpression(
|
|
348
|
+
t.identifier('__rppTrack'),
|
|
349
|
+
[
|
|
350
|
+
t.callExpression(callee, path.node.arguments as t.Expression[]),
|
|
351
|
+
metadata,
|
|
352
|
+
]
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
// Inject the tracker function if not already done
|
|
356
|
+
if (!state.registryInjected) {
|
|
357
|
+
injectTrackerFunction(path, t);
|
|
358
|
+
state.registryInjected = true;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
path.replaceWith(wrappedCall);
|
|
362
|
+
path.skip(); // Don't revisit this node
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Inject the __rppTrack function that registers animations
|
|
371
|
+
*/
|
|
372
|
+
function injectTrackerFunction(
|
|
373
|
+
path: NodePath<t.CallExpression>,
|
|
374
|
+
t: typeof import('@babel/core').types
|
|
375
|
+
): void {
|
|
376
|
+
const program = path.findParent((p) => p.isProgram()) as NodePath<t.Program>;
|
|
377
|
+
if (!program) return;
|
|
378
|
+
|
|
379
|
+
// Check if already injected
|
|
380
|
+
const hasTracker = program.node.body.some(
|
|
381
|
+
(node) =>
|
|
382
|
+
t.isVariableDeclaration(node) &&
|
|
383
|
+
node.declarations.some(
|
|
384
|
+
(d) => t.isIdentifier(d.id) && d.id.name === '__rppTrack'
|
|
385
|
+
)
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
if (hasTracker) return;
|
|
389
|
+
|
|
390
|
+
// Create the tracker function
|
|
391
|
+
// const __rppTrack = (animation, meta) => {
|
|
392
|
+
// if (animation && typeof animation === 'object') {
|
|
393
|
+
// animation.__rpp_meta = meta;
|
|
394
|
+
// }
|
|
395
|
+
// if (globalThis.__RPP_REGISTRY__?.register) {
|
|
396
|
+
// globalThis.__RPP_REGISTRY__.register(meta);
|
|
397
|
+
// }
|
|
398
|
+
// return animation;
|
|
399
|
+
// };
|
|
400
|
+
const tracker = t.variableDeclaration('const', [
|
|
401
|
+
t.variableDeclarator(
|
|
402
|
+
t.identifier('__rppTrack'),
|
|
403
|
+
t.arrowFunctionExpression(
|
|
404
|
+
[t.identifier('animation'), t.identifier('meta')],
|
|
405
|
+
t.blockStatement([
|
|
406
|
+
// if (animation && typeof animation === 'object') { animation.__rpp_meta = meta; }
|
|
407
|
+
t.ifStatement(
|
|
408
|
+
t.logicalExpression(
|
|
409
|
+
'&&',
|
|
410
|
+
t.identifier('animation'),
|
|
411
|
+
t.binaryExpression(
|
|
412
|
+
'===',
|
|
413
|
+
t.unaryExpression('typeof', t.identifier('animation')),
|
|
414
|
+
t.stringLiteral('object')
|
|
415
|
+
)
|
|
416
|
+
),
|
|
417
|
+
t.expressionStatement(
|
|
418
|
+
t.assignmentExpression(
|
|
419
|
+
'=',
|
|
420
|
+
t.memberExpression(
|
|
421
|
+
t.identifier('animation'),
|
|
422
|
+
t.identifier('__rpp_meta')
|
|
423
|
+
),
|
|
424
|
+
t.identifier('meta')
|
|
425
|
+
)
|
|
426
|
+
)
|
|
427
|
+
),
|
|
428
|
+
// if (globalThis.__RPP_REGISTRY__?.register) { ... }
|
|
429
|
+
t.ifStatement(
|
|
430
|
+
t.optionalMemberExpression(
|
|
431
|
+
t.memberExpression(
|
|
432
|
+
t.identifier('globalThis'),
|
|
433
|
+
t.identifier('__RPP_REGISTRY__')
|
|
434
|
+
),
|
|
435
|
+
t.identifier('register'),
|
|
436
|
+
false,
|
|
437
|
+
true
|
|
438
|
+
),
|
|
439
|
+
t.expressionStatement(
|
|
440
|
+
t.callExpression(
|
|
441
|
+
t.memberExpression(
|
|
442
|
+
t.memberExpression(
|
|
443
|
+
t.identifier('globalThis'),
|
|
444
|
+
t.identifier('__RPP_REGISTRY__')
|
|
445
|
+
),
|
|
446
|
+
t.identifier('register')
|
|
447
|
+
),
|
|
448
|
+
[t.identifier('meta')]
|
|
449
|
+
)
|
|
450
|
+
)
|
|
451
|
+
),
|
|
452
|
+
// return animation;
|
|
453
|
+
t.returnStatement(t.identifier('animation')),
|
|
454
|
+
])
|
|
455
|
+
)
|
|
456
|
+
),
|
|
457
|
+
]);
|
|
458
|
+
|
|
459
|
+
// Find insertion point (after imports)
|
|
460
|
+
let insertIndex = 0;
|
|
461
|
+
for (let i = 0; i < program.node.body.length; i++) {
|
|
462
|
+
const node = program.node.body[i];
|
|
463
|
+
if (!t.isImportDeclaration(node)) {
|
|
464
|
+
// Skip the marker if present
|
|
465
|
+
if (
|
|
466
|
+
t.isExpressionStatement(node) &&
|
|
467
|
+
t.isAssignmentExpression(node.expression) &&
|
|
468
|
+
t.isMemberExpression(node.expression.left) &&
|
|
469
|
+
t.isIdentifier(node.expression.left.property, {
|
|
470
|
+
name: '__RPP_INSTALLED__',
|
|
471
|
+
})
|
|
472
|
+
) {
|
|
473
|
+
insertIndex = i + 1;
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
insertIndex = i;
|
|
477
|
+
break;
|
|
478
|
+
}
|
|
479
|
+
insertIndex = i + 1;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
program.node.body.splice(insertIndex, 0, tracker);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
module.exports = reanimatedPausePlugin;
|
|
486
|
+
module.exports.default = reanimatedPausePlugin;
|
package/src/global.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Native global development flag
|
|
3
|
+
*/
|
|
4
|
+
declare const __DEV__: boolean;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Extend globalThis for worklet context (UI thread has requestAnimationFrame)
|
|
8
|
+
*/
|
|
9
|
+
declare namespace globalThis {
|
|
10
|
+
var requestAnimationFrame: ((callback: (timestamp: number) => void) => number) | undefined;
|
|
11
|
+
}
|