eslint-plugin-prettier 3.3.1 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## v3.4.0 (2021-04-15)
4
+
5
+ * feat: support processor virtual filename ([#401](git@github.com:prettier/eslint-plugin-prettier/issues/401)) ([ee0ccc6](git@github.com:prettier/eslint-plugin-prettier/commit/ee0ccc6ac06d13cd546e78b444e53164f59eb27f))
6
+ * Simplify report logic ([#380](git@github.com:prettier/eslint-plugin-prettier/issues/380)) ([d993f24](git@github.com:prettier/eslint-plugin-prettier/commit/d993f247b5661683af031ab3b93955a0dfe448fa))
7
+ * Update: README.md ([#375](git@github.com:prettier/eslint-plugin-prettier/issues/375)) ([3ea4242](git@github.com:prettier/eslint-plugin-prettier/commit/3ea4242a8d4acdb76eb7e7dca9e44d3e87db70e3))
8
+
3
9
  ## v3.3.1 (2021-01-04)
4
10
 
5
11
  * fix: add eslint-config-prettier as an optional peer dependency ([#374](git@github.com:prettier/eslint-plugin-prettier/issues/374)) ([d59df27](git@github.com:prettier/eslint-plugin-prettier/commit/d59df27890aaffec9e528ceb3155831a0261848d))
package/README.md CHANGED
@@ -4,6 +4,8 @@ Runs [Prettier](https://github.com/prettier/prettier) as an [ESLint](http://esli
4
4
 
5
5
  If your desired formatting does not match Prettier’s output, you should use a different tool such as [prettier-eslint](https://github.com/prettier/prettier-eslint) instead.
6
6
 
7
+ Please read [Integrating with linters](https://prettier.io/docs/en/integrating-with-linters.html) before installing.
8
+
7
9
  ## Sample
8
10
 
9
11
  ```js
@@ -74,20 +76,6 @@ This plugin ships with a `plugin:prettier/recommended` config that sets up both
74
76
 
75
77
  You can then set Prettier's own options inside a `.prettierrc` file.
76
78
 
77
- 3. Some ESLint plugins (such as [eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react)) also contain rules that conflict with Prettier. Add extra exclusions for the plugins you use like so:
78
-
79
- ```json
80
- {
81
- "extends": [
82
- "plugin:prettier/recommended",
83
- "prettier/flowtype",
84
- "prettier/react"
85
- ]
86
- }
87
- ```
88
-
89
- For the list of every available exclusion rule set, please see the [readme of eslint-config-prettier](https://github.com/prettier/eslint-config-prettier/blob/master/README.md).
90
-
91
79
  Exactly what does `plugin:prettier/recommended` do? Well, this is what it expands to:
92
80
 
93
81
  ```json
@@ -102,7 +90,7 @@ Exactly what does `plugin:prettier/recommended` do? Well, this is what it expand
102
90
  }
103
91
  ```
104
92
 
105
- - `"extends": ["prettier"]` enables the main config from `eslint-config-prettier`, which turns off some ESLint core rules that conflict with Prettier.
93
+ - `"extends": ["prettier"]` enables the config from `eslint-config-prettier`, which turns off some ESLint rules that conflict with Prettier.
106
94
  - `"plugins": ["prettier"]` registers this plugin.
107
95
  - `"prettier/prettier": "error"` turns on the rule provided by this plugin, which runs Prettier from within ESLint.
108
96
  - `"arrow-body-style": "off"` and `"prefer-arrow-callback": "off"` turns off two ESLint core rules that unfortunately are problematic with this plugin – see the next section.
@@ -9,6 +9,9 @@
9
9
  // Requirements
10
10
  // ------------------------------------------------------------------------------
11
11
 
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+
12
15
  const {
13
16
  showInvisibles,
14
17
  generateDifferences
@@ -25,6 +28,9 @@ const { INSERT, DELETE, REPLACE } = generateDifferences;
25
28
  // ------------------------------------------------------------------------------
26
29
 
27
30
  // Lazily-loaded Prettier.
31
+ /**
32
+ * @type {import('prettier')}
33
+ */
28
34
  let prettier;
29
35
 
30
36
  // ------------------------------------------------------------------------------
@@ -32,72 +38,49 @@ let prettier;
32
38
  // ------------------------------------------------------------------------------
33
39
 
34
40
  /**
35
- * Reports an "Insert ..." issue where text must be inserted.
36
- * @param {RuleContext} context - The ESLint rule context.
37
- * @param {number} offset - The source offset where to insert text.
38
- * @param {string} text - The text to be inserted.
41
+ * Reports a difference.
42
+ * @param {import('eslint').Rule.RuleContext} context - The ESLint rule context.
43
+ * @param {import('prettier-linter-helpers').Difference} difference - The difference object.
39
44
  * @returns {void}
40
45
  */
41
- function reportInsert(context, offset, text) {
42
- const pos = context.getSourceCode().getLocFromIndex(offset);
43
- const range = [offset, offset];
44
- context.report({
45
- message: 'Insert `{{ code }}`',
46
- data: { code: showInvisibles(text) },
47
- loc: { start: pos, end: pos },
48
- fix(fixer) {
49
- return fixer.insertTextAfterRange(range, text);
50
- }
51
- });
52
- }
46
+ function reportDifference(context, difference) {
47
+ const { operation, offset, deleteText = '', insertText = '' } = difference;
48
+ const range = [offset, offset + deleteText.length];
49
+ const [start, end] = range.map(index =>
50
+ context.getSourceCode().getLocFromIndex(index)
51
+ );
53
52
 
54
- /**
55
- * Reports a "Delete ..." issue where text must be deleted.
56
- * @param {RuleContext} context - The ESLint rule context.
57
- * @param {number} offset - The source offset where to delete text.
58
- * @param {string} text - The text to be deleted.
59
- * @returns {void}
60
- */
61
- function reportDelete(context, offset, text) {
62
- const start = context.getSourceCode().getLocFromIndex(offset);
63
- const end = context.getSourceCode().getLocFromIndex(offset + text.length);
64
- const range = [offset, offset + text.length];
65
53
  context.report({
66
- message: 'Delete `{{ code }}`',
67
- data: { code: showInvisibles(text) },
54
+ messageId: operation,
55
+ data: {
56
+ deleteText: showInvisibles(deleteText),
57
+ insertText: showInvisibles(insertText)
58
+ },
68
59
  loc: { start, end },
69
- fix(fixer) {
70
- return fixer.removeRange(range);
71
- }
60
+ fix: fixer => fixer.replaceTextRange(range, insertText)
72
61
  });
73
62
  }
74
63
 
75
64
  /**
76
- * Reports a "Replace ... with ..." issue where text must be replaced.
77
- * @param {RuleContext} context - The ESLint rule context.
78
- * @param {number} offset - The source offset where to replace deleted text
79
- with inserted text.
80
- * @param {string} deleteText - The text to be deleted.
81
- * @param {string} insertText - The text to be inserted.
82
- * @returns {void}
65
+ * Given a filepath, get the nearest path that is a regular file.
66
+ * The filepath provided by eslint may be a virtual filepath rather than a file
67
+ * on disk. This attempts to transform a virtual path into an on-disk path
68
+ * @param {string} filepath
69
+ * @returns {string}
83
70
  */
84
- function reportReplace(context, offset, deleteText, insertText) {
85
- const start = context.getSourceCode().getLocFromIndex(offset);
86
- const end = context
87
- .getSourceCode()
88
- .getLocFromIndex(offset + deleteText.length);
89
- const range = [offset, offset + deleteText.length];
90
- context.report({
91
- message: 'Replace `{{ deleteCode }}` with `{{ insertCode }}`',
92
- data: {
93
- deleteCode: showInvisibles(deleteText),
94
- insertCode: showInvisibles(insertText)
95
- },
96
- loc: { start, end },
97
- fix(fixer) {
98
- return fixer.replaceTextRange(range, insertText);
71
+ function getOnDiskFilepath(filepath) {
72
+ try {
73
+ if (fs.statSync(filepath).isFile()) {
74
+ return filepath;
99
75
  }
100
- });
76
+ } catch (err) {
77
+ // https://github.com/eslint/eslint/issues/11989
78
+ if (err.code === 'ENOTDIR') {
79
+ return getOnDiskFilepath(path.dirname(filepath));
80
+ }
81
+ }
82
+
83
+ return filepath;
101
84
  }
102
85
 
103
86
  // ------------------------------------------------------------------------------
@@ -143,7 +126,12 @@ module.exports = {
143
126
  },
144
127
  additionalProperties: true
145
128
  }
146
- ]
129
+ ],
130
+ messages: {
131
+ [INSERT]: 'Insert `{{ insertText }}`',
132
+ [DELETE]: 'Delete `{{ deleteText }}`',
133
+ [REPLACE]: 'Replace `{{ deleteText }}` with `{{ insertText }}`'
134
+ }
147
135
  },
148
136
  create(context) {
149
137
  const usePrettierrc =
@@ -152,6 +140,14 @@ module.exports = {
152
140
  (context.options[1] && context.options[1].fileInfoOptions) || {};
153
141
  const sourceCode = context.getSourceCode();
154
142
  const filepath = context.getFilename();
143
+ // Processors that extract content from a file, such as the markdown
144
+ // plugin extracting fenced code blocks may choose to specify virtual
145
+ // file paths. If this is the case then we need to resolve prettier
146
+ // config and file info using the on-disk path instead of the virtual
147
+ // path.
148
+ // See https://github.com/eslint/eslint/issues/11989 for ideas around
149
+ // being able to get this value directly from eslint in the future.
150
+ const onDiskFilepath = getOnDiskFilepath(filepath);
155
151
  const source = sourceCode.text;
156
152
 
157
153
  return {
@@ -164,13 +160,13 @@ module.exports = {
164
160
  const eslintPrettierOptions = context.options[0] || {};
165
161
 
166
162
  const prettierRcOptions = usePrettierrc
167
- ? prettier.resolveConfig.sync(filepath, {
163
+ ? prettier.resolveConfig.sync(onDiskFilepath, {
168
164
  editorconfig: true
169
165
  })
170
166
  : null;
171
167
 
172
168
  const prettierFileInfo = prettier.getFileInfo.sync(
173
- filepath,
169
+ onDiskFilepath,
174
170
  Object.assign(
175
171
  {},
176
172
  { resolveConfig: true, ignorePath: '.prettierignore' },
@@ -185,7 +181,7 @@ module.exports = {
185
181
 
186
182
  const initialOptions = {};
187
183
 
188
- // ESLint suppports processors that let you extract and lint JS
184
+ // ESLint supports processors that let you extract and lint JS
189
185
  // fragments within a non-JS language. In the cases where prettier
190
186
  // supports the same language as a processor, we want to process
191
187
  // the provided source code as javascript (as ESLint provides the
@@ -193,9 +189,14 @@ module.exports = {
193
189
  // based off the filename. Otherwise, for instance, on a .md file we
194
190
  // end up trying to run prettier over a fragment of JS using the
195
191
  // markdown parser, which throws an error.
196
- // If we can't infer the parser from from the filename, either
197
- // because no filename was provided or because there is no parser
198
- // found for the filename, use javascript.
192
+ // Processors may set virtual filenames for these extracted blocks.
193
+ // If they do so then we want to trust the file extension they
194
+ // provide, and no override is needed.
195
+ // If the processor does not set any virtual filename (signified by
196
+ // `filepath` and `onDiskFilepath` being equal) AND we can't
197
+ // infer the parser from the filename, either because no filename
198
+ // was provided or because there is no parser found for the
199
+ // filename, use javascript.
199
200
  // This is added to the options first, so that
200
201
  // prettierRcOptions and eslintPrettierOptions can still override
201
202
  // the parser.
@@ -207,6 +208,7 @@ module.exports = {
207
208
  // from the file type.
208
209
  const parserBlocklist = [null, 'graphql', 'markdown', 'html'];
209
210
  if (
211
+ filepath === onDiskFilepath &&
210
212
  parserBlocklist.indexOf(prettierFileInfo.inferredParser) !== -1
211
213
  ) {
212
214
  // Prettier v1.16.0 renamed the `babylon` parser to `babel`
@@ -227,7 +229,7 @@ module.exports = {
227
229
  );
228
230
 
229
231
  // prettier.format() may throw a SyntaxError if it cannot parse the
230
- // source code it is given. Ususally for JS files this isn't a
232
+ // source code it is given. Usually for JS files this isn't a
231
233
  // problem as ESLint will report invalid syntax before trying to
232
234
  // pass it to the prettier plugin. However this might be a problem
233
235
  // for non-JS languages that are handled by a plugin. Notably Vue
@@ -245,7 +247,7 @@ module.exports = {
245
247
  let message = 'Parsing error: ' + err.message;
246
248
 
247
249
  // Prettier's message contains a codeframe style preview of the
248
- // invalid code and the line/column at which the error occured.
250
+ // invalid code and the line/column at which the error occurred.
249
251
  // ESLint shows those pieces of information elsewhere already so
250
252
  // remove them from the message
251
253
  if (err.codeFrame) {
@@ -263,32 +265,9 @@ module.exports = {
263
265
  if (source !== prettierSource) {
264
266
  const differences = generateDifferences(source, prettierSource);
265
267
 
266
- differences.forEach(difference => {
267
- switch (difference.operation) {
268
- case INSERT:
269
- reportInsert(
270
- context,
271
- difference.offset,
272
- difference.insertText
273
- );
274
- break;
275
- case DELETE:
276
- reportDelete(
277
- context,
278
- difference.offset,
279
- difference.deleteText
280
- );
281
- break;
282
- case REPLACE:
283
- reportReplace(
284
- context,
285
- difference.offset,
286
- difference.deleteText,
287
- difference.insertText
288
- );
289
- break;
290
- }
291
- });
268
+ for (const difference of differences) {
269
+ reportDifference(context, difference);
270
+ }
292
271
  }
293
272
  }
294
273
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-prettier",
3
- "version": "3.3.1",
3
+ "version": "3.4.0",
4
4
  "description": "Runs prettier as an eslint rule",
5
5
  "keywords": [
6
6
  "eslint",