Skip to content

Commit 1666067

Browse files
ikatyangnot-an-aardvark
authored andcommitted
New: expose reporter api (fixes #39) (#41)
1 parent 6c351c3 commit 1666067

File tree

4 files changed

+150
-71
lines changed

4 files changed

+150
-71
lines changed

.eslintrc.js

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module.exports = {
1212
'plugin:eslint-plugin/recommended',
1313
'prettier'
1414
],
15+
env: { mocha: true },
1516
root: true,
1617
rules: {
1718
'prettier/prettier': ['error', { singleQuote: true }],

eslint-plugin-prettier.js

+118-69
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ const FB_PRETTIER_OPTIONS = {
2727

2828
const LINE_ENDING_RE = /\r\n|[\r\n\u2028\u2029]/;
2929

30+
const OPERATION_INSERT = 'insert';
31+
const OPERATION_DELETE = 'delete';
32+
const OPERATION_REPLACE = 'replace';
33+
3034
// ------------------------------------------------------------------------------
3135
// Privates
3236
// ------------------------------------------------------------------------------
@@ -104,18 +108,13 @@ function showInvisibles(str) {
104108
return ret;
105109
}
106110

107-
// ------------------------------------------------------------------------------
108-
// Rule Definition
109-
// ------------------------------------------------------------------------------
110-
111111
/**
112-
* Reports issues where the context's source code differs from the Prettier
113-
formatted version.
114-
* @param {RuleContext} context - The ESLint rule context.
112+
* Generate results for differences between source code and formatted version.
113+
* @param {string} source - The original source.
115114
* @param {string} prettierSource - The Prettier formatted source.
116-
* @returns {void}
115+
* @returns {Array} - An array contains { operation, offset, insertText, deleteText }
117116
*/
118-
function reportDifferences(context, prettierSource) {
117+
function generateDifferences(source, prettierSource) {
119118
// fast-diff returns the differences between two texts as a series of
120119
// INSERT, DELETE or EQUAL operations. The results occur only in these
121120
// sequences:
@@ -130,8 +129,8 @@ function reportDifferences(context, prettierSource) {
130129
// and another's beginning does not have line endings (i.e. issues that occur
131130
// on contiguous lines).
132131

133-
const source = context.getSourceCode().text;
134132
const results = diff(source, prettierSource);
133+
const differences = [];
135134

136135
const batch = [];
137136
let offset = 0; // NOTE: INSERT never advances the offset.
@@ -166,6 +165,8 @@ function reportDifferences(context, prettierSource) {
166165
}
167166
}
168167

168+
return differences;
169+
169170
function flush() {
170171
let aheadDeleteText = '';
171172
let aheadInsertText = '';
@@ -187,16 +188,33 @@ function reportDifferences(context, prettierSource) {
187188
}
188189
}
189190
if (aheadDeleteText && aheadInsertText) {
190-
reportReplace(context, offset, aheadDeleteText, aheadInsertText);
191+
differences.push({
192+
offset,
193+
operation: OPERATION_REPLACE,
194+
insertText: aheadInsertText,
195+
deleteText: aheadDeleteText
196+
});
191197
} else if (!aheadDeleteText && aheadInsertText) {
192-
reportInsert(context, offset, aheadInsertText);
198+
differences.push({
199+
offset,
200+
operation: OPERATION_INSERT,
201+
insertText: aheadInsertText
202+
});
193203
} else if (aheadDeleteText && !aheadInsertText) {
194-
reportDelete(context, offset, aheadDeleteText);
204+
differences.push({
205+
offset,
206+
operation: OPERATION_DELETE,
207+
deleteText: aheadDeleteText
208+
});
195209
}
196210
offset += aheadDeleteText.length;
197211
}
198212
}
199213

214+
// ------------------------------------------------------------------------------
215+
// Rule Definition
216+
// ------------------------------------------------------------------------------
217+
200218
/**
201219
* Reports an "Insert ..." issue where text must be inserted.
202220
* @param {RuleContext} context - The ESLint rule context.
@@ -268,68 +286,99 @@ function reportReplace(context, offset, deleteText, insertText) {
268286
// Module Definition
269287
// ------------------------------------------------------------------------------
270288

271-
module.exports.rules = {
272-
prettier: {
273-
meta: {
274-
fixable: 'code',
275-
schema: [
276-
// Prettier options:
277-
{
278-
anyOf: [
279-
{ enum: [null, 'fb'] },
280-
{ type: 'object', properties: {}, additionalProperties: true }
281-
]
282-
},
283-
// Pragma:
284-
{ type: 'string', pattern: '^@\\w+$' }
285-
]
286-
},
287-
create(context) {
288-
const prettierOptions = context.options[0] === 'fb'
289-
? FB_PRETTIER_OPTIONS
290-
: context.options[0];
291-
292-
const pragma = context.options[1]
293-
? context.options[1].slice(1) // Remove leading @
294-
: null;
289+
module.exports = {
290+
showInvisibles,
291+
generateDifferences,
292+
rules: {
293+
prettier: {
294+
meta: {
295+
fixable: 'code',
296+
schema: [
297+
// Prettier options:
298+
{
299+
anyOf: [
300+
{ enum: [null, 'fb'] },
301+
{ type: 'object', properties: {}, additionalProperties: true }
302+
]
303+
},
304+
// Pragma:
305+
{ type: 'string', pattern: '^@\\w+$' }
306+
]
307+
},
308+
create(context) {
309+
const prettierOptions = context.options[0] === 'fb'
310+
? FB_PRETTIER_OPTIONS
311+
: context.options[0];
295312

296-
const sourceCode = context.getSourceCode();
297-
const source = sourceCode.text;
313+
const pragma = context.options[1]
314+
? context.options[1].slice(1) // Remove leading @
315+
: null;
298316

299-
// The pragma is only valid if it is found in a block comment at the very
300-
// start of the file.
301-
if (pragma) {
302-
// ESLint 3.x reports the shebang as a "Line" node, while ESLint 4.x
303-
// reports it as a "Shebang" node. This works for both versions:
304-
const hasShebang = source.startsWith('#!');
305-
const allComments = sourceCode.getAllComments();
306-
const firstComment = hasShebang ? allComments[1] : allComments[0];
307-
if (
308-
!(firstComment &&
309-
firstComment.type === 'Block' &&
310-
firstComment.loc.start.line === (hasShebang ? 2 : 1) &&
311-
firstComment.loc.start.column === 0)
312-
) {
313-
return {};
314-
}
315-
const parsed = docblock.parse(firstComment.value);
316-
if (parsed[pragma] !== '') {
317-
return {};
318-
}
319-
}
317+
const sourceCode = context.getSourceCode();
318+
const source = sourceCode.text;
320319

321-
return {
322-
Program() {
323-
if (!prettier) {
324-
// Prettier is expensive to load, so only load it if needed.
325-
prettier = require('prettier');
320+
// The pragma is only valid if it is found in a block comment at the very
321+
// start of the file.
322+
if (pragma) {
323+
// ESLint 3.x reports the shebang as a "Line" node, while ESLint 4.x
324+
// reports it as a "Shebang" node. This works for both versions:
325+
const hasShebang = source.startsWith('#!');
326+
const allComments = sourceCode.getAllComments();
327+
const firstComment = hasShebang ? allComments[1] : allComments[0];
328+
if (
329+
!(firstComment &&
330+
firstComment.type === 'Block' &&
331+
firstComment.loc.start.line === (hasShebang ? 2 : 1) &&
332+
firstComment.loc.start.column === 0)
333+
) {
334+
return {};
326335
}
327-
const prettierSource = prettier.format(source, prettierOptions);
328-
if (source !== prettierSource) {
329-
reportDifferences(context, prettierSource);
336+
const parsed = docblock.parse(firstComment.value);
337+
if (parsed[pragma] !== '') {
338+
return {};
330339
}
331340
}
332-
};
341+
342+
return {
343+
Program() {
344+
if (!prettier) {
345+
// Prettier is expensive to load, so only load it if needed.
346+
prettier = require('prettier');
347+
}
348+
const prettierSource = prettier.format(source, prettierOptions);
349+
if (source !== prettierSource) {
350+
const differences = generateDifferences(source, prettierSource);
351+
352+
differences.forEach(difference => {
353+
switch (difference.operation) {
354+
case OPERATION_INSERT:
355+
reportInsert(
356+
context,
357+
difference.offset,
358+
difference.insertText
359+
);
360+
break;
361+
case OPERATION_DELETE:
362+
reportDelete(
363+
context,
364+
difference.offset,
365+
difference.deleteText
366+
);
367+
break;
368+
case OPERATION_REPLACE:
369+
reportReplace(
370+
context,
371+
difference.offset,
372+
difference.deleteText,
373+
difference.insertText
374+
);
375+
break;
376+
}
377+
});
378+
}
379+
}
380+
};
381+
}
333382
}
334383
}
335384
};

package.json

-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
"jest-docblock": "^20.0.1"
3131
},
3232
"peerDependencies": {
33-
"eslint": ">=3.14.1",
3433
"prettier": ">= 0.11.0"
3534
},
3635
"devDependencies": {

test/prettier.js

+31-1
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@
1414

1515
const fs = require('fs');
1616
const path = require('path');
17+
const assert = require('assert');
1718

18-
const rule = require('..').rules.prettier;
19+
const eslintPluginPrettier = require('..');
20+
21+
const rule = eslintPluginPrettier.rules.prettier;
1922
const RuleTester = require('eslint').RuleTester;
2023

2124
// ------------------------------------------------------------------------------
@@ -64,6 +67,33 @@ ruleTester.run('prettier', rule, {
6467
].map(loadInvalidFixture)
6568
});
6669

70+
describe('generateDifferences', () => {
71+
it('operation: insert', () => {
72+
const differences = eslintPluginPrettier.generateDifferences(
73+
'abc',
74+
'abcdef'
75+
);
76+
assert.deepEqual(differences, [
77+
{ operation: 'insert', offset: 3, insertText: 'def' }
78+
]);
79+
});
80+
it('operation: delete', () => {
81+
const differences = eslintPluginPrettier.generateDifferences(
82+
'abcdef',
83+
'abc'
84+
);
85+
assert.deepEqual(differences, [
86+
{ operation: 'delete', offset: 3, deleteText: 'def' }
87+
]);
88+
});
89+
it('operation: replace', () => {
90+
const differences = eslintPluginPrettier.generateDifferences('abc', 'def');
91+
assert.deepEqual(differences, [
92+
{ operation: 'replace', offset: 0, deleteText: 'abc', insertText: 'def' }
93+
]);
94+
});
95+
});
96+
6797
// ------------------------------------------------------------------------------
6898
// Helpers
6999
// ------------------------------------------------------------------------------

0 commit comments

Comments
 (0)