Skip to content

Commit

Permalink
feat(path): add eslintPath and prettierPath options (#6)
Browse files Browse the repository at this point in the history
So you can specify whether to use your project's version of `eslint` or
`prettier` (if they don't match up for some reason). Or even :shudder:
use a global version :shudder:.

This will be especially useful for the CLI and the atom plugin I think.

Closes #3
  • Loading branch information
Kent C. Dodds authored Jan 18, 2017
1 parent 9048d17 commit 6fd8f23
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 11 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,17 @@ When there's an error, `prettier-eslint` will log it to the console. To disable
`disableLog` as an option to the call to `format` or you can set: `format.options.disableLog = true` to disable it
"globally."

#### eslintPath (?String)

By default, `prettier-eslint` will try to find your project's version of `eslint` (and `prettier`). If it cannot find
one, then it will use the version that `prettier-eslint` has installed locally. If you'd like to specify a path to the
`eslint` module you would like to have `prettier-eslint` use, then you can provide the full path to it with the
`eslintPath` option.

#### prettierPath (?String)

This is basically the same as `eslintPath` except for the `prettier` module.

### throws

`prettier-eslint` will propagate errors when either `prettier` or `eslint` fails for one reason or another. In addition
Expand Down
43 changes: 32 additions & 11 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
/* eslint no-console:0 */
/* eslint no-console:0, global-require:0, import/no-dynamic-require:0 */
import path from 'path'
import {CLIEngine} from 'eslint'
import prettier from 'prettier'
import {getPrettierOptionsFromESLintRules} from './utils'

const options = {disableLog: false}
Expand All @@ -15,6 +13,10 @@ module.exports.options = options
* @param {String} options.filePath - the path of the file being formatted
* can be used in leu of `eslintConfig` (eslint will be used to find the
* relevant config for the file)
* @param {String} options.eslintPath - the path to the eslint module to use.
* Will default to require.resolve('eslint')
* @param {String} options.prettierPath - the path to the prettier module to use.
* Will default to require.resovlve('prettierPath')
* @param {Boolean} options.disableLog - disables any logging
* @param {String} options.eslintConfig - the config to use for formatting
* with ESLint.
Expand All @@ -26,23 +28,32 @@ module.exports.options = options
function format({
text,
filePath,
eslintPath = require.resolve('eslint'),
prettierPath = require.resolve('prettier'),
disableLog = options.disableLog,
eslintConfig = getConfig(filePath),
eslintConfig = getConfig(filePath, eslintPath),
prettierOptions = getPrettierOptionsFromESLintRules(eslintConfig),
}) {
const originalLogValue = options.disableLog
options.disableLog = disableLog

try {
const pretty = prettify(text, prettierOptions)
const eslintFixed = eslintFix(pretty, eslintConfig)
const pretty = prettify(text, prettierOptions, prettierPath)
const eslintFixed = eslintFix(pretty, eslintConfig, eslintPath)
return eslintFixed
} finally {
options.disableLog = originalLogValue
}
}

function prettify(text, formatOptions) {
function prettify(text, formatOptions, prettierPath) {
let prettier
try {
prettier = require(prettierPath)
} catch (error) {
logError(`There was trouble getting prettier. Is "prettierPath: ${prettierPath}" a correct path to the prettier module?`)
throw error
}
try {
return prettier.format(text, formatOptions)
} catch (error) {
Expand All @@ -52,7 +63,7 @@ function prettify(text, formatOptions) {
}
}

function eslintFix(text, eslintConfig) {
function eslintFix(text, eslintConfig, eslintPath) {
const eslintOptions = {
// overrideables
useEslintrc: false,
Expand All @@ -67,7 +78,7 @@ function eslintFix(text, eslintConfig) {
// for a --fix though so :shrug:
globals: [],
}
const eslint = new CLIEngine(eslintOptions)
const eslint = getESLintCLIEngine(eslintPath, eslintOptions)
try {
const report = eslint.executeOnText(text)
const [{output}] = report.results
Expand All @@ -83,12 +94,12 @@ function eslintFix(text, eslintConfig) {
}
}

function getConfig(filePath) {
function getConfig(filePath, eslintPath) {
const eslintOptions = {}
if (filePath) {
eslintOptions.cwd = path.dirname(filePath)
}
const configFinder = new CLIEngine(eslintOptions)
const configFinder = getESLintCLIEngine(eslintPath, eslintOptions)
try {
const config = configFinder.getConfigForFile(filePath)
return config
Expand All @@ -99,6 +110,16 @@ function getConfig(filePath) {
}
}

function getESLintCLIEngine(eslintPath, eslintOptions) {
try {
const {CLIEngine} = require(eslintPath)
return new CLIEngine(eslintOptions)
} catch (error) {
logError(`There was trouble creating the ESLint CLIEngine. Is "eslintPath: ${eslintPath}" a correct path to the ESLint module?`)
throw error
}
}

function logError(...args) {
if (!options.disableLog) {
console.error('prettier-eslint error:', ...args)
Expand Down
38 changes: 38 additions & 0 deletions src/index.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* eslint no-console:0 */
import path from 'path'
import stripIndent from 'strip-indent'
import eslintMock from 'eslint'
import prettierMock from 'prettier'
Expand Down Expand Up @@ -54,6 +55,9 @@ const tests = [

beforeEach(() => {
console.error.mockClear()
eslintMock.mock.executeOnText.mockClear()
eslintMock.mock.getConfigForFile.mockClear()
prettierMock.format.mockClear()
})

tests.forEach(({title, modifier, input, output}) => {
Expand Down Expand Up @@ -126,6 +130,40 @@ test('can disable log on a single call as part of the options', () => {
prettierMockFormat.throwError = null
})

test('can accept a path to an eslint module and uses that instead.', () => {
const eslintPath = path.join(__dirname, './__mocks__/eslint')
const {executeOnText} = eslintMock.mock
format({text: '', eslintPath})
expect(executeOnText).toHaveBeenCalledTimes(1)
})

test('fails with an error if the eslint module cannot be resolved.', () => {
const eslintPath = path.join(__dirname, './__mocks__/non-existant-eslint-module')
expect(() => format({text: '', eslintPath})).toThrowError(/non-existant-eslint-module/)
expect(console.error).toHaveBeenCalledTimes(1)
expect(console.error).toHaveBeenCalledWith(
'prettier-eslint error:',
expect.stringMatching(/ESLint.*?eslintPath.*non-existant-eslint-module/),
)
})

test('can accept a path to a prettier module and uses that instead.', () => {
const prettierPath = path.join(__dirname, './__mocks__/prettier')
const {format: prettierMockFormat} = prettierMock
format({text: '', prettierPath})
expect(prettierMockFormat).toHaveBeenCalledTimes(1)
})

test('fails with an error if the prettier module cannot be resolved.', () => {
const prettierPath = path.join(__dirname, './__mocks__/non-existant-prettier-module')
expect(() => format({text: '', prettierPath})).toThrowError(/non-existant-prettier-module/)
expect(console.error).toHaveBeenCalledTimes(1)
expect(console.error).toHaveBeenCalledWith(
'prettier-eslint error:',
expect.stringMatching(/prettier.*?prettierPath.*non-existant-prettier-module/),
)
})

function getESLintConfigWithDefaultRules(overrides) {
return {
parserOptions: {
Expand Down

0 comments on commit 6fd8f23

Please sign in to comment.