UNPKG

asciidoc-link-check

Version:

Checks if all hyperlinks in an asciidoc file are alive(or dead).

131 lines (105 loc) 4.99 kB
'use strict'; const _ = require('lodash'); const async = require('async'); const linkCheck = require('link-check'); const LinkCheckResult = require('link-check').LinkCheckResult; const asciidocLinkExtractor = require('asciidoc-link-extractor'); const ProgressBar = require('progress'); const envVarPatternMatcher = /(?<pattern>{{env\.(?<name>[a-zA-Z0-9\-_]+)}})/; /* * Performs some special replacements for the following patterns: * - {{BASEURL}} - to be replaced with opts.projectBaseUrl * - {{env.<env_var_name>}} - to be replaced with the environment variable specified with <env_var_name> */ function performSpecialReplacements(str, opts) { // replace the `{{BASEURL}}` with the opts.projectBaseUrl. Helpful to build absolute urls "relative" to project roots str = str.replace('{{BASEURL}}', opts.projectBaseUrl); // replace {{env.<env_var_name>}} with the corresponding environment variable or an empty string if none is set. var envVarMatch; do { envVarMatch = envVarPatternMatcher.exec(str); if(!envVarMatch) { break; } var envVarPattern = envVarMatch.groups.pattern; var envVarName = envVarMatch.groups.name; var envVarPatternReplacement = ''; if(envVarName in process.env) { envVarPatternReplacement = process.env[envVarName]; } str = str.replace(envVarPattern, envVarPatternReplacement); } while (true); return str; } module.exports = function asciidocLinkCheck(asciidoc, opts, callback) { if (arguments.length === 2 && typeof opts === 'function') { // optional 'opts' not supplied. callback = opts; opts = {}; } if(!opts.ignoreDisable) { asciidoc = [ /(<!--[ \t]+asciidoc-link-check-disable[ \t]+-->[\S\s]*?<!--[ \t]+asciidoc-link-check-enable[ \t]+-->)/mg, /(<!--[ \t]+asciidoc-link-check-disable[ \t]+-->[\S\s]*(?!<!--[ \t]+asciidoc-link-check-enable[ \t]+-->))/mg, /(<!--[ \t]+asciidoc-link-check-disable-next-line[ \t]+-->\r?\n[^\r\n]*)/mg, /([^\r\n]*<!--[ \t]+asciidoc-link-check-disable-line[ \t]+-->[^\r\n]*)/mg ].reduce(function(_asciidoc, disablePattern) { return _asciidoc.replace(new RegExp(disablePattern), ''); }, asciidoc); } const linksCollection = _.uniq(asciidocLinkExtractor(asciidoc)); const bar = (opts.showProgressBar) ? new ProgressBar('Checking... [:bar] :percent', { complete: '=', incomplete: ' ', width: 25, total: linksCollection.length }) : undefined; async.mapLimit(linksCollection, 2, function (link, callback) { if (opts.ignorePatterns) { const shouldIgnore = opts.ignorePatterns.some(function(ignorePattern) { return ignorePattern.pattern instanceof RegExp ? ignorePattern.pattern.test(link) : (new RegExp(ignorePattern.pattern)).test(link) ? true : false; }); if (shouldIgnore) { const result = new LinkCheckResult(opts, link, 0, undefined); result.status = 'ignored'; // custom status for ignored links callback(null, result); return; } } if (opts.replacementPatterns) { for (let replacementPattern of opts.replacementPatterns) { let pattern = replacementPattern.pattern instanceof RegExp ? replacementPattern.pattern : new RegExp(replacementPattern.pattern); link = link.replace(pattern, performSpecialReplacements(replacementPattern.replacement, opts)); } } // Make sure it is not undefined and that the appropriate headers are always recalculated for a given link. opts.headers = {}; if (opts.httpHeaders) { for (const httpHeader of opts.httpHeaders) { if (httpHeader.headers) { for (const header of Object.keys(httpHeader.headers)) { httpHeader.headers[header] = performSpecialReplacements(httpHeader.headers[header], opts); } } for (const url of httpHeader.urls) { if (link.startsWith(url)) { Object.assign(opts.headers, httpHeader.headers); // The headers of this httpHeader has been applied, the other URLs of this httpHeader don't need to be evaluated any further. break; } } } } linkCheck(link, opts, function (err, result) { if (opts.showProgressBar) { bar.tick(); } if (err) { result = new LinkCheckResult(opts, link, 500, err); result.status = 'error'; // custom status for errored links } callback(null, result); }); }, callback); };