11const fs = require ( 'fs' ) ;
22const gulp = require ( 'gulp' ) ;
3- const { exec} = require ( 'child_process' ) ;
3+ const { exec } = require ( 'child_process' ) ;
44const express = require ( 'express' ) ;
5- const checkPages = require ( 'check-pages' ) ;
65const { glob } = require ( 'glob' ) ;
76const cheerio = require ( 'cheerio' ) ;
87
8+ async function linkCheck ( options ) {
9+ const { LinkChecker } = await import ( 'linkinator' ) ;
10+ const checker = new LinkChecker ( ) ;
11+
12+ return await checker . check ( {
13+ path : options . pageUrls [ 0 ] ,
14+ linksToSkip : options . linksToSkip ,
15+ recurse : true ,
16+ concurrency : 5
17+ } ) ;
18+ }
19+
920function displayErrors ( err , stdout , stderr ) {
1021 if ( err ) {
1122 console . log ( '\nERROR FOUND\n\n' + err ) ;
@@ -32,13 +43,12 @@ function checkInternalLinksAndExit(htmlPath) {
3243 if ( foundElementByName ) return ;
3344
3445 const text = $ ( anchor ) . text ( ) ;
35- badLinks . push ( { text, href} ) ;
46+ badLinks . push ( { text, href } ) ;
3647 }
3748 } ) ;
3849
3950 $ ( 'h1,h2,h3' ) . each ( ( index , element ) => {
4051 const id = $ ( element ) . attr ( 'id' ) ;
41-
4252 if ( id ) {
4353 if ( seenHeadingIds . has ( id ) ) duplicateHeadingIds . push ( id ) ;
4454 else seenHeadingIds . add ( id ) ;
@@ -48,7 +58,7 @@ function checkInternalLinksAndExit(htmlPath) {
4858 if ( badLinks . length ) {
4959 console . error ( 'v3 docs error: Found invalid internal links' ) ;
5060 console . error ( 'Make sure these `href`s correspond to the `id`s of real headings in the HTML:' ) ;
51- console . error ( badLinks . map ( ( { text, href} ) => ` - [${ text } ](${ href } )` ) . join ( '\n' ) ) ;
61+ console . error ( badLinks . map ( ( { text, href } ) => ` - [${ text } ](${ href } )` ) . join ( '\n' ) ) ;
5262 }
5363
5464 if ( duplicateHeadingIds . length ) {
@@ -65,6 +75,7 @@ function checkInternalLinksAndExit(htmlPath) {
6575function checkSyntaxErrorsAndExit ( htmlPath ) {
6676 const $ = cheerio . load ( fs . readFileSync ( htmlPath , 'utf8' ) ) ;
6777 const syntaxErrors = $ ( 'code .err' ) ;
78+
6879 if ( syntaxErrors . length ) {
6980 syntaxErrors . each ( ( _index , errorElement ) => {
7081 console . error ( '⚠️ v3 docs error: Found syntax error' ) ;
@@ -77,21 +88,34 @@ function checkSyntaxErrorsAndExit(htmlPath) {
7788 }
7889}
7990
80- function checkPathAndExit ( path , options , done ) {
91+ async function checkPathAndExit ( path , options , done ) {
8192 const app = express ( ) ;
8293 app . use ( express . static ( path ) ) ;
83- const server = app . listen ( { port : 8001 } ) ;
94+ const server = app . listen ( { port : 8001 } ) ;
95+
96+ try {
97+ const result = await linkCheck ( {
98+ linksToSkip : options . linksToSkip ,
99+ pageUrls : ( options . pageUrls && options . pageUrls . length ) ? options . pageUrls : [ 'http://localhost:8001/' ]
100+ } ) ;
84101
85- return checkPages ( console , options , ( err , stdout , stderr ) => {
86102 server . close ( ) ;
87103 done ( ) ;
88104
89- if ( err ) {
90- return displayErrors ( err , stdout , stderr ) ;
105+ if ( result . passed === false ) {
106+ // linkinator gives us a state for each link, e.g. 'BROKEN', 'OK', 'SKIPPED' etc.
107+ const brokenLinks = result . links . filter ( x => x . state === 'BROKEN' ) ;
108+ console . error ( `Found ${ brokenLinks . length } broken links:` ) ;
109+ brokenLinks . forEach ( ( link ) => {
110+ console . error ( `- ${ link . url } : ${ link . status } ` ) ;
111+ } ) ;
112+ process . exit ( 1 ) ;
91113 }
92-
93- return true ;
94- } ) ;
114+ } catch ( err ) {
115+ server . close ( ) ;
116+ done ( ) ;
117+ displayErrors ( err , '' , '' ) ;
118+ }
95119}
96120
97121gulp . task ( 'build' , cb => {
@@ -110,7 +134,7 @@ gulp.task('webserver', cb => {
110134 }
111135 cb ( ) ;
112136 } ) ;
113- console . log ( 'Your docs are waiting for you at http://localhost:8000' )
137+ console . log ( 'Your docs are waiting for you at http://localhost:8000' ) ;
114138} ) ;
115139
116140gulp . task ( 'default' , gulp . series ( 'webserver' ) ) ;
@@ -125,12 +149,18 @@ gulp.task('checkV3docs', gulp.series('build', done => {
125149 terse : true ,
126150 onlySameDomain : true ,
127151 pageUrls : [ 'http://localhost:8001/' ] ,
128- linksToIgnore : [ 'http://localhost:8001/version/release-candidate' ]
152+ linksToSkip : [ 'http://localhost:8001/version/release-candidate' ]
129153 } , done ) ;
130154} ) ) ;
131155
132- gulp . task ( 'checkV2docs' , async ( done ) => {
133- const htmlFiles = await glob ( '../v2/**/*.html' )
156+ gulp . task ( 'checkV2docs' , async ( done ) => {
157+ const htmlFiles = await new Promise ( ( resolve , reject ) => {
158+ glob ( '../v2/**/*.html' , ( err , matches ) => {
159+ if ( err ) return reject ( err ) ;
160+ resolve ( matches ) ;
161+ } ) ;
162+ } ) ;
163+
134164 const fixedFiles = htmlFiles . map ( fname => {
135165 return 'http://localhost:8001' + fname . substr ( '../v2' . length ) ;
136166 } ) ;
@@ -140,8 +170,9 @@ gulp.task('checkV2docs', async(done) => {
140170 summary : true ,
141171 terse : true ,
142172 onlySameDomain : true ,
143- pageUrls : [ 'http://localhost:8001/' ] . concat ( fixedFiles )
173+ pageUrls : [ 'http://localhost:8001/' ] . concat ( fixedFiles ) ,
174+ linksToSkip : [ ]
144175 } , done ) ;
145176} ) ;
146177
147- gulp . task ( 'checkdocs' , gulp . parallel ( 'checkV2docs' , 'checkV3docs' ) ) ;
178+ gulp . task ( 'checkdocs' , gulp . parallel ( 'checkV2docs' , 'checkV3docs' ) ) ;
0 commit comments