9
10
import * as React from "react";
11
12
-const { useRef, useEffect } = React;
12
+const { useRef, useEffect, isValidElement } = React;
13
const ReactSecretInternals =
14
//@ts-ignore
15
React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE ??
251
};
252
});
253
}
254
+
255
+const seenErrors = new Set();
256
+
257
+export function $structuralCheck(
258
+ oldValue: any,
259
+ newValue: any,
260
+ variableName: string,
261
+ fnName: string
262
+): void {
263
+ function error(l: string, r: string, path: string, depth: number) {
264
+ const str = `${fnName}: ${variableName}${path} changed from ${l} to ${r} at depth ${depth}`;
265
+ if (seenErrors.has(str)) {
266
+ return;
267
+ }
268
+ seenErrors.add(str);
269
+ console.error(str);
270
+ }
271
+ const depthLimit = 2;
272
+ function recur(oldValue: any, newValue: any, path: string, depth: number) {
273
+ if (depth > depthLimit) {
274
+ return;
275
+ } else if (oldValue === newValue) {
276
+ return;
277
+ } else if (typeof oldValue !== typeof newValue) {
278
+ error(`type ${typeof oldValue}`, `type ${typeof newValue}`, path, depth);
279
+ } else if (typeof oldValue === "object") {
280
+ const oldArray = Array.isArray(oldValue);
281
+ const newArray = Array.isArray(newValue);
282
+ if (oldValue === null && newValue !== null) {
283
+ error("null", `type ${typeof newValue}`, path, depth);
284
+ } else if (newValue === null) {
285
+ error(`type ${typeof oldValue}`, null, path, depth);
286
+ } else if (oldValue instanceof Map) {
287
+ if (!(newValue instanceof Map)) {
288
+ error(`Map instance`, `other value`, path, depth);
289
+ } else if (oldValue.size !== newValue.size) {
290
+ error(
291
+ `Map instance with size ${oldValue.size}`,
292
+ `Map instance with size ${newValue.size}`,
293
+ path,
294
+ depth
295
+ );
296
+ } else {
297
+ for (const [k, v] of oldValue) {
298
+ if (!newValue.has(k)) {
299
+ error(
300
+ `Map instance with key ${k}`,
301
+ `Map instance without key ${k}`,
302
+ path,
303
+ depth
304
+ );
305
+ } else {
306
+ recur(v, newValue.get(k), `${path}.get(${k})`, depth + 1);
307
+ }
308
+ }
309
+ }
310
+ } else if (newValue instanceof Map) {
311
+ error("other value", `Map instance`, path, depth);
312
+ } else if (oldValue instanceof Set) {
313
+ if (!(newValue instanceof Set)) {
314
+ error(`Set instance`, `other value`, path, depth);
315
+ } else if (oldValue.size !== newValue.size) {
316
+ error(
317
+ `Set instance with size ${oldValue.size}`,
318
+ `Set instance with size ${newValue.size}`,
319
+ path,
320
+ depth
321
+ );
322
+ } else {
323
+ for (const v of newValue) {
324
+ if (!oldValue.has(v)) {
325
+ error(
326
+ `Set instance without element ${v}`,
327
+ `Set instance with element ${v}`,
328
+ path,
329
+ depth
330
+ );
331
+ }
332
+ }
333
+ }
334
+ } else if (newValue instanceof Set) {
335
+ error("other value", `Set instance`, path, depth);
336
+ } else if (oldArray || newArray) {
337
+ if (oldArray !== newArray) {
338
+ error(
339
+ `type ${oldArray ? "array" : "object"}`,
340
+ `type ${newArray ? "array" : "object"}`,
341
+ path,
342
+ depth
343
+ );
344
+ } else if (oldValue.length !== newValue.length) {
345
+ error(
346
+ `array with length ${oldValue.length}`,
347
+ `array with length ${newValue.length}`,
348
+ path,
349
+ depth
350
+ );
351
+ } else {
352
+ for (let ii = 0; ii < oldValue.length; ii++) {
353
+ recur(oldValue[ii], newValue[ii], `${path}[${ii}]`, depth + 1);
354
+ }
355
+ }
356
+ } else if (isValidElement(oldValue) || isValidElement(newValue)) {
357
+ if (isValidElement(oldValue) !== isValidElement(newValue)) {
358
+ error(
359
+ `type ${isValidElement(oldValue) ? "React element" : "object"}`,
360
+ `type ${isValidElement(newValue) ? "React element" : "object"}`,
361
+ path,
362
+ depth
363
+ );
364
+ } else if (oldValue.type !== newValue.type) {
365
+ error(
366
+ `React element of type ${oldValue.type}`,
367
+ `React element of type ${newValue.type}`,
368
+ path,
369
+ depth
370
+ );
371
+ } else {
372
+ recur(
373
+ oldValue.props,
374
+ newValue.props,
375
+ `[props of ${path}]`,
376
+ depth + 1
377
+ );
378
+ }
379
+ } else {
380
+ for (const key in newValue) {
381
+ if (!(key in oldValue)) {
382
+ error(
383
+ `object without key ${key}`,
384
+ `object with key ${key}`,
385
+ path,
386
+ depth
387
+ );
388
+ }
389
+ }
390
+ for (const key in oldValue) {
391
+ if (!(key in newValue)) {
392
+ error(
393
+ `object with key ${key}`,
394
+ `object without key ${key}`,
395
+ path,
396
+ depth
397
+ );
398
+ } else {
399
+ recur(oldValue[key], newValue[key], `${path}.${key}`, depth + 1);
400
+ }
401
+ }
402
+ }
403
+ } else if (typeof oldValue === "function") {
404
+ // Bail on functions for now
405
+ return;
406
+ } else if (isNaN(oldValue) || isNaN(newValue)) {
407
+ if (isNaN(oldValue) !== isNaN(newValue)) {
408
+ error(
409
+ `${isNaN(oldValue) ? "NaN" : "non-NaN value"}`,
410
+ `${isNaN(newValue) ? "NaN" : "non-NaN value"}`,
411
+ path,
412
+ depth
413
+ );
414
+ }
415
+ } else if (oldValue !== newValue) {
416
+ error(oldValue, newValue, path, depth);
417
+ }
418
+ }
419
+ recur(oldValue, newValue, "", 0);
420
+}