@@ -62,14 +62,23 @@ export class SitemapReader extends EventEmitter {
6262 return ct . split ( ";" ) [ 0 ] ;
6363 }
6464
65- async _fetchWithRetry ( url : string , message : string ) {
65+ async _fetchWithRetry ( url : string , expectedCT = XML_CONTENT_TYPES ) {
6666 while ( true ) {
6767 const resp = await fetch ( url , {
6868 headers : this . headers ,
6969 dispatcher : getProxyDispatcher ( url ) ,
7070 } ) ;
7171
7272 if ( resp . ok ) {
73+ const ct = resp . headers . get ( "content-type" ) ;
74+ if ( expectedCT && ct && ! expectedCT . includes ( ct . split ( ";" ) [ 0 ] ) ) {
75+ logger . debug (
76+ "Not loading sitemap: invalid content-type" ,
77+ { ct } ,
78+ "sitemap" ,
79+ ) ;
80+ return null ;
81+ }
7382 return resp ;
7483 }
7584
@@ -85,148 +94,108 @@ export class SitemapReader extends EventEmitter {
8594 continue ;
8695 }
8796
88- logger . debug ( message , { status : resp . status } , "sitemap" ) ;
89- return null ;
90- }
91- }
92-
93- async tryFetch ( url : string , expectedCT ?: string [ ] | null ) {
94- try {
9597 logger . debug (
96- "Detecting Sitemap: fetching " ,
97- { url , expectedCT } ,
98+ "Not loading sitemap: invalid status code " ,
99+ { status : resp . status } ,
98100 "sitemap" ,
99101 ) ;
100-
101- const resp = await this . _fetchWithRetry (
102- url ,
103- "Detecting Sitemap: invalid status code" ,
104- ) ;
105-
106- if ( ! resp ) {
107- return null ;
108- }
109-
110- const ct = resp . headers . get ( "content-type" ) ;
111- if ( expectedCT && ct && ! expectedCT . includes ( ct . split ( ";" ) [ 0 ] ) ) {
112- logger . debug (
113- "Detecting Sitemap: invalid content-type" ,
114- { ct } ,
115- "sitemap" ,
116- ) ;
117- return null ;
118- }
119-
120- return resp ;
121- } catch ( e ) {
122- logger . debug ( "Detecting Sitemap: unknown error" , e , "sitemap" ) ;
123102 return null ;
124103 }
125104 }
126105
127106 async parse ( sitemap : string , seedUrl : string ) {
128- let resp : Response | null = null ;
129- let fullUrl : string | null = null ;
130- let isRobots = false ;
131- let isSitemap = false ;
132-
133- // if set to auto-detect, eg. --sitemap / --useSitemap with no URL
134- // 1. first check robots.txt
135- // 2. if not found, check /sitemap.xml
107+ let found = false ;
108+
136109 if ( sitemap === DETECT_SITEMAP ) {
110+ // if set to auto-detect, eg. --sitemap / --useSitemap with no URL
111+ // 1. first check robots.txt
112+ // 2. if not found, check /sitemap.xml
137113 logger . debug ( "Detecting sitemap for seed" , { seedUrl } , "sitemap" ) ;
138- fullUrl = new URL ( "/robots.txt" , seedUrl ) . href ;
139- resp = await this . tryFetch ( fullUrl , TEXT_CONTENT_TYPE ) ;
140- if ( resp ) {
141- isRobots = true ;
142- } else {
143- fullUrl = new URL ( "/sitemap.xml" , seedUrl ) . href ;
144- resp = await this . tryFetch ( fullUrl , XML_CONTENT_TYPES ) ;
145- if ( resp ) {
146- isSitemap = true ;
147- }
114+ const robotsUrl = new URL ( "/robots.txt" , seedUrl ) . href ;
115+ found = await this . parseRobotsForSitemap ( robotsUrl ) ;
116+
117+ const sitemapUrl = new URL ( "/sitemap.xml" , seedUrl ) . href ;
118+
119+ if ( ! found ) {
120+ found = await this . parseSitemap ( sitemapUrl ) ;
121+ }
122+ if ( ! found ) {
123+ logger . debug (
124+ "Sitemap not detected in robots.txt or sitemap.xml" ,
125+ { robotsUrl, sitemapUrl, seedUrl } ,
126+ "sitemap" ,
127+ ) ;
148128 }
149129 } else {
150130 // if specific URL provided, check if its a .xml file or a robots.txt file
151- fullUrl = new URL ( sitemap , seedUrl ) . href ;
152- let expected = null ;
131+ const fullUrl = new URL ( sitemap , seedUrl ) . href ;
153132 if ( fullUrl . endsWith ( ".xml" ) || fullUrl . endsWith ( ".xml.gz" ) ) {
154- expected = XML_CONTENT_TYPES ;
155- isSitemap = true ;
133+ found = await this . parseSitemap ( fullUrl ) ;
156134 } else if ( fullUrl . endsWith ( ".txt" ) ) {
157- expected = TEXT_CONTENT_TYPE ;
158- isRobots = true ;
135+ found = await this . parseRobotsForSitemap ( fullUrl ) ;
136+ } else {
137+ logger . debug (
138+ "URL provided must be a sitemap XML or robots TXT file" ,
139+ { sitemap, seedUrl, fullUrl } ,
140+ "sitemap" ,
141+ ) ;
159142 }
160- resp = await this . tryFetch ( fullUrl , expected ) ;
161143 }
162144
163- // fail if no successful response fetched
164- if ( ! resp ) {
165- logger . debug (
166- "Sitemap not found" ,
167- { sitemap, seedUrl, fullUrl } ,
168- "sitemap" ,
169- ) ;
170- throw new Error ( "not found" ) ;
171- }
172-
173- // fail if neither an xml nor robots.txt
174- if ( ! isRobots && ! isSitemap ) {
175- logger . info ( "Sitemap not detected for seed" , { seedUrl } , "sitemap" ) ;
176- throw new Error ( "not xml or robots.txt" ) ;
177- }
145+ return found ;
146+ }
178147
179- if ( isRobots ) {
180- logger . debug (
181- "Sitemap: parsing from robots.txt" ,
182- { fullUrl, seedUrl } ,
183- "sitemap" ,
184- ) ;
185- await this . _parseRobotsFromResponse ( resp ) ;
186- } else if ( isSitemap ) {
148+ private async parseRobotsForSitemap ( robotsUrl : string ) {
149+ let sitemapFound = false ;
150+ try {
187151 logger . debug (
188- "Sitemap: parsing from top-level sitemap XML " ,
189- { fullUrl , seedUrl } ,
152+ "Sitemap: Parsing robots to detect sitemap " ,
153+ { url : robotsUrl } ,
190154 "sitemap" ,
191155 ) ;
192- await this . parseSitemapFromResponse ( fullUrl , resp ) ;
193- }
156+ const resp = await this . _fetchWithRetry ( robotsUrl , TEXT_CONTENT_TYPE ) ;
157+ if ( ! resp ) {
158+ return sitemapFound ;
159+ }
194160
195- await this . checkIfDone ( ) ;
196- }
161+ const text = await resp . text ( ) ;
197162
198- async parseFromRobots ( url : string ) {
199- const resp = await this . _fetchWithRetry (
200- url ,
201- "Sitemap robots.txt parse failed" ,
202- ) ;
203- if ( ! resp ) {
204- return ;
163+ text . replace ( / ^ S i t e m a p : \s ? ( [ ^ \s ] + ) $ / gim, ( m , urlStr ) => {
164+ try {
165+ const url = new URL ( urlStr , robotsUrl ) . href ;
166+ logger . debug ( "Sitemap: Added from robots" , { url } , "sitemap" ) ;
167+ this . addNewSitemap ( url , null ) ;
168+ sitemapFound = true ;
169+ } catch ( e ) {
170+ // ignore invalid
171+ }
172+ return urlStr ;
173+ } ) ;
174+ } catch ( e ) {
175+ //
205176 }
206-
207- await this . _parseRobotsFromResponse ( resp ) ;
208- }
209-
210- private async _parseRobotsFromResponse ( resp : Response ) {
211- const text = await resp . text ( ) ;
212-
213- text . replace ( / ^ S i t e m a p : \s ? ( [ ^ \s ] + ) $ / gim, ( m , url ) => {
214- this . addNewSitemap ( url , null ) ;
215- return url ;
216- } ) ;
177+ return sitemapFound ;
217178 }
218179
219180 async parseSitemap ( url : string ) {
220- this . seenSitemapSet . add ( url ) ;
181+ try {
182+ this . seenSitemapSet . add ( url ) ;
221183
222- const resp = await this . _fetchWithRetry ( url , "Sitemap parse failed" ) ;
223- if ( ! resp ) {
224- return ;
225- }
184+ logger . debug ( "Parsing sitemap XML" , url , "sitemap" ) ;
185+
186+ const resp = await this . _fetchWithRetry ( url ) ;
187+ if ( ! resp ) {
188+ return false ;
189+ }
226190
227- await this . parseSitemapFromResponse ( url , resp ) ;
191+ await this . parseSitemapFromResponse ( url , resp ) ;
228192
229- await this . checkIfDone ( ) ;
193+ await this . checkIfDone ( ) ;
194+ return true ;
195+ } catch ( e ) {
196+ logger . warn ( "Sitemap parse failed" , { url, ...formatErr ( e ) } , "sitemap" ) ;
197+ return false ;
198+ }
230199 }
231200
232201 private async parseSitemapFromResponse ( url : string , resp : Response ) {
@@ -430,14 +399,19 @@ export class SitemapReader extends EventEmitter {
430399 parserStream . on ( "text" , ( text : string ) => processText ( text ) ) ;
431400 parserStream . on ( "cdata" , ( text : string ) => processText ( text ) ) ;
432401
402+ let limitLogged = false ;
403+
433404 parserStream . on ( "error" , ( err : Error ) => {
434405 const msg = { url, err, errCount } ;
435406 if ( this . atLimit ( ) ) {
436- logger . warn (
437- "Sitemap parsing aborting, page limit reached" ,
438- msg ,
439- "sitemap" ,
440- ) ;
407+ if ( ! limitLogged ) {
408+ logger . warn (
409+ "Sitemap parsing aborting, page limit reached" ,
410+ msg ,
411+ "sitemap" ,
412+ ) ;
413+ limitLogged = true ;
414+ }
441415 resolve ( ) ;
442416 } else {
443417 logger . warn ( "Sitemap error parsing XML" , msg , "sitemap" ) ;
@@ -490,17 +464,7 @@ export class SitemapReader extends EventEmitter {
490464 return ;
491465 }
492466
493- void this . queue . add ( async ( ) => {
494- try {
495- await this . parseSitemap ( url ) ;
496- } catch ( e ) {
497- logger . warn (
498- "Sitemap parse failed" ,
499- { url, ...formatErr ( e ) } ,
500- "sitemap" ,
501- ) ;
502- }
503- } ) ;
467+ void this . queue . add ( ( ) => this . parseSitemap ( url ) ) ;
504468 }
505469
506470 emitEntry ( url : string | null , lastmod : Date | null ) {
0 commit comments