Skip to content
This repository has been archived by the owner on May 12, 2024. It is now read-only.

Commit

Permalink
Merge pull request #29 from gaurav-nelson/UpdatesAndFixes
Browse files Browse the repository at this point in the history
Updates and fixes
  • Loading branch information
gaurav-nelson committed Feb 11, 2021
2 parents 8e502a6 + 6eb277a commit 655c003
Show file tree
Hide file tree
Showing 8 changed files with 273 additions and 176 deletions.
16 changes: 0 additions & 16 deletions .github/workflows/publish-snap.yml

This file was deleted.

13 changes: 9 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
FROM node:latest
FROM node:alpine
LABEL maintainer="https://github.com/gaurav-nelson"

# Use the Open Container Image Annotation Spec (https://github.com/opencontainers/image-spec/blob/master/annotations.md)
LABEL org.opencontainers.image.title="asciidoc-link-check"
LABEL org.opencontainers.image.description="Checks if all hyperlinks in an asciidoc file are alive(or dead)."
LABEL org.opencontainers.image.documentation="https://github.com/gaurav-nelson/asciidoc-link-check/blob/master/README.md"
LABEL org.opencontainers.image.source="https://github.com/gaurav-nelson/asciidoc-link-check"

# Install app dependencies
COPY package.json /src/package.json
COPY package.json /src/
WORKDIR /src
RUN set -ex; \
npm install \
&& npm ls
npm install
# Bundle app source
COPY . /src
ENTRYPOINT [ "/src/asciidoc-link-check" ]
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,7 @@
Local Installation
==================

