Skip to content

Commit

Permalink
feat: add prettier support
Browse files Browse the repository at this point in the history
  • Loading branch information
theoludwig committed Sep 15, 2021
1 parent b92f2da commit 74a2227
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 16 deletions.
34 changes: 20 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

## Overview

Wrap your own eslint rules in a easy-to-use command line tool and/or a JS module.
Wrap your own eslint and prettier rules in a easy-to-use command line tool and/or a JS module.

## Install

Expand Down Expand Up @@ -61,6 +61,7 @@ require('standard-engine').cli(opts)

```js
const eslint = require('eslint')
const prettier = require('prettier')
const path = require('path')
const pkg = require('./package.json')

Expand All @@ -70,20 +71,24 @@ module.exports = {
homepage: pkg.homepage,
bugs: pkg.bugs.url,
eslint: eslint, // pass any version of eslint >= 1.0.0
prettier: prettier, // pass any version of prettier >= 2.0.0
cmd: 'pocketlint', // should match the "bin" key in your package.json
tagline: 'Live by your own standards!', // displayed in output --help
eslintConfig: {
configFile: path.join(__dirname, 'eslintrc.json')
configFile: path.join(__dirname, '.eslintrc.json')
},
prettierConfig: {
configFile: path.join(__dirname, '.prettierrc.json')
},
cwd: '' // current working directory, passed to eslint
}
```

Additionally an optional `resolveEslintConfig()` function can be provided. See below for details.

### `eslintrc.json`
### `.eslintrc.json`

