@keepsnappy/react-native 0.0.0-stage → 0.1.0-alpha.11
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 +260 -2
- package/android/build.gradle +35 -0
- package/android/src/main/java/expo/modules/perflab/PerfLabModule.kt +445 -0
- package/android/src/main/java/expo/modules/perflab/PerfLabSampler.kt +515 -0
- package/app.plugin.js +105 -0
- package/expo-module.config.json +9 -0
- package/ios/PerfLab.podspec +29 -0
- package/ios/PerfLabHermes.h +18 -0
- package/ios/PerfLabHermes.mm +64 -0
- package/ios/PerfLabModule.swift +371 -0
- package/ios/PerfLabSampler.swift +364 -0
- package/js/babel-transformer.js +71 -0
- package/js/hook.polyfill.js +333 -0
- package/js/renderer-switch.js +16 -0
- package/js/sdk/activation.ts +375 -0
- package/js/sdk/active.ts +62 -0
- package/js/sdk/artifacts.ts +632 -0
- package/js/sdk/boot.ts +21 -0
- package/js/sdk/causes.ts +932 -0
- package/js/sdk/core.ts +2403 -0
- package/js/sdk/cpuprof.ts +133 -0
- package/js/sdk/dormant.ts +77 -0
- package/js/sdk/env.d.ts +2 -0
- package/js/sdk/fiber.ts +591 -0
- package/js/sdk/interactions.ts +531 -0
- package/js/sdk/links.ts +201 -0
- package/js/sdk/nav.ts +383 -0
- package/js/sdk/net-install.ts +49 -0
- package/js/sdk/net.ts +684 -0
- package/js/sdk/profile-export.ts +257 -0
- package/js/sdk/profile.ts +539 -0
- package/js/sdk/push-install.ts +158 -0
- package/js/sdk/push.ts +819 -0
- package/js/sdk/retain-floor.ts +11 -0
- package/js/sdk/sampler.ts +236 -0
- package/js/sdk/screens.ts +586 -0
- package/js/sdk/stream.ts +125 -0
- package/js/zustand-vanilla.js +31 -0
- package/metro.js +238 -0
- package/package.json +26 -4
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
// perf-lab React DevTools hook, installed as a Metro polyfill: it runs before
|
|
2
|
+
// any module, so it exists before the React renderer evaluates and injects.
|
|
3
|
+
// A polyfill is a plain script (no require), so this file stays self-contained:
|
|
4
|
+
// it only counts and buffers. The SDK boot module attaches a listener later.
|
|
5
|
+
//
|
|
6
|
+
// Dormant builds (EXPO_PUBLIC_PERF_LAB_MODE=dormant) first read the activation
|
|
7
|
+
// record through the native module, synchronously (Expo installs
|
|
8
|
+
// `expo.modules` before the bundle runs; on Android the polyfill installs them itself). Not activated: nothing is installed
|
|
9
|
+
// and the React DevTools global stays exactly as without the SDK. If the native
|
|
10
|
+
// module cannot be reached yet, the hook is prepared but not installed and the
|
|
11
|
+
// boot module decides (it still runs before the renderer loads).
|
|
12
|
+
//
|
|
13
|
+
// `profile` on __PERFLAB_ACTIVATION__ picks the React renderer in a
|
|
14
|
+
// PERF_LAB_REACT_PROFILING=switch bundle (js/renderer-switch.js): the
|
|
15
|
+
// activation's `profile` in a dormant build, EXPO_PUBLIC_PERF_LAB_PROFILE otherwise.
|
|
16
|
+
(function (g) {
|
|
17
|
+
if (g.__PERFLAB_HOOK__ || g.__PERFLAB_ACTIVATION__) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
var HOOK = '__REACT_DEVTOOLS_GLOBAL_HOOK__';
|
|
21
|
+
|
|
22
|
+
// process.env.EXPO_PUBLIC_* below are inlined at bundle time by the SDK's
|
|
23
|
+
// transformer; the guard only matters if they were not.
|
|
24
|
+
var env = function (read) {
|
|
25
|
+
try {
|
|
26
|
+
return read();
|
|
27
|
+
} catch (_e) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
var modeEnv = env(function () {
|
|
32
|
+
return (process.env.EXPO_PUBLIC_KEEPSNAPPY_MODE ?? process.env.EXPO_PUBLIC_PERF_LAB_MODE);
|
|
33
|
+
});
|
|
34
|
+
var autoEnv = env(function () {
|
|
35
|
+
return (process.env.EXPO_PUBLIC_KEEPSNAPPY_AUTO ?? process.env.EXPO_PUBLIC_PERF_LAB_AUTO);
|
|
36
|
+
});
|
|
37
|
+
var profileEnv = env(function () {
|
|
38
|
+
return (process.env.EXPO_PUBLIC_KEEPSNAPPY_PROFILE ?? process.env.EXPO_PUBLIC_PERF_LAB_PROFILE);
|
|
39
|
+
});
|
|
40
|
+
var MODE =
|
|
41
|
+
modeEnv === 'dormant' || modeEnv === 'auto'
|
|
42
|
+
? modeEnv
|
|
43
|
+
: autoEnv === '1'
|
|
44
|
+
? 'auto'
|
|
45
|
+
: 'manual';
|
|
46
|
+
|
|
47
|
+
// Same rule as decide() in js/sdk/activation.ts: null when not active.
|
|
48
|
+
var activeRecord = function (raw, nowMs) {
|
|
49
|
+
if (typeof raw !== 'string' || raw === '') {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
var record = JSON.parse(raw);
|
|
54
|
+
return !!record &&
|
|
55
|
+
record.active === true &&
|
|
56
|
+
typeof record.expiresAt === 'number' &&
|
|
57
|
+
record.expiresAt > nowMs
|
|
58
|
+
? record
|
|
59
|
+
: null;
|
|
60
|
+
} catch (_e) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
var activation = {
|
|
66
|
+
mode: MODE,
|
|
67
|
+
read: 'none',
|
|
68
|
+
raw: null,
|
|
69
|
+
active: MODE !== 'dormant',
|
|
70
|
+
profile: MODE !== 'dormant' && profileEnv === '1',
|
|
71
|
+
t: Date.now(),
|
|
72
|
+
};
|
|
73
|
+
if (MODE === 'dormant') {
|
|
74
|
+
try {
|
|
75
|
+
if (!g.expo) {
|
|
76
|
+
// Android installs `expo.modules` only when expo-modules-core first loads, after this
|
|
77
|
+
// polyfill. Run the same synchronous install it runs (ensureNativeModulesAreInstalled).
|
|
78
|
+
var core =
|
|
79
|
+
(typeof g.__turboModuleProxy === 'function' && g.__turboModuleProxy('ExpoModulesCore')) ||
|
|
80
|
+
(g.nativeModuleProxy && g.nativeModuleProxy.ExpoModulesCore);
|
|
81
|
+
if (core && typeof core.installModules === 'function') {
|
|
82
|
+
var installStart = Date.now();
|
|
83
|
+
core.installModules();
|
|
84
|
+
activation.installMs = Date.now() - installStart;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
var modules = g.expo && g.expo.modules;
|
|
88
|
+
var native = modules ? modules.PerfLab : undefined;
|
|
89
|
+
if (native && typeof native.activationRead === 'function') {
|
|
90
|
+
activation.raw = native.activationRead();
|
|
91
|
+
activation.read = 'polyfill';
|
|
92
|
+
var record = activeRecord(activation.raw, activation.t);
|
|
93
|
+
activation.active = record !== null;
|
|
94
|
+
activation.profile = record !== null && record.profile === true;
|
|
95
|
+
} else {
|
|
96
|
+
activation.read = 'deferred';
|
|
97
|
+
}
|
|
98
|
+
} catch (_e) {
|
|
99
|
+
activation.read = 'deferred';
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
g.__PERFLAB_ACTIVATION__ = activation;
|
|
103
|
+
if (MODE === 'dormant' && activation.read === 'polyfill' && !activation.active) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
var MAX_ERRORS = 3;
|
|
107
|
+
|
|
108
|
+
var clock = function () {
|
|
109
|
+
if (typeof g.nativePerformanceNow === 'function') {
|
|
110
|
+
return g.nativePerformanceNow();
|
|
111
|
+
}
|
|
112
|
+
if (g.performance && typeof g.performance.now === 'function') {
|
|
113
|
+
return g.performance.now();
|
|
114
|
+
}
|
|
115
|
+
return Date.now();
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
var state = {
|
|
119
|
+
installedAt: clock(),
|
|
120
|
+
typeofHookAtPolyfill: typeof g[HOOK],
|
|
121
|
+
// Metro's prelude declares __DEV__ as a global var before any polyfill.
|
|
122
|
+
dev: g.__DEV__ === true,
|
|
123
|
+
mode: 'none',
|
|
124
|
+
chainedTargets: 0,
|
|
125
|
+
replacedAfterInstall: 0,
|
|
126
|
+
injections: [],
|
|
127
|
+
commitsByRenderer: {},
|
|
128
|
+
unmountsByRenderer: {},
|
|
129
|
+
lastRootByRenderer: {},
|
|
130
|
+
errors: 0,
|
|
131
|
+
invalid: false,
|
|
132
|
+
listener: null,
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
var log = function (message) {
|
|
136
|
+
try {
|
|
137
|
+
if (g.console && typeof g.console.log === 'function') {
|
|
138
|
+
g.console.log('PERF_LAB ' + message);
|
|
139
|
+
}
|
|
140
|
+
} catch (_e) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
var fail = function (where, error) {
|
|
146
|
+
state.errors++;
|
|
147
|
+
log('hook listener error where=' + where + ' ' + String(error));
|
|
148
|
+
if (state.errors >= MAX_ERRORS) {
|
|
149
|
+
state.listener = null;
|
|
150
|
+
state.invalid = true;
|
|
151
|
+
log('hook listener detached after ' + state.errors + ' errors');
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
var recordInject = function (id, internals) {
|
|
156
|
+
var entry = {
|
|
157
|
+
id: id,
|
|
158
|
+
pkg: internals && internals.rendererPackageName,
|
|
159
|
+
version: internals && internals.version,
|
|
160
|
+
bundleType: internals && internals.bundleType,
|
|
161
|
+
t: clock(),
|
|
162
|
+
};
|
|
163
|
+
state.injections.push(entry);
|
|
164
|
+
state.commitsByRenderer[id] = 0;
|
|
165
|
+
state.unmountsByRenderer[id] = 0;
|
|
166
|
+
log(
|
|
167
|
+
'hook inject id=' +
|
|
168
|
+
id +
|
|
169
|
+
' renderer=' +
|
|
170
|
+
entry.pkg +
|
|
171
|
+
' version=' +
|
|
172
|
+
entry.version +
|
|
173
|
+
' bundleType=' +
|
|
174
|
+
entry.bundleType +
|
|
175
|
+
' mode=' +
|
|
176
|
+
state.mode +
|
|
177
|
+
' t=' +
|
|
178
|
+
entry.t.toFixed(3) +
|
|
179
|
+
' hookInstalledAt=' +
|
|
180
|
+
state.installedAt.toFixed(3),
|
|
181
|
+
);
|
|
182
|
+
if (state.listener && state.listener.onInject) {
|
|
183
|
+
try {
|
|
184
|
+
state.listener.onInject(id, internals);
|
|
185
|
+
} catch (e) {
|
|
186
|
+
fail('inject', e);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
var recordCommit = function (id, root) {
|
|
192
|
+
state.commitsByRenderer[id] = (state.commitsByRenderer[id] || 0) + 1;
|
|
193
|
+
state.lastRootByRenderer[id] = root;
|
|
194
|
+
if (state.listener) {
|
|
195
|
+
try {
|
|
196
|
+
state.listener.onCommit(id, root);
|
|
197
|
+
} catch (e) {
|
|
198
|
+
fail('commit', e);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
var recordUnmount = function (id, fiber) {
|
|
204
|
+
state.unmountsByRenderer[id] = (state.unmountsByRenderer[id] || 0) + 1;
|
|
205
|
+
if (state.listener && state.listener.onUnmount) {
|
|
206
|
+
try {
|
|
207
|
+
state.listener.onUnmount(id, fiber);
|
|
208
|
+
} catch (e) {
|
|
209
|
+
fail('unmount', e);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
var nextId = 1;
|
|
215
|
+
var noop = function () {
|
|
216
|
+
return undefined;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
var ownHook = {
|
|
220
|
+
supportsFiber: true,
|
|
221
|
+
isDisabled: false,
|
|
222
|
+
renderers: new Map(),
|
|
223
|
+
__perflab: true,
|
|
224
|
+
inject: function (internals) {
|
|
225
|
+
var id = nextId++;
|
|
226
|
+
ownHook.renderers.set(id, internals);
|
|
227
|
+
recordInject(id, internals);
|
|
228
|
+
return id;
|
|
229
|
+
},
|
|
230
|
+
onCommitFiberRoot: function (id, root) {
|
|
231
|
+
recordCommit(id, root);
|
|
232
|
+
},
|
|
233
|
+
onCommitFiberUnmount: function (id, fiber) {
|
|
234
|
+
recordUnmount(id, fiber);
|
|
235
|
+
},
|
|
236
|
+
onPostCommitFiberRoot: noop,
|
|
237
|
+
setStrictMode: noop,
|
|
238
|
+
checkDCE: noop,
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// Wrap a hook someone else installed (React DevTools, bippy, ...): theirs runs
|
|
242
|
+
// first, ours after, and the renderer id stays theirs.
|
|
243
|
+
var chain = function (hook) {
|
|
244
|
+
if (!hook || hook.__perflab || hook.__perflabChained) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
var origInject = hook.inject;
|
|
248
|
+
var origCommit = hook.onCommitFiberRoot;
|
|
249
|
+
var origUnmount = hook.onCommitFiberUnmount;
|
|
250
|
+
hook.inject = function (internals) {
|
|
251
|
+
var id = origInject ? origInject.apply(hook, arguments) : nextId++;
|
|
252
|
+
recordInject(id, internals);
|
|
253
|
+
return id;
|
|
254
|
+
};
|
|
255
|
+
hook.onCommitFiberRoot = function (id, root) {
|
|
256
|
+
var result = origCommit ? origCommit.apply(hook, arguments) : undefined;
|
|
257
|
+
recordCommit(id, root);
|
|
258
|
+
return result;
|
|
259
|
+
};
|
|
260
|
+
hook.onCommitFiberUnmount = function (id, fiber) {
|
|
261
|
+
var result = origUnmount ? origUnmount.apply(hook, arguments) : undefined;
|
|
262
|
+
recordUnmount(id, fiber);
|
|
263
|
+
return result;
|
|
264
|
+
};
|
|
265
|
+
if (hook.renderers && typeof hook.renderers.forEach === 'function') {
|
|
266
|
+
hook.renderers.forEach(function (internals, id) {
|
|
267
|
+
recordInject(id, internals);
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
hook.__perflabChained = true;
|
|
271
|
+
state.chainedTargets++;
|
|
272
|
+
state.mode = 'chained';
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
var installOwn = function () {
|
|
276
|
+
var current = ownHook;
|
|
277
|
+
try {
|
|
278
|
+
// A later installer that replaces the global gets wrapped, not lost.
|
|
279
|
+
Object.defineProperty(g, HOOK, {
|
|
280
|
+
configurable: true,
|
|
281
|
+
enumerable: false,
|
|
282
|
+
get: function () {
|
|
283
|
+
return current;
|
|
284
|
+
},
|
|
285
|
+
set: function (next) {
|
|
286
|
+
if (next && next !== current) {
|
|
287
|
+
state.replacedAfterInstall++;
|
|
288
|
+
chain(next);
|
|
289
|
+
current = next;
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
} catch (_e) {
|
|
294
|
+
g[HOOK] = ownHook;
|
|
295
|
+
}
|
|
296
|
+
state.mode = 'own';
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
// Idempotent: installs our hook if none exists, chains the one that does.
|
|
300
|
+
var ensure = function () {
|
|
301
|
+
var existing = g[HOOK];
|
|
302
|
+
if (existing === undefined || existing === null) {
|
|
303
|
+
installOwn();
|
|
304
|
+
} else if (!existing.__perflab) {
|
|
305
|
+
chain(existing);
|
|
306
|
+
}
|
|
307
|
+
return state.mode;
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
// In dev, InitializeCore installs the real DevTools hook later; installing
|
|
311
|
+
// ours first would make it bail out. The boot module calls ensure() then.
|
|
312
|
+
// A dormant build whose activation is not known yet waits for boot as well.
|
|
313
|
+
if (!state.dev && activation.active) {
|
|
314
|
+
ensure();
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
g.__PERFLAB_HOOK__ = {
|
|
318
|
+
state: state,
|
|
319
|
+
ensure: ensure,
|
|
320
|
+
attach: function (listener) {
|
|
321
|
+
state.listener = listener;
|
|
322
|
+
},
|
|
323
|
+
now: clock,
|
|
324
|
+
};
|
|
325
|
+
})(
|
|
326
|
+
typeof globalThis !== 'undefined'
|
|
327
|
+
? globalThis
|
|
328
|
+
: typeof global !== 'undefined'
|
|
329
|
+
? global
|
|
330
|
+
: typeof window !== 'undefined'
|
|
331
|
+
? window
|
|
332
|
+
: this,
|
|
333
|
+
);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// React Native's production renderer in a PERF_LAB_REACT_PROFILING=switch bundle.
|
|
2
|
+
// Both renderers are in the bundle; this module picks one when React Native
|
|
3
|
+
// first requires it, from what hook.polyfill.js (or the boot module) read out
|
|
4
|
+
// of the activation record before React loaded. withPerfLab (metro.js)
|
|
5
|
+
// resolves the two module names below to RN's own implementation files.
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
var activation = globalThis.__PERFLAB_ACTIVATION__;
|
|
9
|
+
var profiling = !!activation && activation.profile === true;
|
|
10
|
+
if (activation) {
|
|
11
|
+
activation.renderer = profiling ? 'profiling' : 'prod';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = profiling
|
|
15
|
+
? require('@keepsnappy/react-native/renderer/ReactFabric-profiling')
|
|
16
|
+
: require('@keepsnappy/react-native/renderer/ReactFabric-prod');
|