77// When you change the configuration in the dashboard, change it here too.
88//
99// The API key below is the crawler's *write* key and is deliberately not in
10- // this repository. Set it in the dashboard, or in the crawler's environment.
10+ // this repository. Keep the existing key when pasting this file into the
11+ // dashboard; never replace this placeholder in Git.
1112//
1213// Every selector here was checked against the generated HTML, not assumed.
1314
1415new Crawler ( {
1516 appId : '7IZIJ13AI7' ,
16- apiKey : process . env . ALGOLIA_CRAWLER_WRITE_KEY ,
17+ apiKey : '< ALGOLIA_CRAWLER_WRITE_KEY>' ,
1718 indexPrefix : '' ,
1819 rateLimit : 8 ,
1920 maxDepth : 10 ,
20- schedule : 'every 1 day' ,
21+ schedule : 'at 9:50 AM on Thursday' ,
22+ ignoreCanonicalTo : true ,
23+ saveBackup : true ,
24+
25+ safetyChecks : {
26+ beforeIndexPublishing : {
27+ maxLostRecordsPercentage : 10
28+ } ,
29+ // The dashboard's current config.d.ts exposes this at the safetyChecks
30+ // level, rather than inside beforeIndexPublishing.
31+ maxFailedUrls : 5
32+ } ,
2133
2234 // Only the released site. next.taskfile.dev serves the same URLs from the
2335 // upcoming release, and both sites share the single `taskfile` index, so
2436 // crawling it as well would give every page a duplicate record.
25- startUrls : [ 'https://taskfile.dev/docs/' ] ,
37+ // The root exists both before and after the documentation refactor. The old
38+ // production site has no /docs/ landing page yet.
39+ startUrls : [ 'https://taskfile.dev/' ] ,
2640 sitemaps : [ 'https://taskfile.dev/sitemap.xml' ] ,
2741 // Only /docs is indexed, so there is no reason to fetch the blog, the
2842 // homepage or /adopters on every crawl.
@@ -40,11 +54,68 @@ new Crawler({
4054 {
4155 indexName : 'taskfile' ,
4256 pathsToMatch : [ 'https://taskfile.dev/docs/**' ] ,
43- recordExtractor : ( { $, helpers } ) => {
57+ recordExtractor : ( { $, helpers, url } ) => {
4458 // The banner the llms plugin injects sits inside .vp-doc, ahead of the
4559 // h1. It is display:none for readers and must not become content.
4660 $ ( '[data-nosnippet]' ) . remove ( ) ;
4761
62+ // DocSearch expects content selectors to target paragraphs or list
63+ // items. Copy code blocks into crawler-only paragraphs so experienced
64+ // users can search for exact Taskfile keys and command syntax without
65+ // changing the page rendered to readers.
66+ $ ( '.vp-doc pre code' ) . each ( ( _ , element ) => {
67+ const code = $ ( element ) . text ( ) . trim ( ) ;
68+ if ( ! code ) return ;
69+ const paragraph = $ ( '<p></p>' ) . addClass ( 'docsearch-code' ) . text ( code ) ;
70+ $ ( element ) . closest ( 'pre' ) . after ( paragraph ) ;
71+ } ) ;
72+
73+ // Frontmatter metadata is available after the refactor. Infer the same
74+ // values from the URL while the old monolithic guide is still live, so
75+ // this configuration can be installed before the website PR merges.
76+ // Remove this URL inference once the refactored documentation is live
77+ // and every indexed page exposes the DocSearch metadata.
78+ const pathname = url . pathname . replace ( / \/ + $ / , '' ) || '/' ;
79+ const inferredSection = ( ( ) => {
80+ if ( pathname === '/docs' ) return 'Overview' ;
81+ if (
82+ / ^ \/ d o c s \/ ( i n s t a l l a t i o n | g e t t i n g - s t a r t e d | i n t e g r a t i o n s ) $ / . test (
83+ pathname
84+ )
85+ ) {
86+ return 'Getting Started' ;
87+ }
88+ if ( / ^ \/ d o c s \/ r e f e r e n c e \/ / . test ( pathname ) ) return 'Reference' ;
89+ if ( / ^ \/ d o c s \/ ( c o n t r i b u t i n g | r e l e a s i n g | s t y l e g u i d e ) $ / . test ( pathname ) ) {
90+ return 'Contributing' ;
91+ }
92+ if (
93+ / ^ \/ d o c s \/ ( e x p e r i m e n t s | d e p r e c a t i o n s | s e c u r i t y ) ( \/ | $ ) / . test (
94+ pathname
95+ ) ||
96+ / ^ \/ d o c s \/ ( c h a n g e l o g | f a q | t a s k f i l e - v e r s i o n s | c o m m u n i t y ) $ / . test (
97+ pathname
98+ )
99+ ) {
100+ return 'Project' ;
101+ }
102+ return 'Guide' ;
103+ } ) ( ) ;
104+ const section =
105+ $ ( 'meta[name="docsearch:section"]' ) . attr ( 'content' ) ||
106+ inferredSection ;
107+ const docType =
108+ $ ( 'meta[name="docsearch:doc_type"]' ) . attr ( 'content' ) ||
109+ ( {
110+ Overview : 'overview' ,
111+ Reference : 'reference' ,
112+ Contributing : 'contributing' ,
113+ Project : 'project' ,
114+ Guide : 'guide' ,
115+ 'Getting Started' : 'guide'
116+ } [ section ] ??
117+ 'guide' ) ;
118+
48119 return helpers . docsearch ( {
49120 recordProps : {
50121 // Not a heading on the page: the section the page belongs to,
@@ -53,8 +124,10 @@ new Crawler({
53124 // DOM instead, which ties the index to the theme's markup and
54125 // breaks silently when that markup changes.
55126 lvl0 : {
56- selectors : 'meta[name="docsearch:section"]' ,
57- defaultValue : 'Documentation'
127+ // Algolia documents an empty selector as the way to provide a
128+ // raw, dynamically computed lvl0 through defaultValue.
129+ selectors : '' ,
130+ defaultValue : section
58131 } ,
59132 // Everything below is scoped to .vp-doc. VitePress renders the
60133 // sidebar's section labels as <h2 class="text"> inside
@@ -64,55 +137,44 @@ new Crawler({
64137 lvl2 : '.vp-doc h2' ,
65138 lvl3 : '.vp-doc h3' ,
66139 lvl4 : '.vp-doc h4' ,
67- content : '.vp-doc p, .vp-doc li, .vp-doc td' ,
68- // Faceted so a search can be narrowed to reference pages, or
69- // weighted differently later.
70- docType : {
71- selectors : 'meta[name="docsearch:type"]' ,
72- defaultValue : 'guide'
73- }
74- } ,
75- aggregateContent : true ,
76- recordVersion : 'v3'
77- } ) ;
78- }
79- } ,
80- {
81- // Deprecation notices describe what to stop doing. They should be
82- // findable by name, but never ahead of the page documenting the
83- // replacement.
84- indexName : 'taskfile' ,
85- pathsToMatch : [ 'https://taskfile.dev/docs/deprecations/**' ] ,
86- pageRank : - 10 ,
87- recordExtractor : ( { $, helpers } ) => {
88- $ ( '[data-nosnippet]' ) . remove ( ) ;
89- return helpers . docsearch ( {
90- recordProps : {
91- lvl0 : {
92- selectors : 'meta[name="docsearch:section"]' ,
93- defaultValue : 'Deprecations'
140+ lvl5 : '.vp-doc h5' ,
141+ lvl6 : '.vp-doc h6' ,
142+ content : '.vp-doc p, .vp-doc li, .vp-doc td, .vp-doc th' ,
143+ section : { defaultValue : section } ,
144+ doc_type : { defaultValue : docType } ,
145+ lang : {
146+ defaultValue : $ ( 'html' ) . attr ( 'lang' ) || 'en-US'
94147 } ,
95- lvl1 : '.vp-doc h1' ,
96- lvl2 : '.vp-doc h2' ,
97- lvl3 : '.vp-doc h3' ,
98- content : '.vp-doc p, .vp-doc li, .vp-doc td'
148+ // Deprecation notices remain findable, but don't outrank the page
149+ // that documents the supported replacement.
150+ pageRank : pathname . startsWith ( '/docs/deprecations/' ) ? '-10' : '0'
99151 } ,
152+ indexHeadings : true ,
100153 aggregateContent : true ,
101154 recordVersion : 'v3'
102155 } ) ;
103156 }
104157 }
105158 ] ,
106159
160+ // These settings initialize a new index. Algolia doesn't apply them to an
161+ // existing index, so import the same values in the taskfile index settings
162+ // when this configuration changes them.
107163 initialIndexSettings : {
108164 taskfile : {
109- attributesForFaceting : [ 'type' , 'lang' , 'docType' ] ,
165+ hitsPerPage : 20 ,
166+ maxValuesPerFacet : 100 ,
167+ attributesForFaceting : [ 'type' , 'lang' , 'section' , 'doc_type' ] ,
110168 attributesToRetrieve : [
111169 'hierarchy' ,
112170 'content' ,
113171 'anchor' ,
114172 'url' ,
115- 'docType'
173+ 'url_without_anchor' ,
174+ 'type' ,
175+ 'lang' ,
176+ 'section' ,
177+ 'doc_type'
116178 ] ,
117179 attributesToHighlight : [ 'hierarchy' , 'content' ] ,
118180 attributesToSnippet : [ 'content:10' ] ,
@@ -123,6 +185,8 @@ new Crawler({
123185 'unordered(hierarchy.lvl2)' ,
124186 'unordered(hierarchy.lvl3)' ,
125187 'unordered(hierarchy.lvl4)' ,
188+ 'unordered(hierarchy.lvl5)' ,
189+ 'unordered(hierarchy.lvl6)' ,
126190 'content'
127191 ] ,
128192 distinct : true ,
@@ -150,7 +214,13 @@ new Crawler({
150214 ignorePlurals : true ,
151215 advancedSyntax : true ,
152216 attributeCriteriaComputedByMinProximity : true ,
153- removeWordsIfNoResults : 'allOptional'
217+ removeWordsIfNoResults : 'allOptional' ,
218+ separatorsToIndex : '_' ,
219+ paginationLimitedTo : 1000 ,
220+ exactOnSingleWordQuery : 'attribute' ,
221+ queryType : 'prefixLast' ,
222+ snippetEllipsisText : '' ,
223+ alternativesAsExact : [ 'ignorePlurals' , 'singleWordSynonym' ]
154224 }
155225 }
156226} ) ;
0 commit comments