Skip to content

Commit

Permalink
feat: Allow clients to access messages from eslint
Browse files Browse the repository at this point in the history
  Adds another export `analyze` from this package, which behaves
  exactly like `format` except that it returns a simple object
  with properties `output` giving the formatted text and `messages`
  giving the eslint messages produced in processing the input text.
  Clients can call `analyze` instead of `format` if they wish, for
  example, to capture any errors that esling may have encountered.

  Resolves prettier#704.
  • Loading branch information
gwhitney committed Dec 17, 2022
1 parent 35f2232 commit 235af13
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 7 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ const config = {
}
],
'import/no-import-module-exports': 'off'
},
settings: {
'import/ignore': ['node_modules', 'src'] // Using CommonJS in src
}
};

Expand Down
18 changes: 17 additions & 1 deletion src/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import stripIndent from 'strip-indent';
import eslintMock from 'eslint';
import prettierMock from 'prettier';
import loglevelMock from 'loglevel-colored-level-prefix';
import format from '../';
import {format, analyze} from '../';

jest.mock('fs');

Expand Down Expand Up @@ -256,6 +256,22 @@ tests.forEach(({ title, modifier, input, output }) => {
});
});

test('analyze returns the messages', async () => {
const text = 'var x = 0;';
const result = await analyze({
text,
eslintConfig: {
rules: { 'no-var': 'error' }
}
})
expect(result.output).toBe(`${text}\n`);
expect(result.messages).toHaveLength(1);
const msg = result.messages[0];
expect(msg.ruleId).toBe('no-var');
expect(msg.column).toBe(1);
expect(msg.endColumn).toBe(11);
})

test('failure to fix with eslint throws and logs an error', async () => {
const { lintText } = eslintMock.mock;
const error = new Error('Something happened');
Expand Down
39 changes: 33 additions & 6 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,25 @@ module.exports = format;
* @return {Promise<String>} - the formatted string
*/
async function format(options) {
const {output} = await analyze(options);
return output;
}

/**
* Analyzes and formats text with prettier and eslint, based on the
* identical options as for the `format` function. It differs from
* `format` only in that the return value is a simple object with
* properties `output` giving the formatted code and `messages` giving
* any error messages generated in the analysis.
* @param {Object} identical to options parameter of `format`
* @returns {Promise<Object>} the return value is an object `r` such that
* `r.output` is the formatted string and `r.messages` is an array of
* message specifications from eslint.
*/
async function analyze(options) {
const { logLevel = getDefaultLogLevel() } = options;
logger.setLevel(logLevel);
logger.trace('called format with options:', prettyFormat(options));
logger.trace('called analyze with options:', prettyFormat(options));

const {
filePath,
Expand Down Expand Up @@ -127,11 +143,17 @@ async function format(options) {
const eslintFixed = await eslintFix(text, filePath);
return prettify(eslintFixed);
}
return eslintFix(prettify(text), filePath);
return eslintFix(prettify(text).output, filePath);
}

function createPrettify(formatOptions, prettierPath) {
return function prettify(text) {
return function prettify(param) {
let text = param
let messages = []
if (typeof param !== 'string') {
text = param.output
messages = param.text
}
logger.debug('calling prettier on text');
logger.trace(
stripIndent`
Expand All @@ -152,7 +174,7 @@ function createPrettify(formatOptions, prettierPath) {
${indentString(output, 2)}
`
);
return output;
return {output, messages};
} catch (error) {
logger.error('prettier formatting failed due to a prettier error');
throw error;
Expand Down Expand Up @@ -207,7 +229,7 @@ function createEslintFix(eslintConfig, eslintPath) {
);
// default the output to text because if there's nothing
// to fix, eslint doesn't provide `output`
const [{ output = text }] = await report;
const [{ output = text, messages }] = await report;
logger.trace('eslint --fix: output === input', output === text);
// NOTE: We're ignoring linting errors/warnings here and
// defaulting to the given text if there are any
Expand All @@ -220,7 +242,7 @@ function createEslintFix(eslintConfig, eslintPath) {
${indentString(output, 2)}
`
);
return output;
return {output, messages};
} catch (error) {
logger.error('eslint fix failed due to an eslint error');
throw error;
Expand Down Expand Up @@ -324,3 +346,8 @@ function getModulePath(filePath = __filename, moduleName) {
function getDefaultLogLevel() {
return process.env.LOG_LEVEL || 'warn';
}

// Allow named imports of either `analyze` or `format` from this module,
// while leaving `format` in place as the default import:
module.exports.format = format
module.exports.analyze = analyze

0 comments on commit 235af13

Please sign in to comment.