@@ -507,6 +507,121 @@ describe("Lazy Route Discovery (Fog of War)", () => {
507
507
] ) ;
508
508
} ) ;
509
509
510
+ it ( "de-prioritizes dynamic param routes in favor of looking for better async matches" , async ( ) => {
511
+ router = createRouter ( {
512
+ history : createMemoryHistory ( ) ,
513
+ routes : [
514
+ {
515
+ path : "/" ,
516
+ } ,
517
+ {
518
+ id : "slug" ,
519
+ path : "/:slug" ,
520
+ } ,
521
+ ] ,
522
+ async unstable_patchRoutesOnMiss ( { patch } ) {
523
+ await tick ( ) ;
524
+ patch ( null , [
525
+ {
526
+ id : "static" ,
527
+ path : "/static" ,
528
+ } ,
529
+ ] ) ;
530
+ } ,
531
+ } ) ;
532
+
533
+ await router . navigate ( "/static" ) ;
534
+ expect ( router . state . location . pathname ) . toBe ( "/static" ) ;
535
+ expect ( router . state . matches . map ( ( m ) => m . route . id ) ) . toEqual ( [ "static" ] ) ;
536
+ } ) ;
537
+
538
+ it ( "de-prioritizes dynamic param routes in favor of looking for better async matches (product/:slug)" , async ( ) => {
539
+ router = createRouter ( {
540
+ history : createMemoryHistory ( ) ,
541
+ routes : [
542
+ {
543
+ path : "/" ,
544
+ } ,
545
+ {
546
+ id : "slug" ,
547
+ path : "/product/:slug" ,
548
+ } ,
549
+ ] ,
550
+ async unstable_patchRoutesOnMiss ( { patch } ) {
551
+ await tick ( ) ;
552
+ patch ( null , [
553
+ {
554
+ id : "static" ,
555
+ path : "/product/static" ,
556
+ } ,
557
+ ] ) ;
558
+ } ,
559
+ } ) ;
560
+
561
+ await router . navigate ( "/product/static" ) ;
562
+ expect ( router . state . location . pathname ) . toBe ( "/product/static" ) ;
563
+ expect ( router . state . matches . map ( ( m ) => m . route . id ) ) . toEqual ( [ "static" ] ) ;
564
+ } ) ;
565
+
566
+ it ( "de-prioritizes dynamic param routes in favor of looking for better async matches (child route)" , async ( ) => {
567
+ router = createRouter ( {
568
+ history : createMemoryHistory ( ) ,
569
+ routes : [
570
+ {
571
+ path : "/" ,
572
+ } ,
573
+ {
574
+ id : "product" ,
575
+ path : "/product" ,
576
+ children : [
577
+ {
578
+ id : "slug" ,
579
+ path : ":slug" ,
580
+ } ,
581
+ ] ,
582
+ } ,
583
+ ] ,
584
+ async unstable_patchRoutesOnMiss ( { patch } ) {
585
+ await tick ( ) ;
586
+ patch ( "product" , [
587
+ {
588
+ id : "static" ,
589
+ path : "static" ,
590
+ } ,
591
+ ] ) ;
592
+ } ,
593
+ } ) ;
594
+
595
+ await router . navigate ( "/product/static" ) ;
596
+ expect ( router . state . location . pathname ) . toBe ( "/product/static" ) ;
597
+ expect ( router . state . matches . map ( ( m ) => m . route . id ) ) . toEqual ( [
598
+ "product" ,
599
+ "static" ,
600
+ ] ) ;
601
+ } ) ;
602
+
603
+ it ( "matches dynamic params when other paths don't pan out" , async ( ) => {
604
+ router = createRouter ( {
605
+ history : createMemoryHistory ( ) ,
606
+ routes : [
607
+ {
608
+ path : "/" ,
609
+ } ,
610
+ {
611
+ id : "slug" ,
612
+ path : "/:slug" ,
613
+ } ,
614
+ ] ,
615
+ async unstable_patchRoutesOnMiss ( { matches, patch } ) {
616
+ await tick ( ) ;
617
+ } ,
618
+ } ) ;
619
+
620
+ await router . navigate ( "/a" ) ;
621
+ expect ( router . state . location . pathname ) . toBe ( "/a" ) ;
622
+ expect ( router . state . matches . map ( ( m ) => m . route . id ) ) . toEqual ( [ "slug" ] ) ;
623
+ } ) ;
624
+
510
625
it ( "de-prioritizes splat routes in favor of looking for better async matches" , async ( ) => {
511
626
router = createRouter ( {
512
627
history : createMemoryHistory ( ) ,
@@ -569,6 +684,43 @@ describe("Lazy Route Discovery (Fog of War)", () => {
569
684
expect ( router . state . matches . map ( ( m ) => m . route . id ) ) . toEqual ( [ "static" ] ) ;
570
685
} ) ;
571
686
687
+ it ( "de-prioritizes splat routes in favor of looking for better async matches (child route)" , async ( ) => {
688
+ router = createRouter ( {
689
+ history : createMemoryHistory ( ) ,
690
+ routes : [
691
+ {
692
+ path : "/" ,
693
+ } ,
694
+ {
695
+ id : "product" ,
696
+ path : "/product" ,
697
+ children : [
698
+ {
699
+ id : "splat" ,
700
+ path : "*" ,
701
+ } ,
702
+ ] ,
703
+ } ,
704
+ ] ,
705
+ async unstable_patchRoutesOnMiss ( { patch } ) {
706
+ await tick ( ) ;
707
+ patch ( "product" , [
708
+ {
709
+ id : "static" ,
710
+ path : "static" ,
711
+ } ,
712
+ ] ) ;
713
+ } ,
714
+ } ) ;
715
+
716
+ await router . navigate ( "/product/static" ) ;
717
+ expect ( router . state . location . pathname ) . toBe ( "/product/static" ) ;
718
+ expect ( router . state . matches . map ( ( m ) => m . route . id ) ) . toEqual ( [
719
+ "product" ,
720
+ "static" ,
721
+ ] ) ;
722
+ } ) ;
723
+
572
724
it ( "matches splats when other paths don't pan out" , async ( ) => {
573
725
router = createRouter ( {
574
726
history : createMemoryHistory ( ) ,
@@ -603,6 +755,50 @@ describe("Lazy Route Discovery (Fog of War)", () => {
603
755
expect ( router . state . matches . map ( ( m ) => m . route . id ) ) . toEqual ( [ "splat" ] ) ;
604
756
} ) ;
605
757
758
+ it ( "recurses unstable_patchRoutesOnMiss until a match is found" , async ( ) => {
759
+ let count = 0 ;
760
+ router = createRouter ( {
761
+ history : createMemoryHistory ( ) ,
762
+ routes : [
763
+ {
764
+ path : "/" ,
765
+ } ,
766
+ {
767
+ id : "a" ,
768
+ path : "a" ,
769
+ } ,
770
+ ] ,
771
+ async unstable_patchRoutesOnMiss ( { matches, patch } ) {
772
+ await tick ( ) ;
773
+ count ++ ;
774
+ if ( last ( matches ) . route . id === "a" ) {
775
+ patch ( "a" , [
776
+ {
777
+ id : "b" ,
778
+ path : "b" ,
779
+ } ,
780
+ ] ) ;
781
+ } else if ( last ( matches ) . route . id === "b" ) {
782
+ patch ( "b" , [
783
+ {
784
+ id : "c" ,
785
+ path : "c" ,
786
+ } ,
787
+ ] ) ;
788
+ }
789
+ } ,
790
+ } ) ;
791
+
792
+ await router . navigate ( "/a/b/c" ) ;
793
+ expect ( router . state . location . pathname ) . toBe ( "/a/b/c" ) ;
794
+ expect ( router . state . matches . map ( ( m ) => m . route . id ) ) . toEqual ( [
795
+ "a" ,
796
+ "b" ,
797
+ "c" ,
798
+ ] ) ;
799
+ expect ( count ) . toBe ( 2 ) ;
800
+ } ) ;
801
+
606
802
it ( "discovers routes during initial hydration" , async ( ) => {
607
803
let childrenDfd = createDeferred < AgnosticDataRouteObject [ ] > ( ) ;
608
804
let loaderDfd = createDeferred ( ) ;
@@ -1063,6 +1259,136 @@ describe("Lazy Route Discovery (Fog of War)", () => {
1063
1259
unsubscribe ( ) ;
1064
1260
} ) ;
1065
1261
1262
+ it ( 'does not re-call for previously called "good" paths' , async ( ) => {
1263
+ let count = 0 ;
1264
+ router = createRouter ( {
1265
+ history : createMemoryHistory ( ) ,
1266
+ routes : [
1267
+ {
1268
+ path : "/" ,
1269
+ } ,
1270
+ {
1271
+ id : "param" ,
1272
+ path : ":param" ,
1273
+ } ,
1274
+ ] ,
1275
+ async unstable_patchRoutesOnMiss ( ) {
1276
+ count ++ ;
1277
+ await tick ( ) ;
1278
+ // Nothing to patch - there is no better static route in this case
1279
+ } ,
1280
+ } ) ;
1281
+
1282
+ await router . navigate ( "/whatever" ) ;
1283
+ expect ( count ) . toBe ( 1 ) ;
1284
+ expect ( router . state . location . pathname ) . toBe ( "/whatever" ) ;
1285
+ expect ( router . state . matches . map ( ( m ) => m . route . id ) ) . toEqual ( [ "param" ] ) ;
1286
+
1287
+ await router . navigate ( "/" ) ;
1288
+ expect ( count ) . toBe ( 1 ) ;
1289
+ expect ( router . state . location . pathname ) . toBe ( "/" ) ;
1290
+
1291
+ await router . navigate ( "/whatever" ) ;
1292
+ expect ( count ) . toBe ( 1 ) ; // Not called again
1293
+ expect ( router . state . location . pathname ) . toBe ( "/whatever" ) ;
1294
+ expect ( router . state . matches . map ( ( m ) => m . route . id ) ) . toEqual ( [ "param" ] ) ;
1295
+ } ) ;
1296
+
1297
+ it ( "does not re-call for previously called 404 paths" , async ( ) => {
1298
+ let count = 0 ;
1299
+ router = createRouter ( {
1300
+ history : createMemoryHistory ( ) ,
1301
+ routes : [
1302
+ {
1303
+ id : "index" ,
1304
+ path : "/" ,
1305
+ } ,
1306
+ {
1307
+ id : "static" ,
1308
+ path : "static" ,
1309
+ } ,
1310
+ ] ,
1311
+ async unstable_patchRoutesOnMiss ( ) {
1312
+ count ++ ;
1313
+ } ,
1314
+ } ) ;
1315
+
1316
+ await router . navigate ( "/junk" ) ;
1317
+ expect ( count ) . toBe ( 1 ) ;
1318
+ expect ( router . state . location . pathname ) . toBe ( "/junk" ) ;
1319
+ expect ( router . state . errors ?. index ) . toEqual (
1320
+ new ErrorResponseImpl (
1321
+ 404 ,
1322
+ "Not Found" ,
1323
+ new Error ( 'No route matches URL "/junk"' ) ,
1324
+ true
1325
+ )
1326
+ ) ;
1327
+
1328
+ await router . navigate ( "/" ) ;
1329
+ expect ( count ) . toBe ( 1 ) ;
1330
+ expect ( router . state . location . pathname ) . toBe ( "/" ) ;
1331
+ expect ( router . state . errors ) . toBeNull ( ) ;
1332
+
1333
+ await router . navigate ( "/junk" ) ;
1334
+ expect ( count ) . toBe ( 1 ) ;
1335
+ expect ( router . state . location . pathname ) . toBe ( "/junk" ) ;
1336
+ expect ( router . state . errors ?. index ) . toEqual (
1337
+ new ErrorResponseImpl (
1338
+ 404 ,
1339
+ "Not Found" ,
1340
+ new Error ( 'No route matches URL "/junk"' ) ,
1341
+ true
1342
+ )
1343
+ ) ;
1344
+ } ) ;
1345
+
1346
+ it ( "caps internal fifo queue at 1000 paths" , async ( ) => {
1347
+ let count = 0 ;
1348
+ router = createRouter ( {
1349
+ history : createMemoryHistory ( ) ,
1350
+ routes : [
1351
+ {
1352
+ path : "/" ,
1353
+ } ,
1354
+ {
1355
+ id : "param" ,
1356
+ path : ":param" ,
1357
+ } ,
1358
+ ] ,
1359
+ async unstable_patchRoutesOnMiss ( ) {
1360
+ count ++ ;
1361
+ // Nothing to patch - there is no better static route in this case
1362
+ } ,
1363
+ } ) ;
1364
+
1365
+ // Fill it up with 1000 paths
1366
+ for ( let i = 1 ; i <= 1000 ; i ++ ) {
1367
+ await router . navigate ( `/path-${ i } ` ) ;
1368
+ expect ( count ) . toBe ( i ) ;
1369
+ expect ( router . state . location . pathname ) . toBe ( `/path-${ i } ` ) ;
1370
+
1371
+ await router . navigate ( "/" ) ;
1372
+ expect ( count ) . toBe ( i ) ;
1373
+ expect ( router . state . location . pathname ) . toBe ( "/" ) ;
1374
+ }
1375
+
1376
+ // Don't call patchRoutesOnMiss since this is the first item in the queue
1377
+ await router . navigate ( `/path-1` ) ;
1378
+ expect ( count ) . toBe ( 1000 ) ;
1379
+ expect ( router . state . location . pathname ) . toBe ( `/path-1` ) ;
1380
+
1381
+ // Call patchRoutesOnMiss and evict the first item
1382
+ await router . navigate ( `/path-1001` ) ;
1383
+ expect ( count ) . toBe ( 1001 ) ;
1384
+ expect ( router . state . location . pathname ) . toBe ( `/path-1001` ) ;
1385
+
1386
+ // Call patchRoutesOnMiss since this item was evicted
1387
+ await router . navigate ( `/path-1` ) ;
1388
+ expect ( count ) . toBe ( 1002 ) ;
1389
+ expect ( router . state . location . pathname ) . toBe ( `/path-1` ) ;
1390
+ } ) ;
1391
+
1066
1392
describe ( "errors" , ( ) => {
1067
1393
it ( "lazy 404s (GET navigation)" , async ( ) => {
1068
1394
let childrenDfd = createDeferred < AgnosticDataRouteObject [ ] > ( ) ;
0 commit comments