Skip to content

Commit 27fa865

Browse files
authored
build: use ESLint class to generate formatter examples (#19972)
* build: use `ESLint` class to generate formatter examples * ignore `tools/generate-formatter-examples.js` in knip config
1 parent 2364031 commit 27fa865

File tree

3 files changed

+127
-81
lines changed

3 files changed

+127
-81
lines changed

Makefile.js

Lines changed: 11 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ const checker = require("npm-license"),
1919
path = require("node:path"),
2020
semver = require("semver"),
2121
ejs = require("ejs"),
22-
{ CLIEngine } = require("./lib/cli-engine"),
2322
builtinRules = require("./lib/rules"),
2423
childProcess = require("node:child_process");
2524

@@ -154,24 +153,19 @@ function generateBlogPost(releaseInfo, prereleaseMajorVersion) {
154153

155154
/**
156155
* Generates a doc page with formatter result examples
157-
* @param {Object} formatterInfo Linting results from each formatter
158156
* @returns {void}
159157
*/
160-
function generateFormatterExamples(formatterInfo) {
161-
const output = ejs.render(
162-
cat("./templates/formatter-examples.md.ejs"),
163-
formatterInfo,
164-
);
165-
const outputDir = path.join(DOCS_SRC_DIR, "use/formatters/"),
166-
filename = path.join(outputDir, "index.md"),
167-
htmlFilename = path.join(outputDir, "html-formatter-example.html");
168-
169-
if (!test("-d", outputDir)) {
170-
mkdir(outputDir);
158+
function generateFormatterExamples() {
159+
// We don't need the stack trace of execFileSync if the command fails.
160+
try {
161+
childProcess.execFileSync(
162+
process.execPath,
163+
["tools/generate-formatter-examples.js"],
164+
{ stdio: "inherit" },
165+
);
166+
} catch {
167+
exit(1);
171168
}
172-
173-
output.to(filename);
174-
formatterInfo.formatterResults.html.result.to(htmlFilename);
175169
}
176170

177171
/**
@@ -523,70 +517,6 @@ function getFirstVersionOfDeletion(filePath) {
523517
.sort(semver.compare)[0];
524518
}
525519

526-
/**
527-
* Gets linting results from every formatter, based on a hard-coded snippet and config
528-
* @returns {Object} Output from each formatter
529-
*/
530-
function getFormatterResults() {
531-
const util = require("node:util");
532-
const formattersMetadata = require("./lib/cli-engine/formatters/formatters-meta.json");
533-
534-
const formatterFiles = fs
535-
.readdirSync("./lib/cli-engine/formatters/")
536-
.filter(fileName => !fileName.includes("formatters-meta.json")),
537-
rules = {
538-
"no-else-return": "warn",
539-
indent: ["warn", 4],
540-
"space-unary-ops": "error",
541-
semi: ["warn", "always"],
542-
"consistent-return": "error",
543-
},
544-
cli = new CLIEngine({
545-
useEslintrc: false,
546-
baseConfig: { extends: "eslint:recommended" },
547-
rules,
548-
}),
549-
codeString = [
550-
"function addOne(i) {",
551-
" if (i != NaN) {",
552-
" return i ++",
553-
" } else {",
554-
" return",
555-
" }",
556-
"};",
557-
].join("\n"),
558-
rawMessages = cli.executeOnText(codeString, "fullOfProblems.js", true),
559-
rulesMap = cli.getRules(),
560-
rulesMeta = {};
561-
562-
Object.keys(rules).forEach(ruleId => {
563-
rulesMeta[ruleId] = rulesMap.get(ruleId).meta;
564-
});
565-
566-
return formatterFiles.reduce(
567-
(data, filename) => {
568-
const fileExt = path.extname(filename),
569-
name = path.basename(filename, fileExt);
570-
571-
if (fileExt === ".js") {
572-
const formattedOutput = cli.getFormatter(name)(
573-
rawMessages.results,
574-
{ rulesMeta },
575-
);
576-
577-
data.formatterResults[name] = {
578-
result: util.stripVTControlCharacters(formattedOutput),
579-
description: formattersMetadata.find(
580-
formatter => formatter.name === name,
581-
).description,
582-
};
583-
}
584-
return data;
585-
},
586-
{ formatterResults: {} },
587-
);
588-
}
589-
590520
/**
591521
* Gets a path to an executable in node_modules/.bin
592522
* @param {string} command The executable name
@@ -745,7 +675,7 @@ target.gensite = function () {
745675

746676
// 3. Create Example Formatter Output Page
747677
echo("> Creating the formatter examples (Step 3)");
748-
generateFormatterExamples(getFormatterResults());
678+
generateFormatterExamples();
749679

750680
echo("Done generating documentation");
751681
};

knip.jsonc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
"tests/performance/jshint.js",
2222
// Many are required using dynamic paths such as `fs.readFileSync(path.join())`:
2323
"tests/fixtures/**",
24+
// Run from Makefile.js
25+
"tools/generate-formatter-examples.js",
2426
],
2527
"ignoreDependencies": [
2628
"c8",
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/**
2+
* @fileoverview Generates documentation files for formatters:
3+
* - docs/src/use/formatters/index.md
4+
* - docs/src/use/formatters/html-formatter-example.html
5+
* @author Milos Djermanovic
6+
*/
7+
8+
"use strict";
9+
10+
//-----------------------------------------------------------------------------
11+
// Requirements
12+
//-----------------------------------------------------------------------------
13+
14+
const fs = require("node:fs/promises");
15+
const path = require("node:path");
16+
const util = require("node:util");
17+
18+
const ejs = require("ejs");
19+
20+
const { ESLint } = require("../lib/api");
21+
const { defineConfig } = require("../lib/config-api");
22+
const js = require("../packages/js");
23+
24+
const formattersMetadata = require("../lib/cli-engine/formatters/formatters-meta.json");
25+
26+
//-----------------------------------------------------------------------------
27+
// Helpers
28+
//-----------------------------------------------------------------------------
29+
30+
const FORMATTERS_DOCS_DIR = path.join(__dirname, "../docs/src/use/formatters");
31+
const INDEX_FILENAME = path.resolve(FORMATTERS_DOCS_DIR, "index.md");
32+
const HTML_FORMATTER_FILENAME = path.resolve(
33+
FORMATTERS_DOCS_DIR,
34+
"html-formatter-example.html",
35+
);
36+
37+
const TEMPLATE_FILENAME = path.resolve(
38+
__dirname,
39+
"../templates/formatter-examples.md.ejs",
40+
);
41+
42+
const exampleCode = [
43+
"function addOne(i) {",
44+
" if (i != NaN) {",
45+
" return i ++",
46+
" } else {",
47+
" return",
48+
" }",
49+
"};",
50+
].join("\n");
51+
52+
const exampleConfig = defineConfig([
53+
js.configs.recommended,
54+
{
55+
rules: {
56+
"consistent-return": 2,
57+
indent: [1, 4],
58+
"no-else-return": 1,
59+
semi: [1, "always"],
60+
"space-unary-ops": 2,
61+
},
62+
},
63+
]);
64+
65+
/**
66+
* Gets linting results from every formatter, based on a hard-coded snippet and config
67+
* @returns {Promise<Object>} Output from each formatter
68+
*/
69+
async function getFormatterResults() {
70+
const eslint = new ESLint({
71+
ignore: false,
72+
overrideConfigFile: true,
73+
baseConfig: exampleConfig,
74+
});
75+
76+
const lintResults = await eslint.lintText(exampleCode, {
77+
filePath: "fullOfProblems.js",
78+
});
79+
80+
return Object.fromEntries(
81+
await Promise.all(
82+
formattersMetadata.map(async ({ name, description }) => {
83+
const formatter = await eslint.loadFormatter(name);
84+
85+
return [
86+
name,
87+
{
88+
result: util.stripVTControlCharacters(
89+
formatter.format(lintResults),
90+
),
91+
description,
92+
},
93+
];
94+
}),
95+
),
96+
);
97+
}
98+
99+
//-----------------------------------------------------------------------------
100+
// CLI
101+
//-----------------------------------------------------------------------------
102+
103+
(async () => {
104+
const formatterResults = await getFormatterResults();
105+
const indexFileContent = ejs.render(
106+
await fs.readFile(TEMPLATE_FILENAME, "utf8"),
107+
{ formatterResults },
108+
);
109+
110+
await Promise.all([
111+
fs.writeFile(INDEX_FILENAME, indexFileContent),
112+
fs.writeFile(HTML_FORMATTER_FILENAME, formatterResults.html.result),
113+
]);
114+
})();

0 commit comments

Comments
 (0)