react-jitter 0.3.1 → 0.5.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 +65 -0
- package/package.json +1 -1
- package/plugin-swc/swc_plugin_react_jitter.wasm +0 -0
- package/runtime/dist/index.d.mts +5 -0
- package/runtime/dist/index.d.ts +5 -0
- package/runtime/dist/index.js +47 -12
- package/runtime/dist/index.mjs +47 -12
package/README.md
CHANGED
|
@@ -128,6 +128,44 @@ window.reactJitter.onRender = (render) => {
|
|
|
128
128
|
|
|
129
129
|
Modern bundlers will tree-shake the `import` and the function call from your production build, so it will have zero performance impact.
|
|
130
130
|
|
|
131
|
+
### Advanced: Custom Comparator Selection
|
|
132
|
+
|
|
133
|
+
By default, React Jitter uses the `deepEqual` comparator to detect changes in hook values. However, you can customize which comparator is used on a per-hook basis using the `selectComparator` function. This is useful when dealing with circular data structures or when you need different comparison strategies for different hooks.
|
|
134
|
+
|
|
135
|
+
```js
|
|
136
|
+
// Set a custom comparator selector
|
|
137
|
+
window.reactJitter.selectComparator = (hookAddress) => {
|
|
138
|
+
// Use circularDeepEqual for hooks that might return circular structures
|
|
139
|
+
if (hookAddress.hook === 'useSelector' || hookAddress.hook === 'useReduxState') {
|
|
140
|
+
return 'circularDeepEqual';
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Use deepEqual for everything else (default)
|
|
144
|
+
return 'deepEqual';
|
|
145
|
+
};
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The `hookAddress` parameter contains information about the hook:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
{
|
|
152
|
+
hook: string; // Hook name, e.g., "useState", "useContext"
|
|
153
|
+
file: string; // File path where the hook is called
|
|
154
|
+
line: number; // Line number
|
|
155
|
+
offset: number; // Column offset
|
|
156
|
+
arguments?: string[]; // Hook arguments (if includeArguments is enabled)
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Available Comparators:**
|
|
161
|
+
|
|
162
|
+
- `deepEqual` (default): Fast deep equality check that handles most cases. Will throw an error if it encounters deeply nested or circular structures.
|
|
163
|
+
- `circularDeepEqual`: Slower but handles circular references safely. Use this when your hooks return data with circular dependencies or extremely deep nesting.
|
|
164
|
+
|
|
165
|
+
**When to Use `circularDeepEqual`:**
|
|
166
|
+
|
|
167
|
+
If you see an error like "Maximum call stack size exceeded. Please use the 'circularDeepEqual' comparator", you should configure `selectComparator` to return `'circularDeepEqual'` for the specific hook mentioned in the error message.
|
|
168
|
+
|
|
131
169
|
## API and Configuration
|
|
132
170
|
|
|
133
171
|
The `reactJitter` function accepts a configuration object with two callbacks: `onHookChange` and `onRender`.
|
|
@@ -218,6 +256,33 @@ Here is an example of the `change` object when `includeArguments` is enabled:
|
|
|
218
256
|
|
|
219
257
|
In this example, the `arguments` field shows that the `UserContext` was used, and the `changedKeys` field shows that the `user` property has changed.
|
|
220
258
|
|
|
259
|
+
### Detecting Unstable Hooks in Unit Tests
|
|
260
|
+
|
|
261
|
+
React Jitter can also be a powerful tool for improving code quality within your unit tests.
|
|
262
|
+
|
|
263
|
+
You can leverage this to write tests that fail if a hook becomes unstable, catching performance regressions early in the a testing setup where you might initialize React Jitter in a global setup file, you can easily override the `onHookChange` handler on a per-test basis.
|
|
264
|
+
|
|
265
|
+
```javascript
|
|
266
|
+
// Example of a Vitest/Jest test
|
|
267
|
+
it('should not have unstable hooks', () => {
|
|
268
|
+
const unstableChanges = [];
|
|
269
|
+
// Initialize React Jitter in a global setup file (e.g., setupTests.js)
|
|
270
|
+
// Then, override the onHookChange handler for specific tests.
|
|
271
|
+
window.reactJitter.onHookChange = (change) => {
|
|
272
|
+
// You can ignore mocked hooks or handle them specifically
|
|
273
|
+
if (change.unstable && !change.isMocked) {
|
|
274
|
+
unstableChanges.push(change);
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
render(<MyComponent />);
|
|
279
|
+
|
|
280
|
+
// Assert that no unstable values were detected during the render
|
|
281
|
+
expect(unstableChanges).toHaveLength(0);
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
The `onHookChange` callback's `change` object includes an `isMocked` boolean property. This is automatically set to `true` if React Jitter detects that the hook has been mocked (e.g., using `jest.fn()` or `vi.fn()`). This allows you to reliably identify and assert against unstable values in your test environment.
|
|
221
286
|
|
|
222
287
|
## How It Works
|
|
223
288
|
|
package/package.json
CHANGED
|
Binary file
|
package/runtime/dist/index.d.mts
CHANGED
|
@@ -40,7 +40,9 @@ type HookEndEvent = {
|
|
|
40
40
|
line: number;
|
|
41
41
|
offset: number;
|
|
42
42
|
arguments?: string[];
|
|
43
|
+
isMocked?: boolean;
|
|
43
44
|
};
|
|
45
|
+
type HookAddress = Pick<HookEndEvent, 'hook' | 'file' | 'line' | 'offset' | 'arguments'>;
|
|
44
46
|
type ReactJitterOptions = {
|
|
45
47
|
enabled?: boolean;
|
|
46
48
|
onHookChange?: (change: HookChange) => void;
|
|
@@ -51,6 +53,7 @@ type ReactJitterOptions = {
|
|
|
51
53
|
}) => void;
|
|
52
54
|
};
|
|
53
55
|
type Scope = z.infer<typeof ScopeSchema>;
|
|
56
|
+
type Comparator = 'deepEqual' | 'circularDeepEqual';
|
|
54
57
|
|
|
55
58
|
type HookCall = HookChange & HookEndEvent & {
|
|
56
59
|
scope: Scope;
|
|
@@ -62,6 +65,7 @@ declare global {
|
|
|
62
65
|
reactJitter?: {
|
|
63
66
|
enabled?: boolean;
|
|
64
67
|
onHookChange?: (change: HookCall) => void;
|
|
68
|
+
selectComparator?: (hookAddress: HookAddress) => Comparator;
|
|
65
69
|
onRender?: (scope: Scope & {
|
|
66
70
|
hookResults: Record<string, unknown>;
|
|
67
71
|
renderCount: number;
|
|
@@ -79,6 +83,7 @@ declare function useJitterScope(scope: Scope): {
|
|
|
79
83
|
s: (id: string) => void;
|
|
80
84
|
e: (hookResult: unknown, hookEndEvent: HookEndEvent) => unknown;
|
|
81
85
|
re: <T>(renderResult: T) => T;
|
|
86
|
+
m: (value: unknown) => boolean;
|
|
82
87
|
};
|
|
83
88
|
declare function reactJitter(options: ReactJitterOptions): void;
|
|
84
89
|
|
package/runtime/dist/index.d.ts
CHANGED
|
@@ -40,7 +40,9 @@ type HookEndEvent = {
|
|
|
40
40
|
line: number;
|
|
41
41
|
offset: number;
|
|
42
42
|
arguments?: string[];
|
|
43
|
+
isMocked?: boolean;
|
|
43
44
|
};
|
|
45
|
+
type HookAddress = Pick<HookEndEvent, 'hook' | 'file' | 'line' | 'offset' | 'arguments'>;
|
|
44
46
|
type ReactJitterOptions = {
|
|
45
47
|
enabled?: boolean;
|
|
46
48
|
onHookChange?: (change: HookChange) => void;
|
|
@@ -51,6 +53,7 @@ type ReactJitterOptions = {
|
|
|
51
53
|
}) => void;
|
|
52
54
|
};
|
|
53
55
|
type Scope = z.infer<typeof ScopeSchema>;
|
|
56
|
+
type Comparator = 'deepEqual' | 'circularDeepEqual';
|
|
54
57
|
|
|
55
58
|
type HookCall = HookChange & HookEndEvent & {
|
|
56
59
|
scope: Scope;
|
|
@@ -62,6 +65,7 @@ declare global {
|
|
|
62
65
|
reactJitter?: {
|
|
63
66
|
enabled?: boolean;
|
|
64
67
|
onHookChange?: (change: HookCall) => void;
|
|
68
|
+
selectComparator?: (hookAddress: HookAddress) => Comparator;
|
|
65
69
|
onRender?: (scope: Scope & {
|
|
66
70
|
hookResults: Record<string, unknown>;
|
|
67
71
|
renderCount: number;
|
|
@@ -79,6 +83,7 @@ declare function useJitterScope(scope: Scope): {
|
|
|
79
83
|
s: (id: string) => void;
|
|
80
84
|
e: (hookResult: unknown, hookEndEvent: HookEndEvent) => unknown;
|
|
81
85
|
re: <T>(renderResult: T) => T;
|
|
86
|
+
m: (value: unknown) => boolean;
|
|
82
87
|
};
|
|
83
88
|
declare function reactJitter(options: ReactJitterOptions): void;
|
|
84
89
|
|
package/runtime/dist/index.js
CHANGED
|
@@ -447,7 +447,8 @@ function createCustomEqual(options) {
|
|
|
447
447
|
}
|
|
448
448
|
|
|
449
449
|
// src/utils/getChanges.ts
|
|
450
|
-
function getChanges(prev, next) {
|
|
450
|
+
function getChanges(prev, next, comparator = "deepEqual") {
|
|
451
|
+
const equals = comparator === "circularDeepEqual" ? circularDeepEqual : deepEqual;
|
|
451
452
|
const changedKeys = [];
|
|
452
453
|
const unstableKeys = [];
|
|
453
454
|
const isObject = (v) => v !== null && typeof v === "object";
|
|
@@ -466,7 +467,7 @@ function getChanges(prev, next) {
|
|
|
466
467
|
}
|
|
467
468
|
const max = Math.max(prev.length, next.length);
|
|
468
469
|
for (let i = 0; i < max; i++) {
|
|
469
|
-
const deepEqItem =
|
|
470
|
+
const deepEqItem = equals(prev[i], next[i]);
|
|
470
471
|
const refDiffItem = isObject(prev[i]) && isObject(next[i]) && prev[i] !== next[i];
|
|
471
472
|
if (!deepEqItem || refDiffItem) {
|
|
472
473
|
const key = String(i);
|
|
@@ -479,7 +480,7 @@ function getChanges(prev, next) {
|
|
|
479
480
|
} else if (isObject(prev) && isObject(next)) {
|
|
480
481
|
const allKeys = /* @__PURE__ */ new Set([...Object.keys(prev), ...Object.keys(next)]);
|
|
481
482
|
for (const key of allKeys) {
|
|
482
|
-
const deepEqProp =
|
|
483
|
+
const deepEqProp = equals(prev[key], next[key]);
|
|
483
484
|
const refDiffProp = isObject(prev[key]) && isObject(next[key]) && prev[key] !== next[key];
|
|
484
485
|
if (!deepEqProp || refDiffProp) {
|
|
485
486
|
changedKeys.push(key);
|
|
@@ -489,7 +490,7 @@ function getChanges(prev, next) {
|
|
|
489
490
|
}
|
|
490
491
|
}
|
|
491
492
|
} else {
|
|
492
|
-
const deepEqRoot =
|
|
493
|
+
const deepEqRoot = equals(prev, next);
|
|
493
494
|
const refDiffRoot = isObject(prev) && isObject(next) && prev !== next;
|
|
494
495
|
const unstable = refDiffRoot && deepEqRoot;
|
|
495
496
|
const changed = !deepEqRoot || refDiffRoot;
|
|
@@ -500,7 +501,7 @@ function getChanges(prev, next) {
|
|
|
500
501
|
};
|
|
501
502
|
}
|
|
502
503
|
const isPlainObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
503
|
-
const unstableRoot = isPlainObject(prev) && isPlainObject(next) && prev !== next &&
|
|
504
|
+
const unstableRoot = isPlainObject(prev) && isPlainObject(next) && prev !== next && equals(prev, next);
|
|
504
505
|
if (unstableRoot && changedKeys.length === 0) {
|
|
505
506
|
changedKeys.push("");
|
|
506
507
|
unstableKeys.push("");
|
|
@@ -512,6 +513,31 @@ function getChanges(prev, next) {
|
|
|
512
513
|
};
|
|
513
514
|
}
|
|
514
515
|
|
|
516
|
+
// src/utils/compareChanges.ts
|
|
517
|
+
function compareChanges(hookAddress, prev, current) {
|
|
518
|
+
var _a, _b, _c;
|
|
519
|
+
if (prev !== "undefined" && prev !== current) {
|
|
520
|
+
const comparator = (_c = (_b = (_a = window == null ? void 0 : window.reactJitter) == null ? void 0 : _a.selectComparator) == null ? void 0 : _b.call(_a, hookAddress)) != null ? _c : "deepEqual";
|
|
521
|
+
try {
|
|
522
|
+
return getChanges(prev, current, comparator);
|
|
523
|
+
} catch (error) {
|
|
524
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
525
|
+
const isRecursionError = /(?:maximum call stack(?: size)? exceeded|too much recursion|stack overflow)/i.test(
|
|
526
|
+
errorMessage
|
|
527
|
+
);
|
|
528
|
+
if (isRecursionError && comparator !== "circularDeepEqual") {
|
|
529
|
+
throw new Error(
|
|
530
|
+
`Maximum call stack size exceeded. Please use the "circularDeepEqual" comparator with selectComparator option.
|
|
531
|
+
Hook address: ${JSON.stringify(hookAddress, null, 2)}.`,
|
|
532
|
+
{ cause: error }
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
throw error;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
|
|
515
541
|
// src/index.ts
|
|
516
542
|
var scopes = {};
|
|
517
543
|
var hookStack = /* @__PURE__ */ new Map();
|
|
@@ -544,7 +570,13 @@ function useJitterScope(scope) {
|
|
|
544
570
|
const hookId = `${scopeId}-${hookEndEvent.id}`;
|
|
545
571
|
if (shouldReportChanges()) {
|
|
546
572
|
const prevResult = currentScope.hookResults[hookId];
|
|
547
|
-
const
|
|
573
|
+
const hookAddress = {
|
|
574
|
+
hook: hookEndEvent.hook,
|
|
575
|
+
file: hookEndEvent.file,
|
|
576
|
+
line: hookEndEvent.line,
|
|
577
|
+
offset: hookEndEvent.offset
|
|
578
|
+
};
|
|
579
|
+
const changes = compareChanges(hookAddress, prevResult, hookResult);
|
|
548
580
|
if (changes) {
|
|
549
581
|
const hookCall = {
|
|
550
582
|
hook: hookEndEvent.hook,
|
|
@@ -560,6 +592,9 @@ function useJitterScope(scope) {
|
|
|
560
592
|
if (hookEndEvent.arguments) {
|
|
561
593
|
hookCall.arguments = hookEndEvent.arguments;
|
|
562
594
|
}
|
|
595
|
+
if (hookEndEvent.isMocked) {
|
|
596
|
+
hookCall.isMocked = hookEndEvent.isMocked;
|
|
597
|
+
}
|
|
563
598
|
scopes[scopeId].hookChanges.push(hookCall);
|
|
564
599
|
callOnHookChange(hookCall);
|
|
565
600
|
}
|
|
@@ -571,6 +606,12 @@ function useJitterScope(scope) {
|
|
|
571
606
|
re: (renderResult) => {
|
|
572
607
|
callOnRender(scopes[scopeId]);
|
|
573
608
|
return renderResult;
|
|
609
|
+
},
|
|
610
|
+
m: (value) => {
|
|
611
|
+
if (typeof value !== "function") {
|
|
612
|
+
return false;
|
|
613
|
+
}
|
|
614
|
+
return "mockImplementation" in value || "mockReturnValue" in value;
|
|
574
615
|
}
|
|
575
616
|
};
|
|
576
617
|
}
|
|
@@ -621,12 +662,6 @@ function getScopeCount(scope) {
|
|
|
621
662
|
}
|
|
622
663
|
return scopeCounter[scope.id]++;
|
|
623
664
|
}
|
|
624
|
-
function compareChanges(prev, current) {
|
|
625
|
-
if (prev !== "undefined" && prev !== current) {
|
|
626
|
-
return getChanges(prev, current);
|
|
627
|
-
}
|
|
628
|
-
return null;
|
|
629
|
-
}
|
|
630
665
|
// Annotate the CommonJS export names for ESM import in node:
|
|
631
666
|
0 && (module.exports = {
|
|
632
667
|
reactJitter,
|
package/runtime/dist/index.mjs
CHANGED
|
@@ -412,7 +412,8 @@ function createCustomEqual(options) {
|
|
|
412
412
|
}
|
|
413
413
|
|
|
414
414
|
// src/utils/getChanges.ts
|
|
415
|
-
function getChanges(prev, next) {
|
|
415
|
+
function getChanges(prev, next, comparator = "deepEqual") {
|
|
416
|
+
const equals = comparator === "circularDeepEqual" ? circularDeepEqual : deepEqual;
|
|
416
417
|
const changedKeys = [];
|
|
417
418
|
const unstableKeys = [];
|
|
418
419
|
const isObject = (v) => v !== null && typeof v === "object";
|
|
@@ -431,7 +432,7 @@ function getChanges(prev, next) {
|
|
|
431
432
|
}
|
|
432
433
|
const max = Math.max(prev.length, next.length);
|
|
433
434
|
for (let i = 0; i < max; i++) {
|
|
434
|
-
const deepEqItem =
|
|
435
|
+
const deepEqItem = equals(prev[i], next[i]);
|
|
435
436
|
const refDiffItem = isObject(prev[i]) && isObject(next[i]) && prev[i] !== next[i];
|
|
436
437
|
if (!deepEqItem || refDiffItem) {
|
|
437
438
|
const key = String(i);
|
|
@@ -444,7 +445,7 @@ function getChanges(prev, next) {
|
|
|
444
445
|
} else if (isObject(prev) && isObject(next)) {
|
|
445
446
|
const allKeys = /* @__PURE__ */ new Set([...Object.keys(prev), ...Object.keys(next)]);
|
|
446
447
|
for (const key of allKeys) {
|
|
447
|
-
const deepEqProp =
|
|
448
|
+
const deepEqProp = equals(prev[key], next[key]);
|
|
448
449
|
const refDiffProp = isObject(prev[key]) && isObject(next[key]) && prev[key] !== next[key];
|
|
449
450
|
if (!deepEqProp || refDiffProp) {
|
|
450
451
|
changedKeys.push(key);
|
|
@@ -454,7 +455,7 @@ function getChanges(prev, next) {
|
|
|
454
455
|
}
|
|
455
456
|
}
|
|
456
457
|
} else {
|
|
457
|
-
const deepEqRoot =
|
|
458
|
+
const deepEqRoot = equals(prev, next);
|
|
458
459
|
const refDiffRoot = isObject(prev) && isObject(next) && prev !== next;
|
|
459
460
|
const unstable = refDiffRoot && deepEqRoot;
|
|
460
461
|
const changed = !deepEqRoot || refDiffRoot;
|
|
@@ -465,7 +466,7 @@ function getChanges(prev, next) {
|
|
|
465
466
|
};
|
|
466
467
|
}
|
|
467
468
|
const isPlainObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
468
|
-
const unstableRoot = isPlainObject(prev) && isPlainObject(next) && prev !== next &&
|
|
469
|
+
const unstableRoot = isPlainObject(prev) && isPlainObject(next) && prev !== next && equals(prev, next);
|
|
469
470
|
if (unstableRoot && changedKeys.length === 0) {
|
|
470
471
|
changedKeys.push("");
|
|
471
472
|
unstableKeys.push("");
|
|
@@ -477,6 +478,31 @@ function getChanges(prev, next) {
|
|
|
477
478
|
};
|
|
478
479
|
}
|
|
479
480
|
|
|
481
|
+
// src/utils/compareChanges.ts
|
|
482
|
+
function compareChanges(hookAddress, prev, current) {
|
|
483
|
+
var _a, _b, _c;
|
|
484
|
+
if (prev !== "undefined" && prev !== current) {
|
|
485
|
+
const comparator = (_c = (_b = (_a = window == null ? void 0 : window.reactJitter) == null ? void 0 : _a.selectComparator) == null ? void 0 : _b.call(_a, hookAddress)) != null ? _c : "deepEqual";
|
|
486
|
+
try {
|
|
487
|
+
return getChanges(prev, current, comparator);
|
|
488
|
+
} catch (error) {
|
|
489
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
490
|
+
const isRecursionError = /(?:maximum call stack(?: size)? exceeded|too much recursion|stack overflow)/i.test(
|
|
491
|
+
errorMessage
|
|
492
|
+
);
|
|
493
|
+
if (isRecursionError && comparator !== "circularDeepEqual") {
|
|
494
|
+
throw new Error(
|
|
495
|
+
`Maximum call stack size exceeded. Please use the "circularDeepEqual" comparator with selectComparator option.
|
|
496
|
+
Hook address: ${JSON.stringify(hookAddress, null, 2)}.`,
|
|
497
|
+
{ cause: error }
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
throw error;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
return null;
|
|
504
|
+
}
|
|
505
|
+
|
|
480
506
|
// src/index.ts
|
|
481
507
|
var scopes = {};
|
|
482
508
|
var hookStack = /* @__PURE__ */ new Map();
|
|
@@ -509,7 +535,13 @@ function useJitterScope(scope) {
|
|
|
509
535
|
const hookId = `${scopeId}-${hookEndEvent.id}`;
|
|
510
536
|
if (shouldReportChanges()) {
|
|
511
537
|
const prevResult = currentScope.hookResults[hookId];
|
|
512
|
-
const
|
|
538
|
+
const hookAddress = {
|
|
539
|
+
hook: hookEndEvent.hook,
|
|
540
|
+
file: hookEndEvent.file,
|
|
541
|
+
line: hookEndEvent.line,
|
|
542
|
+
offset: hookEndEvent.offset
|
|
543
|
+
};
|
|
544
|
+
const changes = compareChanges(hookAddress, prevResult, hookResult);
|
|
513
545
|
if (changes) {
|
|
514
546
|
const hookCall = {
|
|
515
547
|
hook: hookEndEvent.hook,
|
|
@@ -525,6 +557,9 @@ function useJitterScope(scope) {
|
|
|
525
557
|
if (hookEndEvent.arguments) {
|
|
526
558
|
hookCall.arguments = hookEndEvent.arguments;
|
|
527
559
|
}
|
|
560
|
+
if (hookEndEvent.isMocked) {
|
|
561
|
+
hookCall.isMocked = hookEndEvent.isMocked;
|
|
562
|
+
}
|
|
528
563
|
scopes[scopeId].hookChanges.push(hookCall);
|
|
529
564
|
callOnHookChange(hookCall);
|
|
530
565
|
}
|
|
@@ -536,6 +571,12 @@ function useJitterScope(scope) {
|
|
|
536
571
|
re: (renderResult) => {
|
|
537
572
|
callOnRender(scopes[scopeId]);
|
|
538
573
|
return renderResult;
|
|
574
|
+
},
|
|
575
|
+
m: (value) => {
|
|
576
|
+
if (typeof value !== "function") {
|
|
577
|
+
return false;
|
|
578
|
+
}
|
|
579
|
+
return "mockImplementation" in value || "mockReturnValue" in value;
|
|
539
580
|
}
|
|
540
581
|
};
|
|
541
582
|
}
|
|
@@ -586,12 +627,6 @@ function getScopeCount(scope) {
|
|
|
586
627
|
}
|
|
587
628
|
return scopeCounter[scope.id]++;
|
|
588
629
|
}
|
|
589
|
-
function compareChanges(prev, current) {
|
|
590
|
-
if (prev !== "undefined" && prev !== current) {
|
|
591
|
-
return getChanges(prev, current);
|
|
592
|
-
}
|
|
593
|
-
return null;
|
|
594
|
-
}
|
|
595
630
|
export {
|
|
596
631
|
reactJitter,
|
|
597
632
|
useJitterScope
|