-
Notifications
You must be signed in to change notification settings - Fork 23.2k
Expand file tree
/
Copy pathfront-matter_linter.js
More file actions
121 lines (107 loc) · 3.19 KB
/
front-matter_linter.js
File metadata and controls
121 lines (107 loc) · 3.19 KB
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
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { eachLimit } from "async";
import cliProgress from "cli-progress";
import { fdir } from "fdir";
import caporal from "@caporal/core";
const { program } = caporal;
import { getAjvValidator, checkFrontMatter } from "./front-matter_utils.js";
async function resolveDirectory(file) {
const stats = await fs.lstat(file);
if (stats.isDirectory()) {
const api = new fdir()
.withErrors()
.withFullPaths()
.filter((filePath) => filePath.endsWith("index.md"))
.exclude((dirName) => dirName === "conflicting" || dirName === "orphaned")
.crawl(file);
return api.withPromise();
} else if (
stats.isFile() &&
file.endsWith("index.md") &&
!file.includes("/conflicting/") &&
!file.includes("/orphaned/") &&
!file.includes("tests/front-matter_test_files")
) {
return [file];
} else {
return [];
}
}
// lint front matter
async function lintFrontMatter(filesAndDirectories, options) {
const files = (
await Promise.all(filesAndDirectories.map(resolveDirectory))
).flat();
options.config = JSON.parse(await fs.readFile(options.configFile, "utf-8"));
options.validator = getAjvValidator(options.config.schema);
const progressBar = new cliProgress.SingleBar({ etaBuffer: 100 });
progressBar.start(files.length, 0);
const errors = [];
const fixableErrors = [];
await eachLimit(files, os.cpus().length, async (file) => {
try {
const [error, fixableError, content] = await checkFrontMatter(
file,
options,
);
if (content) {
fs.writeFile(file, content);
}
error && errors.push(error);
fixableError && fixableErrors.push(fixableError);
} catch (err) {
errors.push(`${err}\n ${err.stack}`);
} finally {
progressBar.increment();
}
});
progressBar.stop();
console.log(errors.length, fixableErrors.length);
if (errors.length || fixableErrors.length) {
let msg = errors.map((error) => `${error}`).join("\n\n");
if (fixableErrors.length) {
msg +=
"\n\nFollowing fixable errors can be fixed using '--fix true' option\n";
msg += fixableErrors.map((error) => `${error}`).join("\n");
}
throw new Error(msg);
}
}
function tryOrExit(f) {
return async ({ options = {}, ...args }) => {
try {
await f({ options, ...args });
} catch (error) {
if (options.verbose || options.v) {
console.error(error.stack);
}
throw error;
}
};
}
program
.option("--fix", "Save corrected output", {
validator: program.BOOLEAN,
default: false,
})
.option("--config-file", "Custom configuration file", {
validator: program.STRING,
default: "./front-matter-config.json",
})
.argument("[files...]", "list of files and/or directories to check", {
default: ["./files/en-us"],
})
.action(
tryOrExit(({ args, options, logger }) => {
const cwd = process.cwd();
const files = (args.files || []).map((f) => path.resolve(cwd, f));
if (!files.length) {
logger.info("No files to lint.");
return;
}
return lintFrontMatter(files, options);
}),
);
program.run();