Skip to content

Commit 6ba8025

Browse files
gwhitneyRebecca Vestidahogurl
authored
feat: Allow clients to access messages from eslint (#827)
Co-authored-by: Rebecca Vest <rvest@equinoxmedia.com> Co-authored-by: Rebecca Vest <olserebe@hotmail.com>
1 parent d4d55fd commit 6ba8025

7 files changed

Lines changed: 116 additions & 8 deletions

File tree

.all-contributorsrc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,15 @@
347347
"avatar_url": "https://avatars.githubusercontent.com/u/999845?v=4",
348348
"profile": "https://jonathan.rehm.me/",
349349
"contributions": ["bug", "code"]
350+
},
351+
{
352+
"login": "gwhitney",
353+
"name": "Glen Whitney",
354+
"avatar_url": "https://avatars.githubusercontent.com/u/3825429?v=4",
355+
"profile": "https://github.com/gwhitney",
356+
"contributions": [
357+
"code"
358+
]
350359
}
351360
],
352361
"repoType": "github",

.changeset/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Changesets
2+
3+
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4+
with multi-package repos, or single-package repos to help you version and publish your code. You can
5+
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6+
7+
We have a quick list of common questions to get you started engaging with this project in
8+
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)

.changeset/six-cycles-double.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'prettier-eslint': minor
3+
---
4+
5+
feat: Allow clients to access messages from ESLint

.eslintrc.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ const config = {
2121
'import/no-import-module-exports': 'off',
2222
'arrow-parens': ['error', 'as-needed'],
2323
quotes: ['error', 'single', { avoidEscape: true }],
24+
},
25+
settings: {
26+
'import/ignore': ['node_modules', 'src'] // Using CommonJS in src
2427
}
2528
};
2629

README.md

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,46 @@ This allows you to use `eslint` to look for bugs and/or bad practices, and use
146146

147147
`prettier-eslint` will **only** propagate _parsing_ errors when either `prettier` or `eslint` fails. In addition to propagating the errors, it will also log a specific message indicating what it was doing at the time of the failure.
148148

149-
**Note:** `prettier-eslint` will not show any message regarding broken rules in either `prettier` or `eslint`.
149+
**Note:** `format` will not show any message regarding broken rules in either `prettier` or `eslint`.
150+
151+
## Capturing ESLint messages
152+
153+
```javascript
154+
const {analyze} = require("prettier-eslint");
155+
156+
const text = 'var x = 0;';
157+
const result = await analyze({
158+
text,
159+
eslintConfig: {
160+
rules: { 'no-var': 'error' }
161+
}
162+
})
163+
console.log(result.messages);
164+
```
165+
166+
produces on the console
167+
168+
```
169+
[{
170+
ruleId: 'no-var',
171+
severity: 2,
172+
message: 'Unexpected var, use let or const instead.',
173+
line: 1,
174+
column: 1,
175+
nodeType: 'VariableDeclaration',
176+
messageId: 'unexpectedVar',
177+
endLine: 1,
178+
endColumn: 11
179+
}]
180+
```
181+
182+
The additional export `analyze` is identical to `format` except that it
183+
returns a simple object with properties `output` giving the exact string
184+
that `format` would return, and `messages` giving the array of message
185+
descriptions (with the structure shown above) produced by the `eslint`
186+
analysis of the code. You may use `analyze` in place of `format` if you
187+
would like to perform the formatting but also capture any errors that
188+
`eslint` may notice.
150189

151190
## Technical details
152191

@@ -308,6 +347,7 @@ Thanks goes to these people ([emoji key][emojis]):
308347
<td align="center" valign="top" width="14.28%"><a href="https://github.com/chrisbobbe"><img src="https://avatars.githubusercontent.com/u/22248748?v=4?s=100" width="100px;" alt="Chris Bobbe"/><br /><sub><b>Chris Bobbe</b></sub></a><br /><a href="https://github.com/prettier/prettier-eslint/issues?q=author%3Achrisbobbe" title="Bug reports">🐛</a> <a href="https://github.com/prettier/prettier-eslint/commits?author=chrisbobbe" title="Code">💻</a></td>
309348
<td align="center" valign="top" width="14.28%"><a href="https://www.1stg.me/"><img src="https://avatars.githubusercontent.com/u/8336744?v=4?s=100" width="100px;" alt="JounQin"/><br /><sub><b>JounQin</b></sub></a><br /><a href="#question-JounQin" title="Answering Questions">💬</a> <a href="https://github.com/prettier/prettier-eslint/commits?author=JounQin" title="Code">💻</a> <a href="#design-JounQin" title="Design">🎨</a> <a href="https://github.com/prettier/prettier-eslint/commits?author=JounQin" title="Documentation">📖</a> <a href="#ideas-JounQin" title="Ideas, Planning, & Feedback">🤔</a> <a href="#infra-JounQin" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#maintenance-JounQin" title="Maintenance">🚧</a> <a href="#plugin-JounQin" title="Plugin/utility libraries">🔌</a> <a href="#projectManagement-JounQin" title="Project Management">📆</a> <a href="https://github.com/prettier/prettier-eslint/pulls?q=is%3Apr+reviewed-by%3AJounQin" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/prettier/prettier-eslint/commits?author=JounQin" title="Tests">⚠️</a> <a href="#tool-JounQin" title="Tools">🔧</a></td>
310349
<td align="center" valign="top" width="14.28%"><a href="https://jonathan.rehm.me/"><img src="https://avatars.githubusercontent.com/u/999845?v=4?s=100" width="100px;" alt="Jonathan Rehm"/><br /><sub><b>Jonathan Rehm</b></sub></a><br /><a href="https://github.com/prettier/prettier-eslint/commits?author=jkrehm" title="Code">💻</a></td>
350+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gwhitney"><img src="https://avatars.githubusercontent.com/u/3825429?v=4?s=100" width="100px;" alt="Glen Whitney"/><br /><sub><b>Glen Whitney</b></sub></a><br /><a href="https://github.com/prettier/prettier-eslint/commits?author=gwhitney" title="Code">💻</a></td>
311351
</tr>
312352
</tbody>
313353
</table>

