Skip to content

Commit c4053cb

Browse files
committed
refactor: cleanup
1 parent 8ec6cde commit c4053cb

File tree

4 files changed

+89
-73
lines changed

4 files changed

+89
-73
lines changed

.changeset/weak-lamps-refuse.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"eslint-plugin-prettier": minor
3+
---
4+
5+
feat: support non-js languages like `css` for `@eslint/css` and `json` for `@eslint/json`

eslint-plugin-prettier.js

Lines changed: 30 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@
77

88
/**
99
* @import {AST, ESLint, Linter, Rule, SourceCode} from 'eslint'
10+
* @import {Position} from 'estree'
1011
* @import {FileInfoOptions, Options as PrettierOptions} from 'prettier'
1112
* @import {Difference} from 'prettier-linter-helpers'
1213
*/
1314

1415
/**
15-
* @typedef {{ line: number; column: number; offset: number }} Location
16-
*
1716
* @typedef {PrettierOptions & {
1817
* onDiskFilepath: string;
1918
* parserMeta?: ESLint.ObjectMetaProperties['meta'];
@@ -27,11 +26,6 @@
2726
* options: Options,
2827
* fileInfoOptions: FileInfoOptions,
2928
* ) => string} PrettierFormat
30-
*
31-
*
32-
* @typedef {Parameters<
33-
* Exclude<ESLint.Plugin['rules'], undefined>[string]['create']
34-
* >[0]} RuleContext
3529
*/
3630

3731
'use strict';
@@ -64,29 +58,46 @@ let prettierFormat;
6458
// Rule Definition
6559
// ------------------------------------------------------------------------------
6660

