diff --git a/site/lib/src/extensions/registry.dart b/site/lib/src/extensions/registry.dart index fb51180b84b..a2690a6a188 100644 --- a/site/lib/src/extensions/registry.dart +++ b/site/lib/src/extensions/registry.dart @@ -10,7 +10,7 @@ import 'glossary_link_processor.dart'; import 'header_extractor.dart'; import 'header_processor.dart'; import 'table_processor.dart'; -import 'tutorial_prefetch_processor.dart'; +import 'tutorial_navigation.dart'; import 'tutorial_structure_processor.dart'; /// A list of all node-processing, page extensions to applied to diff --git a/site/lib/src/extensions/tutorial_prefetch_processor.dart b/site/lib/src/extensions/tutorial_navigation.dart similarity index 80% rename from site/lib/src/extensions/tutorial_prefetch_processor.dart rename to site/lib/src/extensions/tutorial_navigation.dart index 27ba7b19748..ed5aed6d3aa 100644 --- a/site/lib/src/extensions/tutorial_prefetch_processor.dart +++ b/site/lib/src/extensions/tutorial_navigation.dart @@ -2,14 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:jaspr/dom.dart'; -import 'package:jaspr/jaspr.dart'; import 'package:jaspr_content/jaspr_content.dart'; import '../models/tutorial_model.dart'; -/// A page extension for Jaspr Content that adds page navigation and a -/// prefetch link for the next unit to the current tutorial page. +/// A page extension for Jaspr Content that adds +/// page navigation to the current tutorial page. final class TutorialNavigationExtension implements PageExtension { const TutorialNavigationExtension(); @@ -67,19 +65,6 @@ final class TutorialNavigationExtension implements PageExtension { }, ); - if (nextChapter == null) { - return nodes; - } - - return [ - ComponentNode( - Document.head( - children: [ - link(rel: 'prefetch', href: nextChapter.url), - ], - ), - ), - ...nodes, - ]; + return nodes; } } diff --git a/site/lib/src/layouts/dash_layout.dart b/site/lib/src/layouts/dash_layout.dart index b06835835b8..56ee4244ad5 100644 --- a/site/lib/src/layouts/dash_layout.dart +++ b/site/lib/src/layouts/dash_layout.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:convert'; + import 'package:jaspr/dom.dart'; import 'package:jaspr/jaspr.dart'; import 'package:jaspr_content/jaspr_content.dart'; @@ -25,6 +27,14 @@ abstract class FlutterDocsLayout extends PageLayoutBase { String get defaultSidenav => 'default'; + /// Returns page-specific URLs to eagerly speculate on, in addition to + /// the document-level rules that match all internal links. + /// + /// Override in subclasses to provide page-specific URLs for + /// eager prerendering and prefetching. + ({Set prerender, Set prefetch}) speculationUrls(Page page) => + const (prerender: {}, prefetch: {}); + @override @mustCallSuper Iterable buildHead(Page page) { @@ -148,6 +158,9 @@ ga('create', 'UA-67589403-1', 'auto'); ga('send', 'pageview'); '''), + // Add speculation rules and prefetch fallback links for + // URLs provided by subclass overrides of speculationUrls. + ..._buildSpeculationRulesHead(page), ]; } @@ -263,4 +276,66 @@ if (sidenav) { ], ); } + + /// Builds the speculation rules `'), + // Fall back to prefetch link tags for browsers without + // Speculation Rules API support. + for (final url in {...prerender, ...prefetch}) + link(rel: 'prefetch', href: url), + ]; + } } diff --git a/site/lib/src/layouts/doc_layout.dart b/site/lib/src/layouts/doc_layout.dart index cee1cb2ae74..35974839360 100644 --- a/site/lib/src/layouts/doc_layout.dart +++ b/site/lib/src/layouts/doc_layout.dart @@ -24,6 +24,27 @@ class DocLayout extends FlutterDocsLayout { bool get allowBreadcrumbs => true; + @override + ({Set prerender, Set prefetch}) speculationUrls(Page page) { + // On the homepage, prefetch pages commonly navigated to, + // such as entries from the top navigation menu. + if (page.path == 'index.md') { + return ( + prerender: const {}, + prefetch: const { + '/learn/pathway', + '/ai/create-with-ai', + }, + ); + } + + final pageData = page.data.page; + return ( + prerender: {?_pathFromPageInfo(pageData['next'])}, + prefetch: {?_pathFromPageInfo(pageData['prev'])}, + ); + } + @override Component buildBody(Page page, Component child) { final pageData = page.data.page; @@ -89,3 +110,12 @@ class DocLayout extends FlutterDocsLayout { ); } } + +/// Extracts and returns the `path` value from a page info map, +/// or `null` if [data] is not a map or has no `path` entry. +String? _pathFromPageInfo(Object? data) { + if (data case {'path': final String path}) { + return path; + } + return null; +}