421
); // simulated Webpack 'cheap-module-source-map'
422
});
423
});
424
+
425
+ describe('extended source maps', () => {
426
+ let parseMock;
427
+
428
+ beforeEach(() => {
429
+ parseMock = jest.fn();
430
+ jest.mock('@babel/parser', () => {
431
+ const actual = jest.requireActual('@babel/parser');
432
+ const parse = (...args) => {
433
+ parseMock();
434
+ return actual.parse(...args);
435
+ };
436
+ return {
437
+ parse,
438
+ ...actual,
439
+ };
440
+ });
441
+ });
442
+
443
+ it('should work for simple components', async () => {
444
+ async function test(path, name = 'Component') {
445
+ const Component = require(path)[name];
446
+ const hookNames = await getHookNamesForComponent(Component);
447
+ expectHookNamesToEqual(hookNames, [
448
+ 'count', // useState
449
+ ]);
450
+ expect(parseMock).toHaveBeenCalledTimes(0);
451
+ }
452
+
453
+ await test('./__source__/Example'); // original source (uncompiled)
454
+ await test(
455
+ './__source__/__compiled__/inline/fb-sources-extended/Example',
456
+ ); // x_fb_sources extended inline source map
457
+ await test(
458
+ './__source__/__compiled__/external/fb-sources-extended/Example',
459
+ ); // x_fb_sources extended external source map
460
+ await test(
461
+ './__source__/__compiled__/inline/react-sources-extended/Example',
462
+ ); // x_react_sources extended inline source map
463
+ await test(
464
+ './__source__/__compiled__/external/react-sources-extended/Example',
465
+ ); // x_react_sources extended external source map
466
+ // TODO test no-columns and bundle cases with extended source maps
467
+ });
468
+
469
+ it('should work with more complex files and components', async () => {
470
+ async function test(path, name = undefined) {
471
+ const components = name != null ? require(path)[name] : require(path);
472
+
473
+ let hookNames = await getHookNamesForComponent(components.List);
474
+ expectHookNamesToEqual(hookNames, [
475
+ 'newItemText', // useState
476
+ 'items', // useState
477
+ 'uid', // useState
478
+ 'handleClick', // useCallback
479
+ 'handleKeyPress', // useCallback
480
+ 'handleChange', // useCallback
481
+ 'removeItem', // useCallback
482
+ 'toggleItem', // useCallback
483
+ ]);
484
+
485
+ hookNames = await getHookNamesForComponent(components.ListItem, {
486
+ item: {},
487
+ });
488
+ expectHookNamesToEqual(hookNames, [
489
+ 'handleDelete', // useCallback
490
+ 'handleToggle', // useCallback
491
+ ]);
492
+
493
+ expect(parseMock).toHaveBeenCalledTimes(0);
494
+ }
495
+
496
+ await test('./__source__/ToDoList'); // original source (uncompiled)
497
+ await test(
498
+ './__source__/__compiled__/inline/fb-sources-extended/ToDoList',
499
+ ); // x_fb_sources extended inline source map
500
+ await test(
501
+ './__source__/__compiled__/external/fb-sources-extended/ToDoList',
502
+ ); // x_fb_sources extended external source map
503
+ await test(
504
+ './__source__/__compiled__/inline/react-sources-extended/ToDoList',
505
+ ); // x_react_sources extended inline source map
506
+ await test(
507
+ './__source__/__compiled__/external/react-sources-extended/ToDoList',
508
+ ); // x_react_sources extended external source map
509
+ // TODO test no-columns and bundle cases with extended source maps
510
+ });
511
+
512
+ it('should work for custom hook', async () => {
513
+ async function test(path, name = 'Component') {
514
+ const Component = require(path)[name];
515
+ const hookNames = await getHookNamesForComponent(Component);
516
+ expectHookNamesToEqual(hookNames, [
517
+ 'count', // useState()
518
+ 'isDarkMode', // useIsDarkMode()
519
+ 'isDarkMode', // useIsDarkMode -> useState()
520
+ ]);
521
+ expect(parseMock).toHaveBeenCalledTimes(0);
522
+ }
523
+
524
+ await test('./__source__/ComponentWithCustomHook'); // original source (uncompiled)
525
+ await test(
526
+ './__source__/__compiled__/inline/fb-sources-extended/ComponentWithCustomHook',
527
+ ); // x_fb_sources extended inline source map
528
+ await test(
529
+ './__source__/__compiled__/external/fb-sources-extended/ComponentWithCustomHook',
530
+ ); // x_fb_sources extended external source map
531
+ await test(
532
+ './__source__/__compiled__/inline/react-sources-extended/ComponentWithCustomHook',
533
+ ); // x_react_sources extended inline source map
534
+ await test(
535
+ './__source__/__compiled__/external/react-sources-extended/ComponentWithCustomHook',
536
+ ); // x_react_sources extended external source map
537
+ // TODO test no-columns and bundle cases with extended source maps
538
+ });
539
+
540
+ it('should work when code is using hooks indirectly', async () => {
541
+ async function test(path, name = 'Component') {
542
+ const Component = require(path)[name];
543
+ const hookNames = await getHookNamesForComponent(Component);
544
+ expectHookNamesToEqual(hookNames, [
545
+ 'count', // useState()
546
+ 'darkMode', // useDarkMode()
547
+ 'isDarkMode', // useState()
548
+ ]);
549
+ expect(parseMock).toHaveBeenCalledTimes(0);
550
+ }
551
+
552
+ await test(
553
+ './__source__/__compiled__/inline/fb-sources-extended/ComponentUsingHooksIndirectly',
554
+ ); // x_fb_sources extended inline source map
555
+ await test(
556
+ './__source__/__compiled__/external/fb-sources-extended/ComponentUsingHooksIndirectly',
557
+ ); // x_fb_sources extended external source map
558
+ await test(
559
+ './__source__/__compiled__/inline/react-sources-extended/ComponentUsingHooksIndirectly',
560
+ ); // x_react_sources extended inline source map
561
+ await test(
562
+ './__source__/__compiled__/external/react-sources-extended/ComponentUsingHooksIndirectly',
563
+ ); // x_react_sources extended external source map
564
+ // TODO test no-columns and bundle cases with extended source maps
565
+ });
566
+
567
+ it('should work when code is using nested hooks', async () => {
568
+ async function test(path, name = 'Component') {
569
+ const Component = require(path)[name];
570
+ let InnerComponent;
571
+ const hookNames = await getHookNamesForComponent(Component, {
572
+ callback: innerComponent => {
573
+ InnerComponent = innerComponent;
574
+ },
575
+ });
576
+ const innerHookNames = await getHookNamesForComponent(InnerComponent);
577
+ expectHookNamesToEqual(hookNames, [
578
+ 'InnerComponent', // useMemo()
579
+ ]);
580
+ expectHookNamesToEqual(innerHookNames, [
581
+ 'state', // useState()
582
+ ]);
583
+ expect(parseMock).toHaveBeenCalledTimes(0);
584
+ }
585
+
586
+ await test(
587
+ './__source__/__compiled__/inline/fb-sources-extended/ComponentWithNestedHooks',
588
+ ); // x_fb_sources extended inline source map
589
+ await test(
590
+ './__source__/__compiled__/external/fb-sources-extended/ComponentWithNestedHooks',
591
+ ); // x_fb_sources extended external source map
592
+ await test(
593
+ './__source__/__compiled__/inline/react-sources-extended/ComponentWithNestedHooks',
594
+ ); // x_react_sources extended inline source map
595
+ await test(
596
+ './__source__/__compiled__/external/react-sources-extended/ComponentWithNestedHooks',
597
+ ); // x_react_sources extended external source map
598
+ // TODO test no-columns and bundle cases with extended source maps
599
+ });
600
+
601
+ it('should work for external hooks', async () => {
602
+ async function test(path, name = 'Component') {
603
+ const Component = require(path)[name];
604
+ const hookNames = await getHookNamesForComponent(Component);
605
+ expectHookNamesToEqual(hookNames, [
606
+ 'theme', // useTheme()
607
+ 'theme', // useContext()
608
+ ]);
609
+ expect(parseMock).toHaveBeenCalledTimes(0);
610
+ }
611
+
612
+ // We can't test the uncompiled source here, because it either needs to get transformed,
613
+ // which would break the source mapping, or the import statements will fail.
614
+
615
+ await test(
616
+ './__source__/__compiled__/inline/fb-sources-extended/ComponentWithExternalCustomHooks',
617
+ ); // x_fb_sources extended inline source map
618
+ await test(
619
+ './__source__/__compiled__/external/fb-sources-extended/ComponentWithExternalCustomHooks',
620
+ ); // x_fb_sources extended external source map
621
+ await test(
622
+ './__source__/__compiled__/inline/react-sources-extended/ComponentWithExternalCustomHooks',
623
+ ); // x_react_sources extended inline source map
624
+ await test(
625
+ './__source__/__compiled__/external/react-sources-extended/ComponentWithExternalCustomHooks',
626
+ ); // x_react_sources extended external source map
627
+ // TODO test no-columns and bundle cases with extended source maps
628
+ });
629
+
630
+ it('should work when multiple hooks are on a line', async () => {
631
+ async function test(path, name = 'Component') {
632
+ const Component = require(path)[name];
633
+ const hookNames = await getHookNamesForComponent(Component);
634
+ expectHookNamesToEqual(hookNames, [
635
+ 'a', // useContext()
636
+ 'b', // useContext()
637
+ 'c', // useContext()
638
+ 'd', // useContext()
639
+ ]);
640
+ expect(parseMock).toHaveBeenCalledTimes(0);
641
+ }
642
+
643
+ await test(
644
+ './__source__/__compiled__/inline/fb-sources-extended/ComponentWithMultipleHooksPerLine',
645
+ ); // x_fb_sources extended inline source map
646
+ await test(
647
+ './__source__/__compiled__/external/fb-sources-extended/ComponentWithMultipleHooksPerLine',
648
+ ); // x_fb_sources extended external source map
649
+ await test(
650
+ './__source__/__compiled__/inline/react-sources-extended/ComponentWithMultipleHooksPerLine',
651
+ ); // x_react_sources extended inline source map
652
+ await test(
653
+ './__source__/__compiled__/external/react-sources-extended/ComponentWithMultipleHooksPerLine',
654
+ ); // x_react_sources extended external source map
655
+ // TODO test no-columns and bundle cases with extended source maps
656
+ });
657
+
658
+ // TODO Inline require (e.g. require("react").useState()) isn't supported yet.
659
+ // Maybe this isn't an important use case to support,
660
+ // since inline requires are most likely to exist in compiled source (if at all).
661
+ xit('should work for inline requires', async () => {
662
+ async function test(path, name = 'Component') {
663
+ const Component = require(path)[name];
664
+ const hookNames = await getHookNamesForComponent(Component);
665
+ expectHookNamesToEqual(hookNames, [
666
+ 'count', // useState()
667
+ ]);
668
+ expect(parseMock).toHaveBeenCalledTimes(0);
669
+ }
670
+
671
+ await test('./__source__/InlineRequire'); // original source (uncompiled)
672
+ await test(
673
+ './__source__/__compiled__/inline/fb-sources-extended/InlineRequire',
674
+ ); // x_fb_sources extended inline source map
675
+ await test(
676
+ './__source__/__compiled__/external/fb-sources-extended/InlineRequire',
677
+ ); // x_fb_sources extended external source map
678
+ await test(
679
+ './__source__/__compiled__/inline/react-sources-extended/InlineRequire',
680
+ ); // x_react_sources extended inline source map
681
+ await test(
682
+ './__source__/__compiled__/external/react-sources-extended/InlineRequire',
683
+ ); // x_react_sources extended external source map
684
+ // TODO test no-columns and bundle cases with extended source maps
685
+ });
686
+
687
+ it('should support sources that contain the string "sourceMappingURL="', async () => {
688
+ async function test(path, name = 'Component') {
689
+ const Component = require(path)[name];
690
+ const hookNames = await getHookNamesForComponent(Component);
691
+ expectHookNamesToEqual(hookNames, [
692
+ 'count', // useState()
693
+ ]);
694
+ expect(parseMock).toHaveBeenCalledTimes(0);
695
+ }
696
+
697
+ // We expect the inline sourceMappingURL to be invalid in this case; mute the warning.
698
+ console.warn = () => {};
699
+
700
+ await test('./__source__/ContainingStringSourceMappingURL'); // original source (uncompiled)
701
+ await test(
702
+ './__source__/__compiled__/inline/fb-sources-extended/ContainingStringSourceMappingURL',
703
+ ); // x_fb_sources extended inline source map
704
+ await test(
705
+ './__source__/__compiled__/external/fb-sources-extended/ContainingStringSourceMappingURL',
706
+ ); // x_fb_sources extended external source map
707
+ await test(
708
+ './__source__/__compiled__/inline/react-sources-extended/ContainingStringSourceMappingURL',
709
+ ); // x_react_sources extended inline source map
710
+ await test(
711
+ './__source__/__compiled__/external/react-sources-extended/ContainingStringSourceMappingURL',
712
+ ); // x_react_sources extended external source map
713
+ // TODO test no-columns and bundle cases with extended source maps
714
+ });
715
+ });
716
});
717
718
describe('parseHookNames worker', () => {