forked from aframevr/aframe
-
Notifications
You must be signed in to change notification settings - Fork 0
/
docsLint.js
159 lines (138 loc) · 4.6 KB
/
docsLint.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/* eslint-disable no-useless-escape */
'use strict';
const chalk = require('chalk');
const glob = require('glob');
const fs = require('fs');
const path = require('path');
const writeGood = require('write-good');
let errorCount = 0;
let warningCount = 0;
const errors = {};
const warnings = {};
let pages = glob.sync('docs/**/*.md');
pages.forEach(function checkPage (pagePath) {
var match;
var referencedMatch;
var referencingMatch;
var referencingRegex;
var urlRegex;
let content = fs.readFileSync(pagePath, 'utf-8');
// Check prose with `write-good`.
checkProse(pagePath, content);
// Inline links: `[link](../page.md)`
let inlineLinkRegex = /\[.*?\]\((.*?)\)/g;
match = inlineLinkRegex.exec(content);
while (match !== null) {
checkLink(pagePath, match[1], 'Page does not exist');
match = inlineLinkRegex.exec(content);
}
// Referenced links: `[link][page] -> [page]: ../page.md`
let referencedLinkRegex = /\[.*?\]\[(.*?)\]/g;
match = referencedLinkRegex.exec(content);
while (match !== null) {
urlRegex = new RegExp(`\\[${match[1]}\\]: \(\.\*\)`, 'g');
referencedMatch = urlRegex.exec(content);
if (referencedMatch) {
if (!checkLink(pagePath, referencedMatch[1])) {
addError(pagePath, referencedMatch[0], 'Page does not exist');
}
} else {
addError(pagePath, match[0], 'Link definition does not exist');
}
match = referencedLinkRegex.exec(content);
}
// Unused defined links: `[page]: ../page.md -> [*][page]`
let referenceRegex = new RegExp(`\\[\(\.\*\)\?\\]: \.\*`, 'g');
match = referenceRegex.exec(content);
while (match !== null) {
referencingRegex = new RegExp(`\\[${match[1]}\\]`, 'g');
referencingMatch = referencingRegex.exec(content);
if (!referencingMatch) {
addError(pagePath, match[1], 'Link definition not being used');
}
match = referenceRegex.exec(content);
}
});
function checkLink (pagePath, url) {
var absPath;
var content;
var hash;
var headingMatch;
var headingRegex;
var headingIds;
var urlPath;
// Relative path.
if (url.indexOf('.') === 0) {
// Check page exists.
urlPath = url.split('#')[0];
absPath = path.resolve(pagePath, `../${urlPath}`);
if (!fs.existsSync(absPath)) { return false; }
if (url.indexOf('#') === -1) { return true; }
// Check hash / anchor heading.
headingIds = [];
hash = url.split('#')[1];
headingRegex = /#+\s+(.*?)\n/g;
content = fs.readFileSync(absPath, 'utf-8');
headingMatch = headingRegex.exec(content);
while (headingMatch !== null) {
headingIds.push(convertHeading(headingMatch[1]));
headingMatch = headingRegex.exec(content);
}
return headingIds.indexOf(hash) !== -1;
}
return true;
}
function convertHeading (heading) {
return heading
.replace(/#+\s+/, '')
.toLowerCase()
.replace(/`\./g, '') // Remove leading dot.
.replace(/[`*\(\),]/g, '') // Remove special characters.
.replace(/[^\w]+/g, '-')
.replace(/-$/g, '');
}
function checkProse (pagePath, content) {
content = content.replace(/^---[\s\S]*?---/g, ''); // Remove metadata.
content = content.replace(/(\|.*\|)+/g, ''); // Remove tables.
content = content.replace(/```[\s\S]*?```/g, ''); // Remove code blocks.
content = content.replace(/`[\s\S]*?`/g, ''); // Remove inline code.
content = content.replace(/\n#+.*/g, ''); // Remove headings.
writeGood(content, {adverb: false, illusion: false}).forEach(function add (error) {
addWarning(
pagePath,
`...${content.substring(error.index - 10, error.index + error.offset + 10).replace(/\n/g, '')}...`,
error.reason);
});
}
function addError (pagePath, str, message) {
if (!errors[pagePath]) { errors[pagePath] = []; }
errors[pagePath].push({
message: message,
str: str
});
errorCount++;
}
function addWarning (pagePath, str, message) {
if (!warnings[pagePath]) { warnings[pagePath] = []; }
warnings[pagePath].push({
message: message,
str: str
});
warningCount++;
}
Object.keys(warnings).forEach(function (pagePath) {
console.log(chalk.yellow.bold(`\n[warn]: ${pagePath}`));
warnings[pagePath].forEach(function (warning) {
console.log(chalk.yellow(` ${warning.message}: `) + `${warning.str}`);
});
});
Object.keys(errors).forEach(function (pagePath) {
console.log(chalk.red.bold(`\n[error]: ${pagePath}`));
errors[pagePath].forEach(function (error) {
console.log(chalk.red(` ${error.message}: `) + `${error.str}`);
});
});
console.log(chalk.yellow.bold(`${warningCount} warnings`));
console.log(chalk.red.bold(`${errorCount} errors`));
// Fail.
if (errorCount) { process.exit(1); }