[![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg)](https://snapcraft.io/asciidoc-link-check)


```bash
sudo snap install asciidoc-link-check
```

Or install with `npm` run:
Install with `npm` run:

``` bash
npm install -g asciidoc-link-check
Expand Down Expand Up @@ -72,6 +65,12 @@ find <directory> -name \*.adoc -exec asciidoc-link-check -p {} \;
asciidoc-link-check README.adoc -c <configfile.json>
```

> **NOTE**
>
> To see other options that `asciidoc-link-check` supports, see
> `markdown-link-check`
> config file format at https://github.com/tcort/markdown-link-check#config-file-format
Using in your node project
--------------------------

Expand All @@ -95,9 +94,10 @@ Docker Run

docker build --tag asciidoc-link-check .

4. Pipe any asciidoc file `test.adoc` to `docker run`:
4. Add current directory with your `test.adoc` file as read only volume to the
`docker run` command:

docker run --rm -i asciidoc-link-check < test.adoc
docker run -v ${PWD}:/tmp:ro --rm -i asciidoc-link-check /tmp/test.adoc

API
===
Expand Down
224 changes: 142 additions & 82 deletions asciidoc-link-check
Original file line number Diff line number Diff line change
Expand Up @@ -2,62 +2,110 @@

'use strict';

var chalk = require('chalk');
var fs = require('fs');
var asciidocLinkCheck = require('./');
var program = require('commander');
const chalk = require('chalk');
const fs = require('fs');
const asciidocLinkCheck = require('./');
const program = require('commander');
const axios = require('axios');
var url = require('url');
var path = require('path');

var statusLabels = {
alive: chalk.green('✓'),
dead: chalk.red('✖'),
ignored: chalk.gray('~')
const url = require('url');
const path = require('path');

const statusLabels = {
alive: chalk.green('✓'),
dead: chalk.red('✖'),
ignored: chalk.blue('/'),
error: chalk.yellow('⚠'),
};

var error = false;
var opts = {};
var filenameOutput = "";
var stream = process.stdin; // read from stdin unless a filename is given
const opts = {};
let filenameForOutput = '';
let stream = process.stdin; // read from stdin unless a filename is given

function commaSeparatedCodesList(value, dummyPrevious) {
return value.split(',').map(function(item) {
return parseInt(item, 10);
});
}

program
.option('-p, --progress', 'Show progress bar')
.option('-c, --config [config]', 'Use a JSON config file')
.option('-q, --quiet', 'Display errors only')
.arguments('[filenameOrUrl]')
.action(function (filenameOrUrl) {
filenameOutput = filenameOrUrl;
if (/https?:/.test(filenameOrUrl)) {
stream = axios.get(filenameOrUrl);
try { // extract baseUrl from supplied URL
var parsed = url.parse(filenameOrUrl);
delete parsed.search;
delete parsed.hash;
if (parsed.pathname.lastIndexOf('/') !== -1) {
parsed.pathname = parsed.pathname.substr(0, parsed.pathname.lastIndexOf('/') + 1);
}
opts.baseUrl = url.format(parsed);
} catch (err) { /* ignore error */ }
} else {
opts.baseUrl = 'file://' + path.dirname(path.resolve(filenameOrUrl));
stream = fs.createReadStream(filenameOrUrl);
}
})
.parse(process.argv);
.option('-p, --progress', 'show progress bar')
.option('-c, --config [config]', 'apply a config file (JSON), holding e.g. url specific header configuration')
.option('-q, --quiet', 'displays errors only')
.option('-v, --verbose', 'displays detailed error information')
.option('-a, --alive <code>', 'comma separated list of HTTP codes to be considered as alive', commaSeparatedCodesList)
.option('-r, --retry', 'retry after the duration indicated in \'retry-after\' header when HTTP code is 429')
.arguments('[filenameOrUrl]')
.action(function (filenameOrUrl) {
filenameForOutput = filenameOrUrl;
if (/https?:/.test(filenameOrUrl)) {
axios(filenameOrUrl, function (error, response, body) {
if (error) {
console.error(chalk.red('\nERROR: Unable to connect! Please provide a valid URL as an argument.'));
process.exit(1);
}
else if (response.statusCode === 404){
console.error(chalk.red('\nERROR: 404 - File not found! Please provide a valid URL as an argument.'));
process.exit(1);
} else {
stream = axios.get(filenameOrUrl);
}

opts.showProgressBar = (program.progress === true); // force true or undefined to be true or false.
opts.quiet = (program.quiet === true);
});
try { // extract baseUrl from supplied URL
const parsed = url.parse(filenameOrUrl);
delete parsed.search;
delete parsed.hash;
if (parsed.pathname.lastIndexOf('/') !== -1) {
parsed.pathname = parsed.pathname.substr(0, parsed.pathname.lastIndexOf('/') + 1);
}
opts.baseUrl = url.format(parsed);
} catch (err) { /* ignore error */
}
} else {
fs.stat(filenameOrUrl, function(error , stats){
if (!error && stats.isDirectory()){
console.error(chalk.red('\nERROR: ' + filenameOrUrl + ' is a directory! Please provide a valid filename as an argument.'));
process.exit(1);
}
});
opts.baseUrl = 'file://' + path.dirname(path.resolve(filenameOrUrl));
stream = fs.createReadStream(filenameOrUrl);
}

var asciidoc = ''; // collect the asciidoc data, then process it
stream.on('data', function (chunk) {
asciidoc += chunk.toString();
}).on('end', function () {
console.log(chalk.cyan('\nFILE: ' + filenameOutput));
}).parse(process.argv);

if (program.config) {
fs.access(program.config, fs.constants.R_OK, function (err) {
const options = program.opts();

opts.showProgressBar = (program.progress === true); // force true or undefined to be true or false.
opts.quiet = (program.quiet === true);
opts.verbose = (program.verbose === true);
opts.retryOn429 = (program.retry === true);
opts.aliveStatusCodes = program.alive;
// set the projectBaseUrl to the current working directory, so that `{{BASEURL}}` can be resolved to the project root.
opts.projectBaseUrl = `file://${process.cwd()}`;

let asciidoc = ''; // collect the asciidoc data, then process it

stream
.on('data', function (chunk) {
asciidoc += chunk.toString();
})
.on('error', function(error) {
if (error.code === 'ENOENT') {
console.error(chalk.red('\nERROR: File not found! Please provide a valid filename as an argument.'));
} else {
console.error(chalk.red(error));
}
return process.exit(1);
})
.on('end', function () {
if (filenameForOutput) {
console.log(chalk.cyan('\nFILE: ' + filenameForOutput));
}
if (options.config) {
fs.access(options.config, (fs.constants || fs).R_OK, function (err) {
if (!err) {
let configStream = fs.createReadStream(program.config);
let configStream = fs.createReadStream(options.config);
let configData = '';

configStream.on('data', function (chunk) {
Expand All @@ -66,12 +114,20 @@ stream.on('data', function (chunk) {
let config = JSON.parse(configData);

opts.ignorePatterns = config.ignorePatterns;
opts.replacementPatterns = config.replacementPatterns;
opts.httpHeaders = config.httpHeaders;
opts.timeout = config.timeout;
opts.ignoreDisable = config.ignoreDisable;
opts.retryOn429 = config.retryOn429;
opts.retryCount = config.retryCount;
opts.fallbackRetryDelay = config.fallbackRetryDelay;
opts.aliveStatusCodes = config.aliveStatusCodes;

runasciidocLinkCheck(asciidoc, opts);
});
}
else {
console.error(chalk.red('\nERROR: Can\'t access configuration file.'));
console.error(chalk.red('\nERROR: Config file not accessible.'));
process.exit(1);
}
});
Expand All @@ -82,37 +138,41 @@ stream.on('data', function (chunk) {
});

function runasciidocLinkCheck(asciidoc, opts) {
asciidocLinkCheck(asciidoc, opts, function (err, results) {
if (err) {
console.error(chalk.red('\nERROR: something went wrong!'));
console.error(err.stack);
process.exit(1);
}

if (results.length === 0 && !opts.quiet) {
console.log(chalk.yellow('No hyperlinks found!'));
}
results.forEach(function (result) {
// Skip messages for non-deadlinks in quiet mode.
if (opts.quiet && result.status !== 'dead') {
return;
}

asciidocLinkCheck(asciidoc, opts, function (err, results) {

if (results.length === 0) {
console.log(chalk.yellow('No hyperlinks found!\n'));
}
results.forEach(function (result) {
if (result.status === 'dead') {
error = true;
}

// Skip messages for non-deadlinks in quiet mode.
if (opts.quiet && result.status !== 'dead') {
return;
}

switch(result.status) {
case 'dead':
console.log('[%s] %s', statusLabels[result.status], chalk.red(result.link));
break;
case 'ignored':
console.log('[%s] %s', statusLabels[result.status], chalk.gray(result.link));
break;
default:
console.log('[%s] %s', statusLabels[result.status], result.link);
}

});
if (error) {
console.error(chalk.red('ERROR: dead links found!\n'));
process.exit(1);
}
});
}
if (opts.verbose) {
if (result.err) {
console.log('[%s] %s → Status: %s %s', statusLabels[result.status], result.link, result.statusCode, result.err);
} else {
console.log('[%s] %s → Status: %s', statusLabels[result.status], result.link, result.statusCode);
}
}
else {
console.log('[%s] %s', statusLabels[result.status], result.link);
}
});
console.log('\n%s links checked.', results.length);
if (results.some((result) => result.status === 'dead')) {
let deadLinks = results.filter(result => { return result.status === 'dead'; });
console.error(chalk.red('\nERROR: %s dead links found!'), deadLinks.length);
deadLinks.forEach(function (result) {
console.log('[%s] %s → Status: %s', statusLabels[result.status], result.link, result.statusCode);
});
process.exit(1);
}
});
}
Loading

0 comments on commit 655c003

Please sign in to comment.