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/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 new file mode 100644 index 0000000..77d7ddb --- /dev/null +++ b/lib/commands/lint.js @@ -0,0 +1,87 @@ +const importLazy = require('import-lazy')(require); + +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) => 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) => { + 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)}${chalk.green(m.ruleId)}`).join('\n'); +}; + +const resultFormatter = (message) => ( + `${chalk.underline(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, + ); + + 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(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.`)); + } + + 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" }