8000 build: use `ESLint` class to generate formatter examples by mdjermanovic · Pull Request #19972 · eslint/eslint · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
build: use ESLint class to generate formatter examples
  • Loading branch information
mdjermanovic committed Jul 26, 2025
commit 74aa245902cf5bb2ae68ffcc661509d323b7f364
92 changes: 11 additions & 81 deletions Makefile.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ const checker = require("npm-license"),
path = require("node:path"),
semver = require("semver"),
ejs = require("ejs"),
{ CLIEngine } = require("./lib/cli-engine"),
builtinRules = require("./lib/rules"),
childProcess = require("node:child_process");

Expand Down Expand Up @@ -154,24 +153,19 @@ function generateBlogPost(releaseInfo, prereleaseMajorVersion) {

/**
* Generates a doc page with formatter result examples
* @param {Object} formatterInfo Linting results from each formatter
* @returns {void}
*/
function generateFormatterExamples(formatterInfo) {
const output = ejs.render(
cat("./templates/formatter-examples.md.ejs"),
formatterInfo,
);
const outputDir = path.join(DOCS_SRC_DIR, "use/formatters/"),
filename = path.join(outputDir, "index.md"),
htmlFilename = path.join(outputDir, "html-formatter-example.html");

if (!test("-d", outputDir)) {
mkdir(outputDir);
function generateFormatterExamples() {
// We don't need the stack trace of execFileSync if the command fails.
try {
childProcess.execFileSync(
process.execPath,
["tools/generate-formatter-examples.js"],
{ stdio: "inherit" },
);
} catch {
exit(1);
}

output.to(filename);
formatterInfo.formatterResults.html.result.to(htmlFilename);
}

/**
Expand Down Expand Up @@ -523,70 +517,6 @@ function getFirstVersionOfDeletion(filePath) {
.sort(semver.compare)[0];
}

/**
* Gets linting results from every formatter, based on a hard-coded snippet and config
* @returns {Object} Output from each formatter
*/
function getFormatterResults() {
const util = require("node:util");
const formattersMetadata = require("./lib/cli-engine/formatters/formatters-meta.json");

const formatterFiles = fs
.readdirSync("./lib/cli-engine/formatters/")
7440 .filter(fileName => !fileName.includes("formatters-meta.json")),
rules = {
"no-else-return": "warn",
indent: ["warn", 4],
"space-unary-ops": "error",
semi: ["warn", "always"],
"consistent-return": "error",
},
cli = new CLIEngine({
useEslintrc: false,
baseConfig: { extends: "eslint:recommended" },
rules,
}),
codeString = [
"function addOne(i) {",
" if (i != NaN) {",
" return i ++",
" } else {",
" return",
" }",
"};",
].join("\n"),
rawMessages = cli.executeOnText(codeString, "fullOfProblems.js", true),
rulesMap = cli.getRules(),
rulesMeta = {};

Object.keys(rules).forEach(ruleId => {
rulesMeta[ruleId] = rulesMap.get(ruleId).meta;
});

return formatterFiles.reduce(
(data, filename) => {
const fileExt = path.extname(filename),
name = path.basename(filename, fileExt);

if (fileExt === ".js") {
const formattedOutput = cli.getFormatter(name)(
rawMessages.results,
{ rulesMeta },
);

data.formatterResults[name] = {
result: util.stripVTControlCharacters(formattedOutput),
description: formattersMetadata.find(
formatter => formatter.name === name,
).description,
};
}
return data;
},
{ formatterResults: {} },
);
}

/**
* Gets a path to an executable in node_modules/.bin
* @param {string} command The executable name
Expand Down Expand Up @@ -745,7 +675,7 @@ target.gensite = function () {

// 3. Create Example Formatter Output Page
echo("> Creating the formatter examples (Step 3)");
generateFormatterExamples(getFormatterResults());
generateFormatterExamples();

echo("Done generating documentation");
};
Expand Down
114 changes: 114 additions & 0 deletions tools/generate-formatter-examples.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* @fileoverview Generates documentation files for formatters:
* - docs/src/use/formatters/index.md
* - docs/src/use/formatters/html-formatter-example.html
* @author Milos Djermanovic
*/

"use strict";

//-----------------------------------------------------------------------------
// Requirements
//-----------------------------------------------------------------------------

const fs = require("node:fs/promises");
const path = require("node:path");
const util = require("node:util");

const ejs = require("ejs");

const { ESLint } = require("../lib/api");
const { defineConfig } = require("../lib/config-api");
const js = require("../packages/js");

const formattersMetadata = require("../lib/cli-engine/formatters/formatters-meta.json");

//-----------------------------------------------------------------------------
// Helpers
//-----------------------------------------------------------------------------

const FORMATTERS_DOCS_DIR = path.join(__dirname, "../docs/src/use/formatters");
const INDEX_FILENAME = path.resolve(FORMATTERS_DOCS_DIR, "index.md");
const HTML_FORMATTER_FILENAME = path.resolve(
FORMATTERS_DOCS_DIR,
"html-formatter-example.html",
);

const TEMPLATE_FILENAME = path.resolve(
__dirname,
"../templates/formatter-examples.md.ejs",
);

const exampleCode = [
"function addOne(i) {",
" if (i != NaN) {",
" return i ++",
" } else {",
" return",
" }",
"};",
].join("\n");

const exampleConfig = defineConfig([
js.configs.recommended,
{
rules: {
"consistent-return": 2,
indent: [1, 4],
"no-else-return": 1,
semi: [1, "always"],
"space-unary-ops": 2,
},
},
]);

/**
* Gets linting results from every formatter, based on a hard-coded snippet and config
* @returns {Promise<Object>} Output from each formatter
*/
async function getFormatterResults() {
const eslint = new ESLint({
ignore: false,
overrideConfigFile: true,
baseConfig: exampleConfig,
});

const lintResults = await eslint.lintText(exampleCode, {
filePath: "fullOfProblems.js",
});

return Object.fromEntries(
await Promise.all(
formattersMetadata.map(async ({ name, description }) => {
const formatter = await eslint.loadFormatter(name);

return [
name,
{
result: util.stripVTControlCharacters(
formatter.format(lintResults),
),
description,
},
];
}),
),
);
}

//-----------------------------------------------------------------------------
// CLI
//-----------------------------------------------------------------------------

(async () => {
const formatterResults = await getFormatterResults();
const indexFileContent = ejs.render(
await fs.readFile(TEMPLATE_FILENAME, "utf8"),
{ formatterResults },
);

await Promise.all([
fs.writeFile(INDEX_FILENAME, indexFileContent),
fs.writeFile(HTML_FORMATTER_FILENAME, formatterResults.html.result),
]);
})();
Loading
0