Put all your .eslintrc rules in this file. A good practice is to create an [ESLint Shareable Config](http://eslint.org/docs/developer-guide/shareable-configs) and extend it, but its not required:
Put all your .eslintrc rules in this file. A good practice is to create an [ESLint Shareable Config](http://eslint.org/docs/developer-guide/shareable-configs) and extend it, but its not required:

```js
{
Expand Down Expand Up @@ -165,12 +170,7 @@ a `ignore` property to `package.json`:
Some files are ignored by default:

```js
const DEFAULT_IGNORE = [
'*.min.js',
'coverage/',
'node_modules/',
'vendor/'
]
const DEFAULT_IGNORE = ['*.min.js', 'coverage/', 'node_modules/', 'vendor/']
```

You can disable these default ignores by setting the `noDefaultIgnore` option to `true`.
Expand Down Expand Up @@ -272,6 +272,7 @@ You can provide a `resolveEslintConfig()` function in the `options.js` exports:

```js
const eslint = require('eslint')
const prettier = require('prettier')
const path = require('path')
const pkg = require('./package.json')

Expand All @@ -281,10 +282,14 @@ module.exports = {
homepage: pkg.homepage,
bugs: pkg.bugs.url,
eslint: eslint, // pass any version of eslint >= 1.0.0
prettier: prettier, // pass any version of prettier >= 2.0.0
cmd: 'pocketlint', // should match the "bin" key in your package.json
tagline: 'Live by your own standards!', // displayed in output --help
eslintConfig: {
configFile: path.join(__dirname, 'eslintrc.json')
configFile: path.join(__dirname, '.eslintrc.json')
},
prettierConfig: {
configFile: path.join(__dirname, '.prettierrc.json')
},
resolveEslintConfig: function (eslintConfig, opts, packageOpts, rootDir) {
// provide implementation here, then return the eslintConfig object (or a new one)
Expand Down Expand Up @@ -312,6 +317,7 @@ be provided:
// common to lintText and lintFiles
cwd: '', // current working directory (default: process.cwd())
fix: false, // automatically fix problems
format: false, // aggressively format code for consistency
extensions: [], // file extensions to lint (has sane defaults)
globals: [], // custom global variables to declare
plugins: [], // custom eslint plugins
Expand All @@ -335,9 +341,7 @@ const results = {
results: [
{
filePath: '',
messages: [
{ ruleId: '', message: '', line: 0, column: 0 }
],
messages: [{ ruleId: '', message: '', line: 0, column: 0 }],
errorCount: 0,
warningCount: 0,
output: '' // fixed source code (only present with {fix: true} option)
Expand Down Expand Up @@ -365,6 +369,7 @@ Lint the provided `files` globs. An `opts` object may be provided:
// common to lintText and lintFiles
cwd: '', // current working directory (default: process.cwd())
fix: false, // automatically fix problems
format: false, // aggressively format code for consistency
extensions: [], // file extensions to lint (has sane defaults)
globals: [], // custom global variables to declare
plugins: [], // custom eslint plugins
Expand Down Expand Up @@ -394,6 +399,7 @@ This is the full set of options accepted by the above APIs. Not all options make
filename: '', // path of the file containing the text being linted (optional)
fix: false, // automatically fix problems
globals: [], // custom global variables to declare
format: false, // aggressively format code for consistency
plugins: [], // custom eslint plugins
envs: [], // custom eslint environment
parser: '' // custom js parser (e.g. babel-eslint)
Expand Down
3 changes: 3 additions & 0 deletions bin/cmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ function cli (rawOpts) {
},
boolean: [
'fix',
'format',
'help',
'stdin',
'version'
Expand Down Expand Up @@ -69,6 +70,7 @@ Usage:
Flags:
--fix Automatically fix problems
--format Aggressively format code for consistency
--version Show current version
-h, --help Show usage information
Expand All @@ -92,6 +94,7 @@ Flags (advanced):

const lintOpts = {
fix: argv.fix,
format: argv.format,
extensions: argv.ext,
globals: argv.global,
plugins: argv.plugin,
Expand Down
56 changes: 54 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/*! standard-engine. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

const os = require('os')
const fs = require('fs')
const path = require('path')
const globby = require('globby')

const CACHE_HOME = require('xdg-basedir').cache || os.tmpdir()

Expand All @@ -11,12 +13,19 @@ const { resolveEslintConfig } = require('./lib/resolve-eslint-config')
/** @typedef {Omit<import('./lib/resolve-eslint-config').ResolveOptions, 'cmd'|'cwd'>} BaseLintOptions */
/** @typedef {(err: Error|unknown, result?: import('eslint').CLIEngine.LintReport) => void} LinterCallback */

/**
* @typedef PrettierOptions
* @property {string | null} [configFile]
*/

/**
* @typedef LinterOptions
* @property {string} cmd
* @property {import('eslint')} eslint
* @property {import('prettier')} prettier
* @property {string} [cwd]
* @property {CLIEngineOptions} [eslintConfig]
* @property {PrettierOptions} [prettierConfig]
* @property {import('./lib/resolve-eslint-config').CustomEslintConfigResolver} [resolveEslintConfig]
* @property {string} [version]
*/
Expand All @@ -28,11 +37,14 @@ class Linter {
constructor (opts) {
if (!opts || !opts.cmd) throw new Error('opts.cmd option is required')
if (!opts.eslint) throw new Error('opts.eslint option is required')
if (!opts.prettier) throw new Error('opts.prettier option is required')

/** @type {string} */
this.cmd = opts.cmd
/** @type {import('eslint')} */
this.eslint = opts.eslint
/** @type {import('prettier')} */
this.prettier = opts.prettier
/** @type {string} */
this.cwd = opts.cwd || process.cwd()
this.customEslintConfigResolver = opts.resolveEslintConfig
Expand All @@ -57,6 +69,11 @@ class Linter {
...(opts.eslintConfig || {})
}

/** @type {PrettierOptions} */
this.prettierConfig = {
configFile: opts.prettierConfig != null ? opts.prettierConfig.configFile : null
}

if (this.eslintConfig.configFile != null) {
this.eslintConfig.resolvePluginsRelativeTo = path.dirname(
this.eslintConfig.configFile
Expand Down Expand Up @@ -102,27 +119,62 @@ class Linter {
* Lint files to enforce JavaScript Style.
*
* @param {Array.<string>} files file globs to lint
* @param {BaseLintOptions & { cwd?: string }} [opts] base options + file globs to ignore (has sane defaults) + current working directory (default: process.cwd())
* @param {BaseLintOptions & { cwd?: string, format?: boolean }} [opts] base options + file globs to ignore (has sane defaults) + current working directory (default: process.cwd())
* @param {LinterCallback} [cb]
* @returns {void}
*/
lintFiles (files, opts, cb) {
if (typeof opts === 'function') { return this.lintFiles(files, undefined, opts) }
if (!cb) throw new Error('callback is required')
const format = opts == null ? false : opts.format

const eslintConfig = this.resolveEslintConfig(opts)

if (typeof files === 'string') files = [files]
if (files.length === 0) files = ['.']

if (format) {
/** @type {string[]} */
const extensionsArray = []
this.prettier.getSupportInfo().languages.forEach((language) => {
if (language != null && language.extensions != null) {
extensionsArray.push(...language.extensions)
}
})
const extensions = extensionsArray.join(',')
const prettierOptions = this.prettier.resolveConfig.sync(
this.prettierConfig.configFile == null ? path.join(__dirname, '.prettierrc.json') : this.prettierConfig.configFile
) || {}
const ignore = typeof eslintConfig.ignorePattern === 'string'
? [eslintConfig.ignorePattern]
: eslintConfig.ignorePattern == null
? []
: eslintConfig.ignorePattern
const files = globby.sync([`**/*{${extensions}}`], {
ignore,
cwd: opts == null ? undefined : opts.cwd
})
for (const file of files) {
const filePath = path.join(process.cwd(), file)
const fileInfo = this.prettier.getFileInfo.sync(filePath, {})
prettierOptions.parser = fileInfo.inferredParser == null ? 'babel' : fileInfo.inferredParser
const input = fs.readFileSync(filePath, 'utf8')
const output = this.prettier.format(input, prettierOptions)
const formatted = input !== output
if (formatted) {
fs.writeFileSync(filePath, output, { encoding: 'utf8' })
}
}
}

let result
try {
result = new this.eslint.CLIEngine(eslintConfig).executeOnFiles(files)
} catch (err) {
return cb(err)
}

if (eslintConfig.fix) {
if (eslintConfig.fix || format) {
this.eslint.CLIEngine.outputFixes(result)
}

Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
},
"dependencies": {
"get-stdin": "^8.0.0",
"globby": "^11.0.4",
"minimist": "^1.2.5",
"pkg-conf": "^3.1.0",
"xdg-basedir": "^4.0.0"
Expand All @@ -48,6 +49,7 @@
"@types/eslint": "^7.28.0",
"@types/minimist": "^1.2.2",
"@types/node": "~12.20.0",
"@types/prettier": "^2.3.2",
"cross-spawn": "^7.0.3",
"eslint": "^7.12.1",
"eslint-config-standard": "^16.0.0",
Expand All @@ -56,6 +58,7 @@
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.0",
"eslint-plugin-react": "^7.21.5",
"prettier": "^2.4.0",
"standard": "*",
"tape": "^5.0.1",
"typescript": "~4.4.3"
Expand Down
2 changes: 2 additions & 0 deletions test/api.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const eslint = require('eslint')
const prettier = require('prettier')
const path = require('path')
const test = require('tape')

Expand All @@ -9,6 +10,7 @@ function getStandard () {
cmd: 'pocketlint',
version: '0.0.0',
eslint,
prettier,
eslintConfig: require('../tmp/standard/options').eslintConfig
})
}
Expand Down
2 changes: 2 additions & 0 deletions test/lib/standard-cmd.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#!/usr/bin/env node
const path = require('path')
const eslint = require('eslint')
const prettier = require('prettier')

const opts = {
cmd: 'pocketlint',
version: '0.0.0',
eslint,
prettier,
eslintConfig: {
configFile: path.join(__dirname, 'standard.json'),
useEslintrc: false
Expand Down

1 comment on commit 74a2227

@bnjmnrsh
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

Please sign in to comment.