src/__tests__/index.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import stripIndent from 'strip-indent';
55
import eslintMock from 'eslint';
66
import prettierMock from 'prettier';
77
import loglevelMock from 'loglevel-colored-level-prefix';
8-
import format from '../';
8+
import {format, analyze} from '../';
99

1010
jest.mock('fs');
1111

@@ -256,6 +256,22 @@ tests.forEach(({ title, modifier, input, output }) => {
256256
});
257257
});
258258

259+
test('analyze returns the messages', async () => {
260+
const text = 'var x = 0;';
261+
const result = await analyze({
262+
text,
263+
eslintConfig: {
264+
rules: { 'no-var': 'error' }
265+
}
266+
})
267+
expect(result.output).toBe(`${text}\n`);
268+
expect(result.messages).toHaveLength(1);
269+
const msg = result.messages[0];
270+
expect(msg.ruleId).toBe('no-var');
271+
expect(msg.column).toBe(1);
272+
expect(msg.endColumn).toBe(11);
273+
})
274+
259275
test('failure to fix with eslint throws and logs an error', async () => {
260276
const { lintText } = eslintMock.mock;
261277
const error = new Error('Something happened');

src/index.js

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,25 @@ module.exports = format;
4141
* @return {Promise<String>} - the formatted string
4242
*/
4343
async function format(options) {
44+
const {output} = await analyze(options);
45+
return output;
46+
}
47+
48+
/**
49+
* Analyzes and formats text with prettier and eslint, based on the
50+
* identical options as for the `format` function. It differs from
51+
* `format` only in that the return value is a simple object with
52+
* properties `output` giving the formatted code and `messages` giving
53+
* any error messages generated in the analysis.
54+
* @param {Object} identical to options parameter of `format`
55+
* @returns {Promise<Object>} the return value is an object `r` such that
56+
* `r.output` is the formatted string and `r.messages` is an array of
57+
* message specifications from eslint.
58+
*/
59+
async function analyze(options) {
4460
const { logLevel = getDefaultLogLevel() } = options;
4561
logger.setLevel(logLevel);
46-
logger.trace('called format with options:', prettyFormat(options));
62+
logger.trace('called analyze with options:', prettyFormat(options));
4763

4864
const {
4965
filePath,
@@ -128,11 +144,17 @@ async function format(options) {
128144
const eslintFixed = await eslintFix(text, filePath);
129145
return prettify(eslintFixed);
130146
}
131-
return eslintFix(await prettify(text), filePath);
147+
return eslintFix((await prettify(text)).output, filePath);
132148
}
133149

134150
function createPrettify(formatOptions, prettierPath) {
135-
return async function prettify(text) {
151+
return async function prettify(param) {
152+
let text = param
153+
let messages = []
154+
if (typeof param !== 'string') {
155+
text = param.output
156+
messages = param.text
157+
}
136158
logger.debug('calling prettier on text');
137159
logger.trace(
138160
stripIndent`
@@ -153,7 +175,7 @@ function createPrettify(formatOptions, prettierPath) {
153175
${indentString(output, 2)}
154176
`
155177
);
156-
return output;
178+
return {output, messages};
157179
} catch (error) {
158180
logger.error('prettier formatting failed due to a prettier error');
159181
throw error;
@@ -208,7 +230,7 @@ function createEslintFix(eslintConfig, eslintPath) {
208230
);
209231
// default the output to text because if there's nothing
210232
// to fix, eslint doesn't provide `output`
211-
const [{ output = text }] = await report;
233+
const [{ output = text, messages }] = await report;
212234
logger.trace('eslint --fix: output === input', output === text);
213235
// NOTE: We're ignoring linting errors/warnings here and
214236
// defaulting to the given text if there are any
@@ -221,7 +243,7 @@ function createEslintFix(eslintConfig, eslintPath) {
221243
${indentString(output, 2)}
222244
`
223245
);
224-
return output;
246+
return {output, messages};
225247
} catch (error) {
226248
logger.error('eslint fix failed due to an eslint error');
227249
throw error;
@@ -320,3 +342,8 @@ function getModulePath(filePath = __filename, moduleName) {
320342
function getDefaultLogLevel() {
321343
return process.env.LOG_LEVEL || 'warn';
322344
}
345+
346+
// Allow named imports of either `analyze` or `format` from this module,
347+
// while leaving `format` in place as the default import:
348+
module.exports.format = format
349+
module.exports.analyze = analyze

0 commit comments

Comments
 (0)