From fb0094b08ed01e728c64361774089c8c5983f127 Mon Sep 17 00:00:00 2001 From: Zak Burke Date: Thu, 16 May 2024 12:44:07 -0400 Subject: [PATCH 1/4] STCLI-207 provide lint command Provide a `lint` command. Making `eslint` a direct dependency here and providing a command to access it allows this package to serve as a conduit through which other packages can access it without having to maintain it as a direct dependency. Refs STCLI-207 --- CHANGELOG.md | 2 ++ lib/commands/lint.js | 71 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 4 +-- 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 lib/commands/lint.js diff --git a/CHANGELOG.md b/CHANGELOG.md index f8152f3..4a4b43b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## 3.2.0 IN PROGRESS +* Provide `lint` command. Refs STCLI-207. + ## [3.1.0](https://github.com/folio-org/stripes-cli/tree/v3.1.0) (2024-03-12) [Full Changelog](https://github.com/folio-org/stripes-cli/compare/v3.0.0...v3.1.0) diff --git a/lib/commands/lint.js b/lib/commands/lint.js new file mode 100644 index 0000000..d7b497f --- /dev/null +++ b/lib/commands/lint.js @@ -0,0 +1,71 @@ +const importLazy = require('import-lazy')(require); + +const { contextMiddleware } = importLazy('../cli/context-middleware'); +const fs = importLazy('fs'); +const process = importLazy('process'); +const _ = importLazy('lodash'); +const { ESLint } = require('eslint'); + +function createESLintInstance(overrideConfig) { + return new ESLint({ overrideConfig }); +} + +const lineFormat = (line, column) => `${_.padStart(line, 4)}:${_.padEnd(column, 3)}`; +const messageFormat = (message) => _.padEnd(message, 90); +const severityFormat = (severity) => _.padEnd(severity === 1 ? 'warning' : 'error', 10); +const messagesFormat = (list) => { + return list.map(m => `${lineFormat(m.line, m.column)}${severityFormat(m.severity)}${messageFormat(m.message)}${m.ruleId}`).join('\n'); +}; + +const resultFormatter = (message) => ( + `${message.filePath}\n${messagesFormat(message.messages)}\n` +); + +const lintCommand = async () => { + let rules = null; + if (fs.existsSync('.eslintrc')) { + rules = JSON.parse(fs.readFileSync('.eslintrc')); + } + + const eslint = createESLintInstance(rules); + const dirs = ['./lib', './src']; + + for (const dir of dirs) { + if (fs.existsSync(dir)) { + const results = await eslint.lintFiles(dir); + const warnings = results.reduce( + (acc, result) => acc + result.warningCount, + 0, + ); + const errors = results.reduce( + (acc, result) => acc + result.errorCount, + 0, + ); + + for (const m of results.filter(i => i.warningCount > 0 || i.errorCount > 0)) { + console.log(resultFormatter(m)); + } + + const dingCount = errors + warnings; + if (errors || warnings) { + console.warn(`✖ ${dingCount} problem${dingCount === 1 ? '' : 's'} (${errors} errors, ${warnings} warnings)`) + } + + if (errors) { + process.exit(1) + } + } + } +} + +module.exports = { + command: 'lint', + describe: 'lint', + builder: (yargs) => { + yargs + .middleware([ + contextMiddleware(), + ]); + }, + handler: lintCommand, +}; diff --git a/package.json b/package.json index 2a8ac27..7bbb46c 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "docs": "node ./lib/doc/generator" }, "dependencies": { + "@folio/eslint-config-stripes": "^7.0.0", "@folio/stripes-testing": "^3.0.0", "@folio/stripes-webpack": "^5.0.0", "@formatjs/cli": "^6.1.3", @@ -54,6 +55,7 @@ "karma-mocha-reporter": "^2.2.5", "karma-webpack": "^5.0.0", "kopy": "^9.4.2", + "eslint": "^7.32.0", "lodash": "^4.17.5", "minipass-fetch": "^3.0.4", "mocha": "^10.2.0", @@ -73,10 +75,8 @@ "yargs": "^17.3.1" }, "devDependencies": { - "@folio/eslint-config-stripes": "^7.0.0", "chai": "^4.1.2", "colors": "1.4.0", - "eslint": "^7.32.0", "sinon": "^15.0.4", "sinon-chai": "^3.7.0" } From 54fe764160305dd20e4cdb56d5a214d25c67b56b Mon Sep 17 00:00:00 2001 From: Zak Burke Date: Thu, 16 May 2024 12:54:33 -0400 Subject: [PATCH 2/4] lint hahahahaha :| --- lib/commands/lint.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/commands/lint.js b/lib/commands/lint.js index d7b497f..5e31f30 100644 --- a/lib/commands/lint.js +++ b/lib/commands/lint.js @@ -48,15 +48,15 @@ const lintCommand = async () => { const dingCount = errors + warnings; if (errors || warnings) { - console.warn(`✖ ${dingCount} problem${dingCount === 1 ? '' : 's'} (${errors} errors, ${warnings} warnings)`) + console.warn(`✖ ${dingCount} problem${dingCount === 1 ? '' : 's'} (${errors} errors, ${warnings} warnings)`); } if (errors) { - process.exit(1) + process.exit(1); } } } -} +}; module.exports = { command: 'lint', From dd6653fc16e6df89096b78cc53902e3015c7ce4f Mon Sep 17 00:00:00 2001 From: Zak Burke Date: Fri, 31 May 2024 08:39:00 -0400 Subject: [PATCH 3/4] even prettier printing --- lib/commands/lint.js | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/commands/lint.js b/lib/commands/lint.js index 5e31f30..b9a3e97 100644 --- a/lib/commands/lint.js +++ b/lib/commands/lint.js @@ -4,21 +4,24 @@ const { contextMiddleware } = importLazy('../cli/context-middleware'); const fs = importLazy('fs'); const process = importLazy('process'); const _ = importLazy('lodash'); +const chalk = importLazy('chalk'); + const { ESLint } = require('eslint'); function createESLintInstance(overrideConfig) { return new ESLint({ overrideConfig }); } -const lineFormat = (line, column) => `${_.padStart(line, 4)}:${_.padEnd(column, 3)}`; -const messageFormat = (message) => _.padEnd(message, 90); -const severityFormat = (severity) => _.padEnd(severity === 1 ? 'warning' : 'error', 10); +const lineFormat = (line, column) => chalk.green(`${_.padStart(line, 4)}:${_.padEnd(column, 3)}`); +const messageFormat = (message, maxLen) => _.padEnd(message, maxLen); +const severityFormat = (severity) => severity === 1 ? chalk.green(_.padEnd('warning', 10)) : chalk.red(_.padEnd('error', 10)); const messagesFormat = (list) => { - return list.map(m => `${lineFormat(m.line, m.column)}${severityFormat(m.severity)}${messageFormat(m.message)}${m.ruleId}`).join('\n'); + const maxLen = Math.max(...list.map(m => m.message.length)) + 2; + return list.map(m => `${lineFormat(m.line, m.column)}${severityFormat(m.severity)}${messageFormat(m.message, maxLen)}${m.ruleId}`).join('\n'); }; const resultFormatter = (message) => ( - `${message.filePath}\n${messagesFormat(message.messages)}\n` + `${chalk.underline(message.filePath)}\n${messagesFormat(message.messages)}\n` ); const lintCommand = async () => { @@ -42,13 +45,26 @@ const lintCommand = async () => { 0, ); + const fwarnings = results.reduce( + (acc, result) => acc + result.fixableWarningCount, + 0, + ); + const ferrors = results.reduce( + (acc, result) => acc + result.fixableErrorCount, + 0, + ); + for (const m of results.filter(i => i.warningCount > 0 || i.errorCount > 0)) { console.log(resultFormatter(m)); } const dingCount = errors + warnings; + const theme = errors ? chalk.bold.red : chalk.bold.green; if (errors || warnings) { - console.warn(`✖ ${dingCount} problem${dingCount === 1 ? '' : 's'} (${errors} errors, ${warnings} warnings)`); + console.warn(theme(`✖ ${dingCount} problem${dingCount === 1 ? '' : 's'} (${errors} errors, ${warnings} warnings)`)); + } + if (ferrors || fwarnings) { + console.warn(theme(` ${ferrors} error${ferrors === 1 ? '' : 's'} and ${fwarnings} warning${fwarnings === 1 ? '' : 's'} potentially fixable with the \`--fix\` option.`)); } if (errors) { From a0f23f5435655a59c605636ab761f07b9059dae9 Mon Sep 17 00:00:00 2001 From: Zak Burke Date: Fri, 21 Jun 2024 09:53:41 -0400 Subject: [PATCH 4/4] ahem; lint :laugh:. and doc. --- doc/commands.md | 77 ++++++++++++++++++++++++++++++++++++++++++++ lib/commands/lint.js | 6 ++-- 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/doc/commands.md b/doc/commands.md index fd41059..7865e68 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -14,6 +14,7 @@ This following command documentation is generated from the CLI's own built-in he * [`app perms` command](#app-perms-command) * [`build` command](#build-command) * [`inventory` command](#inventory-command) +* [`lint` command](#lint-command) * [`mod` command](#mod-command) * [`mod add` command](#mod-add-command) * [`mod descriptor` command](#mod-descriptor-command) @@ -51,6 +52,7 @@ This following command documentation is generated from the CLI's own built-in he * [`status` command](#status-command) * [`test` command](#test-command) * [`test karma` command](#test-karma-command) +* [`translate` command](#translate-command) * [`workspace` command](#workspace-command) * [`completion` command](#completion-command) @@ -1292,3 +1294,78 @@ Usage: ``` $ stripes completion ``` + +## `translate` command + +Translation utilities + +Usage: +``` +$ stripes translate [compile | pcheck | stats] +``` + +Sub-commands: +* [`stripes translate compile`](#translate-compile-command) +* [`stripes translate pcheck`](#translate-pcheck-command) +* [`stripes translate stats`](#translate-stats-command) + +### `translate compile` command + +Compile translations to AST. This is done automatically by CI workflows when merging any PR or publishing a release. Compiled translations must not be committed to source control. + +Usage: +``` +$ stripes translate compile +``` + +### `translate pcheck` command + +Look for permissions defined in `package.json` that lack corresponding translation keys in `en.json`. Exits non-zero if any are missing. + +Usage: +``` +$ stripes translate pcheck +``` +Output: +``` +Could not find a translation for permission.call-number-browse.view. +Could not find a translation for permission.subjects.view. +``` + +### `translate stats` command + +Compile completion statistics for each locale. + +Usage: +``` +$ stripes translate stats +``` +Output: +``` + ar 83% 720/864 + ber 0% 0/864 + ca 0% 2/864 + cs_CZ 98% 855/864 + da 15% 132/864 + de 95% 827/864 + en_GB 0% 0/864 + en_SE 0% 0/864 + es 59% 511/864 +``` + +## `lint` command + +Run lint on `./lib` and/or `./src` directories. Includes `.eslintrc`. Exits 0 on warnings, 1 on errors. + +Usage: +``` +$ stripes lint +``` +Output: +``` +/Users/zburke/projects/folio-org/stripes-cli/lib/commands/lint.js + 17:24 error Arrow function used ambiguously with a conditional expression. no-confusing-arrow + +✖ 1 problem (1 error, 0 warnings) + 1 error and 0 warnings potentially fixable with the `--fix` option. +``` diff --git a/lib/commands/lint.js b/lib/commands/lint.js index b9a3e97..77d7ddb 100644 --- a/lib/commands/lint.js +++ b/lib/commands/lint.js @@ -14,10 +14,10 @@ function createESLintInstance(overrideConfig) { const lineFormat = (line, column) => chalk.green(`${_.padStart(line, 4)}:${_.padEnd(column, 3)}`); const messageFormat = (message, maxLen) => _.padEnd(message, maxLen); -const severityFormat = (severity) => severity === 1 ? chalk.green(_.padEnd('warning', 10)) : chalk.red(_.padEnd('error', 10)); +const severityFormat = (severity) => (severity === 1 ? chalk.green(_.padEnd('warning', 10)) : chalk.red(_.padEnd('error', 10))); const messagesFormat = (list) => { const maxLen = Math.max(...list.map(m => m.message.length)) + 2; - return list.map(m => `${lineFormat(m.line, m.column)}${severityFormat(m.severity)}${messageFormat(m.message, maxLen)}${m.ruleId}`).join('\n'); + return list.map(m => `${lineFormat(m.line, m.column)}${severityFormat(m.severity)}${messageFormat(m.message, maxLen)}${chalk.green(m.ruleId)}`).join('\n'); }; const resultFormatter = (message) => ( @@ -61,7 +61,7 @@ const lintCommand = async () => { const dingCount = errors + warnings; const theme = errors ? chalk.bold.red : chalk.bold.green; if (errors || warnings) { - console.warn(theme(`✖ ${dingCount} problem${dingCount === 1 ? '' : 's'} (${errors} errors, ${warnings} warnings)`)); + console.warn(theme(`✖ ${dingCount} problem${dingCount === 1 ? '' : 's'} (${errors} error${errors === 1 ? '' : 's'}, ${warnings} warning${warnings === 1 ? '' : 's'})`)); } if (ferrors || fwarnings) { console.warn(theme(` ${ferrors} error${ferrors === 1 ? '' : 's'} and ${fwarnings} warning${fwarnings === 1 ? '' : 's'} potentially fixable with the \`--fix\` option.`));