61+
/** @type {WeakMap<SourceCode, number[]>} */
62+
const lineIndexesCache = new WeakMap();
63+
6764
/**
68-
* Converts a byte offset to a Location.
65+
* Ponyfill `sourceCode.getLocFromIndex` when it's unavailable.
6966
*
7067
* See also `getLocFromIndex` in `@eslint/js`.
7168
*
72-
* @param {number[]} lineIndexes
73-
* @param {number} offset
74-
* @returns {Location}
69+
* @param {SourceCode} sourceCode
70+
* @param {number} index
71+
* @returns {Position}
7572
*/
76-
function getLocFromOffset(lineIndexes, offset) {
73+
function getLocFromIndex(sourceCode, index) {
74+
if (typeof sourceCode.getLocFromIndex === 'function') {
75+
return sourceCode.getLocFromIndex(index);
76+
}
77+
78+
let lineIndexes = lineIndexesCache.get(sourceCode);
79+
if (!lineIndexes) {
80+
lineIndexes = [...sourceCode.text.matchAll(/\r?\n/g)].map(
81+
match => match.index,
82+
);
83+
// first line in the file starts at byte offset 0
84+
lineIndexes.unshift(0);
85+
lineIndexesCache.set(sourceCode, lineIndexes);
86+
}
87+
7788
let line = 0;
78-
while (line + 1 < lineIndexes.length && lineIndexes[line + 1] < offset) {
89+
while (line + 1 < lineIndexes.length && lineIndexes[line + 1] < index) {
7990
line += 1;
8091
}
92+
const column = index - lineIndexes[line];
8193

82-
const column = offset - lineIndexes[line];
83-
return { line: line + 1, column, offset };
94+
return { line: line + 1, column };
8495
}
8596

8697
/**
8798
* Reports a difference.
8899
*
89-
* @param {RuleContext} context - The ESLint rule context.
100+
* @param {Rule.RuleContext} context - The ESLint rule context.
90101
* @param {Difference} difference - The difference object.
91102
* @returns {void}
92103
*/
@@ -99,38 +110,7 @@ function reportDifference(context, difference) {
99110
// TODO: Only use property when our eslint peerDependency is >=8.40.0.
100111
const sourceCode = context.sourceCode ?? context.getSourceCode();
101112

102-
const lazy = {
103-
/**
104-
* Lazily computes the line indices for `sourceCode`.
105-
*
106-
* @returns {number[]}
107-
*/
108-
get lineIndexes() {
109-
// @ts-ignore
110-
delete this.lineIndexes;
111-
112-
if (!('text' in sourceCode)) {
113-
throw new Error(
114-
'prettier/prettier: non-textual source code is unsupported',
115-
);
116-
}
117-
118-
// @ts-ignore
119-
this.lineIndexes = [...sourceCode.text.matchAll(/\n/g)].map(
120-
match => match.index,
121-
);
122-
// first line in the file starts at byte offset 0
123-
this.lineIndexes.unshift(0);
124-
return this.lineIndexes;
125-
},
126-
};
127-
128-
const [start, end] = range.map(
129-
index =>
130-
// @ts-ignore
131-
sourceCode.getLocFromIndex?.(index) ??
132-
getLocFromOffset(lazy.lineIndexes, index),
133-
);
113+
const [start, end] = range.map(index => getLocFromIndex(sourceCode, index));
134114

135115
context.report({
136116
messageId: operation,
@@ -147,7 +127,7 @@ function reportDifference(context, difference) {
147127
// Module Definition
148128
// ------------------------------------------------------------------------------
149129

150-
/** @satisfies {ESLint.Plugin} */
130+
/** @type {ESLint.Plugin} */
151131
const eslintPluginPrettier = {
152132
meta: { name, version },
153133
configs: {
@@ -225,6 +205,7 @@ const eslintPluginPrettier = {
225205
const source = sourceCode.text;
226206

227207
return {
208+
/** @param {unknown} node */
228209
[sourceCode.ast.type](node) {
229210
if (!prettierFormat) {
230211
// Prettier is expensive to load, so only load it if needed.

test/fixtures/empty.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

test/prettier.mjs

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ import eslintPluginJson from '@eslint/json';
3030
const rule = eslintPluginPrettier.rules.prettier;
3131
const RuleTester =
3232
eslintUnsupportedApi.FlatRuleTester ?? eslintPackage.RuleTester;
33-
const ESLint = eslintUnsupportedApi.FlatESLint ?? eslintPackage.ESLint;
33+
const ESLint = eslintUnsupportedApi.FlatESLint ?? eslint.ESLint;
34+
35+
const isESLint9 = !eslintUnsupportedApi.FlatRuleTester;
3436

3537
// ------------------------------------------------------------------------------
3638
// Tests
@@ -381,30 +383,52 @@ runFixture('invalid-prettierrc/*', [
381383
],
382384
]);
383385

384-
const jsonRuleTester = new RuleTester({
385-
plugins: {
386-
json: eslintPluginJson,
387-
},
388-
language: 'json/json',
389-
});
390-
391-
jsonRuleTester.run('@eslint/json', rule, {
392-
valid: [
393-
{
394-
code: '{}\n',
395-
filename: 'empty.json',
396-
},
386+
runFixture('*.json', [
387+
[
397388
{
398-
code: '{ "foo": 1 }\n',
399-
filename: 'simple.json',
389+
column: 1,
390+
endColumn: 1,
391+
endLine: 2,
392+
fix: {
393+
range: [0, 1],
394+
text: '',
395+
},
396+
line: 1,
397+
message: 'Delete `⏎`',
398+
messageId: 'delete',
399+
nodeType: null,
400+
ruleId: 'prettier/prettier',
401+
severity: 2,
400402
},
401403
],
402-
invalid: [
403-
Object.assign(loadInvalidFixture('json'), {
404-
filename: 'invalid.json',
405-
}),
406-
],
407-
});
404+
]);
405+
406+
if (isESLint9) {
407+
const jsonRuleTester = new RuleTester({
408+
plugins: {
409+
json: eslintPluginJson,
410+
},
411+
language: 'json/json',
412+
});
413+
414+
jsonRuleTester.run('@eslint/json', rule, {
415+
valid: [
416+
{
417+
code: '{}\n',
418+
filename: 'empty.json',
419+
},
420+
{
421+
code: '{ "foo": 1 }\n',
422+
filename: 'simple.json',
423+
},
424+
],
425+
invalid: [
426+
Object.assign(loadInvalidFixture('json'), {
427+
filename: 'invalid.json',
428+
}),
429+
],
430+
});
431+
}
408432

409433
// ------------------------------------------------------------------------------
410434
// Helpers
@@ -462,7 +486,7 @@ function getPrettierRcJsFilename(dir, file = 'dummy.js') {
462486
* @type {ESLint}
463487
* @import {ESLint} from 'eslint'
464488
*/
465-
let eslint;
489+
var eslint; // bad mocha: `ReferenceError: Cannot access 'eslint' before initialization`
466490

467491
/**
468492
* @param {string} pattern
@@ -530,11 +554,16 @@ async function runFixture(pattern, asserts, skip) {
530554
pug: eslintPluginPug,
531555
},
532556
},
557+
{
558+
files: ['**/*.json'],
559+
plugins: {
560+
json: eslintPluginJson,
561+
},
562+
},
533563
],
534564
ignore: false,
535565
});
536566
}
537-
538567
if (skip) {
539568
return;
540569
}

0 commit comments

Comments
 (0)