• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const fs = require('fs');
4const { extname, join, resolve } = require('path');
5const unified = require('unified');
6const { pathToFileURL } = require('url');
7const DIR = resolve(process.argv[2]);
8
9console.log('Running Markdown link checker...');
10findMarkdownFilesRecursively(DIR);
11
12function* getLinksRecursively(node) {
13  if (node.url && !node.url.startsWith('#')) {
14    yield node;
15  }
16  for (const child of node.children || []) {
17    yield* getLinksRecursively(child);
18  }
19}
20
21function findMarkdownFilesRecursively(dirPath) {
22  const entries = fs.readdirSync(dirPath, { withFileTypes: true });
23
24  for (const entry of entries) {
25    const path = join(dirPath, entry.name);
26
27    if (
28      entry.isDirectory() &&
29      entry.name !== 'api' &&
30      entry.name !== 'build' &&
31      entry.name !== 'changelogs' &&
32      entry.name !== 'deps' &&
33      entry.name !== 'fixtures' &&
34      entry.name !== 'gyp' &&
35      entry.name !== 'node_modules' &&
36      entry.name !== 'out' &&
37      entry.name !== 'tmp'
38    ) {
39      findMarkdownFilesRecursively(path);
40    } else if (entry.isFile() && extname(entry.name) === '.md') {
41      checkFile(path);
42    }
43  }
44}
45
46function checkFile(path) {
47  const tree = unified()
48    .use(require('remark-parse'))
49    .parse(fs.readFileSync(path));
50
51  const base = pathToFileURL(path);
52  let previousDefinitionLabel;
53  for (const node of getLinksRecursively(tree)) {
54    const targetURL = new URL(node.url, base);
55    if (targetURL.protocol === 'file:' && !fs.existsSync(targetURL)) {
56      const { line, column } = node.position.start;
57      console.error((process.env.GITHUB_ACTIONS ?
58        `::error file=${path},line=${line},col=${column}::` : '') +
59        `Broken link at ${path}:${line}:${column} (${node.url})`);
60      process.exitCode = 1;
61    }
62    if (node.type === 'definition') {
63      if (previousDefinitionLabel &&
64          previousDefinitionLabel > node.label) {
65        const { line, column } = node.position.start;
66        console.error((process.env.GITHUB_ACTIONS ?
67          `::error file=${path},line=${line},col=${column}::` : '') +
68          `Unordered reference at ${path}:${line}:${column} (` +
69          `"${node.label}" should be before "${previousDefinitionLabel})"`
70        );
71        process.exitCode = 1;
72      }
73      previousDefinitionLabel = node.label;
74    }
75  }
76}
77