300
}
301
}
302
303
+function getIODescription(value: any): string {
304
+ if (!__DEV__) {
305
+ return '';
306
+ }
307
+ try {
308
+ switch (typeof value) {
309
+ case 'object':
310
+ // Test the object for a bunch of common property names that are useful identifiers.
311
+ // While we only have the return value here, it should ideally be a name that
312
+ // describes the arguments requested.
313
+ if (value === null) {
314
+ return '';
315
+ } else if (value instanceof Error) {
316
+ // eslint-disable-next-line react-internal/safe-string-coercion
317
+ return String(value.message);
318
+ } else if (typeof value.url === 'string') {
319
+ return value.url;
320
+ } else if (typeof value.command === 'string') {
321
+ return value.command;
322
+ } else if (
323
+ typeof value.request === 'object' &&
324
+ typeof value.request.url === 'string'
325
+ ) {
326
+ return value.request.url;
327
+ } else if (
328
+ typeof value.response === 'object' &&
329
+ typeof value.response.url === 'string'
330
+ ) {
331
+ return value.response.url;
332
+ } else if (
333
+ typeof value.id === 'string' ||
334
+ typeof value.id === 'number' ||
335
+ typeof value.id === 'bigint'
336
+ ) {
337
+ // eslint-disable-next-line react-internal/safe-string-coercion
338
+ return String(value.id);
339
+ } else if (typeof value.name === 'string') {
340
+ return value.name;
341
+ } else {
342
+ const str = value.toString();
343
+ if (str.startWith('[object ') || str.length < 5 || str.length > 500) {
344
+ // This is probably not a useful description.
345
+ return '';
346
+ }
347
+ return str;
348
+ }
349
+ case 'string':
350
+ if (value.length < 5 || value.length > 500) {
351
+ return '';
352
+ }
353
+ return value;
354
+ case 'number':
355
+ case 'bigint':
356
+ // eslint-disable-next-line react-internal/safe-string-coercion
357
+ return String(value);
358
+ default:
359
+ // Not useful descriptors.
360
+ return '';
361
+ }
362
+ } catch (x) {
363
+ return '';
364
+ }
365
+}
366
+
367
+function getIOLongName(
368
+ ioInfo: ReactIOInfo,
369
+ description: string,
370
+ env: void | string,
371
+ rootEnv: string,
372
+): string {
373
+ const name = ioInfo.name;
374
+ const longName = description === '' ? name : name + ' (' + description + ')';
375
+ const isPrimaryEnv = env === rootEnv;
376
+ return isPrimaryEnv || env === undefined
377
+ ? longName
378
+ : longName + ' [' + env + ']';
379
+}
380
+
381
+function getIOShortName(
382
+ ioInfo: ReactIOInfo,
383
+ description: string,
384
+ env: void | string,
385
+ rootEnv: string,
386
+): string {
387
+ const name = ioInfo.name;
388
+ const isPrimaryEnv = env === rootEnv;
389
+ const envSuffix = isPrimaryEnv || env === undefined ? '' : ' [' + env + ']';
390
+ let desc = '';
391
+ const descMaxLength = 30 - name.length - envSuffix.length;
392
+ if (descMaxLength > 1) {
393
+ const l = description.length;
394
+ if (l > 0 && l <= descMaxLength) {
395
+ // We can fit the full description
396
+ desc = ' (' + description + ')';
397
+ } else if (
398
+ description.startsWith('http://') ||
399
+ description.startsWith('https://') ||
400
+ description.startsWith('/')
401
+ ) {
402
+ // Looks like a URL. Let's see if we can extract something shorter.
403
+ // We don't have to do a full parse so let's try something cheaper.
404
+ let queryIdx = description.indexOf('?');
405
+ if (queryIdx === -1) {
406
+ queryIdx = description.length;
407
+ }
408
+ if (description.charCodeAt(queryIdx - 1) === 47 /* "/" */) {
409
+ // Ends with slash. Look before that.
410
+ queryIdx--;
411
+ }
412
+ const slashIdx = description.lastIndexOf('/', queryIdx - 1);
413
+ if (queryIdx - slashIdx < descMaxLength) {
414
+ // This may now be either the file name or the host.
415
+ desc = ' (' + description.slice(slashIdx + 1, queryIdx) + ')';
416
+ }
417
+ }
418
+ }
419
+ return name + desc + envSuffix;
420
+}
421
+
422
export function logComponentAwaitAborted(
423
asyncInfo: ReactAsyncInfo,
424
trackIdx: number,
427
rootEnv: string,
428
): void {
429
if (supportsUserTiming && endTime > 0) {
311
- const env = asyncInfo.env;
312
- const name = asyncInfo.awaited.name;
313
- const isPrimaryEnv = env === rootEnv;
430
const entryName =
315
- 'await ' +
316
- (isPrimaryEnv || env === undefined ? name : name + ' [' + env + ']');
431
+ 'await ' + getIOShortName(asyncInfo.awaited, '', asyncInfo.env, rootEnv);
432
const debugTask = asyncInfo.debugTask || asyncInfo.awaited.debugTask;
433
if (__DEV__ && debugTask) {
434
const properties = [
435
['Aborted', 'The stream was aborted before this Promise resolved.'],
436
];
437
+ const tooltipText =
438
+ getIOLongName(asyncInfo.awaited, '', asyncInfo.env, rootEnv) +
439
+ ' Aborted';
440
debugTask.run(
441
// $FlowFixMe[method-unbinding]
442
performance.measure.bind(performance, entryName, {
448
track: trackNames[trackIdx],
449
trackGroup: COMPONENTS_TRACK,
450
properties,
333
- tooltipText: entryName + ' Aborted',
451
+ tooltipText,
452
},
453
},
454
}),
475
error: mixed,
476
): void {
477
if (supportsUserTiming && endTime > 0) {
360
- const env = asyncInfo.env;
361
- const name = asyncInfo.awaited.name;
362
- const isPrimaryEnv = env === rootEnv;
478
+ const description = getIODescription(error);
479
const entryName =
480
'await ' +
365
- (isPrimaryEnv || env === undefined ? name : name + ' [' + env + ']');
481
+ getIOShortName(asyncInfo.awaited, description, asyncInfo.env, rootEnv);
482
const debugTask = asyncInfo.debugTask || asyncInfo.awaited.debugTask;
483
if (__DEV__ && debugTask) {
484
const message =
490
: // eslint-disable-next-line react-internal/safe-string-coercion
491
String(error);
492
const properties = [['Rejected', message]];
493
+ const tooltipText =
494
+ getIOLongName(asyncInfo.awaited, description, asyncInfo.env, rootEnv) +
495
+ ' Rejected';
496
debugTask.run(
497
// $FlowFixMe[method-unbinding]
498
performance.measure.bind(performance, entryName, {
504
track: trackNames[trackIdx],
505
trackGroup: COMPONENTS_TRACK,
506
properties,
388
- tooltipText: entryName + ' Rejected',
507
+ tooltipText,
508
},
509
},
510
}),
531
value: mixed,
532
): void {
533
if (supportsUserTiming && endTime > 0) {
415
- const env = asyncInfo.env;
416
- const name = asyncInfo.awaited.name;
417
- const isPrimaryEnv = env === rootEnv;
534
+ const description = getIODescription(value);
535
+ const name = getIOShortName(
536
+ asyncInfo.awaited,
537
+ description,
538
+ asyncInfo.env,
539
+ rootEnv,
540
+ );
541
+ const entryName = 'await ' + name;
542
const color = getIOColor(name);
419
- const entryName =
420
- 'await ' +
421
- (isPrimaryEnv || env === undefined ? name : name + ' [' + env + ']');
543
const debugTask = asyncInfo.debugTask || asyncInfo.awaited.debugTask;
544
if (__DEV__ && debugTask) {
545
const properties: Array<[string, string]> = [];
548
} else if (value !== undefined) {
549
addValueToProperties('Resolved', value, properties, 0, '');
550
}
551
+ const tooltipText = getIOLongName(
552
+ asyncInfo.awaited,
553
+ description,
554
+ asyncInfo.env,
555
+ rootEnv,
556
+ );
557
debugTask.run(
558
// $FlowFixMe[method-unbinding]
559
performance.measure.bind(performance, entryName, {
565
track: trackNames[trackIdx],
566
trackGroup: COMPONENTS_TRACK,
567
properties,
568
+ tooltipText,
569
},
570
},
571
}),
591
const startTime = ioInfo.start;
592
const endTime = ioInfo.end;
593
if (supportsUserTiming && endTime >= 0) {
466
- const name = ioInfo.name;
467
- const env = ioInfo.env;
468
- const isPrimaryEnv = env === rootEnv;
469
- const entryName =
470
- isPrimaryEnv || env === undefined ? name : name + ' [' + env + ']';
594
+ const description = getIODescription(error);
595
+ const entryName = getIOShortName(ioInfo, description, ioInfo.env, rootEnv);
596
const debugTask = ioInfo.debugTask;
597
if (__DEV__ && debugTask) {
598
const message =
604
: // eslint-disable-next-line react-internal/safe-string-coercion
605
String(error);
606
const properties = [['Rejected', message]];
607
+ const tooltipText =
608
+ getIOLongName(ioInfo, description, ioInfo.env, rootEnv) + ' Rejected';
609
debugTask.run(
610
// $FlowFixMe[method-unbinding]
611
performance.measure.bind(performance, entryName, {
616
color: 'error',
617
track: IO_TRACK,
618
properties,
492
- tooltipText: entryName + ' Rejected',
619
+ tooltipText,
620
},
621
},
622
}),
642
const startTime = ioInfo.start;
643
const endTime = ioInfo.end;
644
if (supportsUserTiming && endTime >= 0) {
518
- const name = ioInfo.name;
519
- const env = ioInfo.env;
520
- const isPrimaryEnv = env === rootEnv;
521
- const entryName =
522
- isPrimaryEnv || env === undefined ? name : name + ' [' + env + ']';
645
+ const description = getIODescription(value);
646
+ const entryName = getIOShortName(ioInfo, description, ioInfo.env, rootEnv);
647
+ const color = getIOColor(entryName);
648
const debugTask = ioInfo.debugTask;
524
- const color = getIOColor(name);
649
if (__DEV__ && debugTask) {
650
const properties: Array<[string, string]> = [];
651
if (typeof value === 'object' && value !== null) {
653
} else if (value !== undefined) {
654
addValueToProperties('Resolved', value, properties, 0, '');
655
}
656
+ const tooltipText = getIOLongName(
657
+ ioInfo,
658
+ description,
659
+ ioInfo.env,
660
+ rootEnv,
661
+ );
662
debugTask.run(
663
// $FlowFixMe[method-unbinding]
664
performance.measure.bind(performance, entryName, {
669
color: color,
670
track: IO_TRACK,
671
properties,
672
+ tooltipText,
673
},
674
},
675
}),