UNPKG

4.64 kBJavaScriptView Raw
1#!/usr/bin/env node
2
3import fs from "fs";
4import path from "path";
5import { fileURLToPath } from "url";
6import yargs from "yargs";
7import { walk } from "../lib/glob.js";
8import { normalize } from "../lib/normalize.js";
9import { metalint } from "../lib/index.js";
10import { SEVERITY } from "../lib/severity.js";
11
12/**
13 * @typedef {import("../lib/types").Checker} Checker
14 * @typedef {import("../lib/types").Formatter} Formatter
15 */
16
17const DIRNAME = path.dirname(fileURLToPath(import.meta.url));
18
19const argv = yargs(process.argv.slice(2)).options({
20 c: {
21 alias: "config",
22 default: ".metalint/metalint.config.js",
23 requiresArg: true,
24 type: "string",
25 },
26 f: {
27 alias: "formatter",
28 requiresArg: true,
29 type: "string",
30 },
31 l: {
32 alias: "level",
33 requiresArg: true,
34 type: "string",
35 },
36 o: {
37 alias: "output",
38 requiresArg: true,
39 type: "string",
40 },
41 p: {
42 alias: "patterns",
43 requiresArg: true,
44 type: "array",
45 },
46 help: {
47 alias: "help",
48 type: "boolean",
49 },
50 version: {
51 alias: "version",
52 type: "boolean",
53 },
54}).help(false).version(false).argv;
55
56/**
57 * Vérifie (en appelant des linters) une liste de fichiers.
58 *
59 * @param {string[]} files La liste des fichiers.
60 * @param {Checker[]} checkers La liste des vérifications faites sur les
61 * fichiers.
62 * @param {string} root L'adresse du répertoire où se trouve le
63 * dossier <code>.metalint/</code>.
64 * @param {Formatter[]} reporters La liste des rapporteurs utilisés pour
65 * afficher les résultats.
66 * @returns {Promise<?number>} La sévérité la plus élévée des résultats.
67 */
68const check = async function (files, checkers, root, reporters) {
69 /** @type {?number} */
70 let severity = null;
71
72 const results = await metalint(files, checkers, root);
73 for (const [file, notices] of Object.entries(results)) {
74 // Déterminer la sévérité la plus élévée des résultats.
75 if (null !== notices) {
76 for (const notice of notices) {
77 if (null === severity || severity > notice.severity) {
78 severity = notice.severity;
79 }
80 }
81 }
82
83 // Afficher les notifications avec chaque rapporteur.
84 for (const reporter of reporters) {
85 reporter.notify(file, notices);
86 }
87 }
88
89 // Attendre tous les rapporteurs.
90 await Promise.all(reporters.map((r) => r.finalize(severity)));
91 return severity;
92};
93
94if (argv.help) {
95 process.stdout.write(fs.readFileSync(path.join(DIRNAME,
96 "/../help/index.txt")));
97 process.exit(0);
98}
99if (argv.version) {
100 const manifest = JSON.parse(fs.readFileSync(path.join(DIRNAME,
101 "/../package.json"),
102 "utf-8"));
103 process.stdout.write("Metalint " + manifest.version + "\n");
104 process.exit(0);
105}
106
107let root = process.cwd();
108// Rechercher le fichier de configuration dans le répertoire courant, puis dans
109// les parents.
110while (!fs.existsSync(path.join(root, argv.config))) {
111 // Si on est remonté à la racine.
112 if (path.join(root, "..") === root) {
113 process.stderr.write("metalint: no such config file.\n");
114 process.exit(10);
115 }
116 root = path.join(root, "..");
117}
118
119import(path.join(root, argv.config)).then(async ({ default: config }) => {
120 try {
121 const { patterns, checkers, reporters } =
122 await normalize(config,
123 root,
124 path.dirname(path.join(root, argv.config)),
125 argv);
126
127 const files = walk(argv._, patterns, root);
128
129 const severity = await check(files, checkers, root, reporters);
130 let code;
131 switch (severity) {
132 case SEVERITY.FATAL: code = 2; break;
133 case SEVERITY.ERROR: code = 1; break;
134 default: code = 0;
135 }
136 process.exit(code);
137 } catch (err) {
138 process.stderr.write("metalint: " + err.message + "\n");
139 process.stderr.write(err.stack + "\n");
140 process.exit(11);
141 }
142}).catch((err) => {
143 process.stderr.write("metalint: " + err.message + "\n");
144 process.stderr.write(err.stack + "\n");
145 process.exit(13);
146});