7
7
8
8
/**
9
9
* @import {AST, ESLint, Linter, Rule, SourceCode} from 'eslint'
10
+ * @import {Position} from 'estree'
10
11
* @import {FileInfoOptions, Options as PrettierOptions} from 'prettier'
11
12
* @import {Difference} from 'prettier-linter-helpers'
12
13
*/
13
14
14
15
/**
15
- * @typedef {{ line: number; column: number; offset: number } } Location
16
- *
17
16
* @typedef {PrettierOptions & {
18
17
* onDiskFilepath: string;
19
18
* parserMeta?: ESLint.ObjectMetaProperties['meta'];
27
26
* options: Options,
28
27
* fileInfoOptions: FileInfoOptions,
29
28
* ) => string} PrettierFormat
30
- *
31
- *
32
- * @typedef {Parameters<
33
- * Exclude<ESLint.Plugin['rules'], undefined>[string]['create']
34
- * >[0]} RuleContext
35
29
*/
36
30
37
31
'use strict' ;
@@ -64,29 +58,46 @@ let prettierFormat;
64
58
// Rule Definition
65
59
// ------------------------------------------------------------------------------
66
60
61
+ /** @type {WeakMap<SourceCode, number[]> } */
62
+ const lineIndexesCache = new WeakMap ( ) ;
63
+
67
64
/**
68
- * Converts a byte offset to a Location .
65
+ * Ponyfill `sourceCode.getLocFromIndex` when it's unavailable .
69
66
*
70
67
* See also `getLocFromIndex` in `@eslint/js`.
71
68
*
72
- * @param {number[] } lineIndexes
73
- * @param {number } offset
74
- * @returns {Location }
69
+ * @param {SourceCode } sourceCode
70
+ * @param {number } index
71
+ * @returns {Position }
75
72
*/
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
+
77
88
let line = 0 ;
78
- while ( line + 1 < lineIndexes . length && lineIndexes [ line + 1 ] < offset ) {
89
+ while ( line + 1 < lineIndexes . length && lineIndexes [ line + 1 ] < index ) {
79
90
line += 1 ;
80
91
}
92
+ const column = index - lineIndexes [ line ] ;
81
93
82
- const column = offset - lineIndexes [ line ] ;
83
- return { line : line + 1 , column, offset } ;
94
+ return { line : line + 1 , column } ;
84
95
}
85
96
86
97
/**
87
98
* Reports a difference.
88
99
*
89
- * @param {RuleContext } context - The ESLint rule context.
100
+ * @param {Rule. RuleContext } context - The ESLint rule context.
90
101
* @param {Difference } difference - The difference object.
91
102
* @returns {void }
92
103
*/
@@ -99,38 +110,7 @@ function reportDifference(context, difference) {
99
110
// TODO: Only use property when our eslint peerDependency is >=8.40.0.
100
111
const sourceCode = context . sourceCode ?? context . getSourceCode ( ) ;
101
112
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 ) ) ;
134
114
135
115
context . report ( {
136
116
messageId : operation ,
@@ -147,7 +127,7 @@ function reportDifference(context, difference) {
147
127
// Module Definition
148
128
// ------------------------------------------------------------------------------
149
129
150
- /** @satisfies {ESLint.Plugin } */
130
+ /** @type {ESLint.Plugin } */
151
131
const eslintPluginPrettier = {
152
132
meta : { name, version } ,
153
133
configs : {
@@ -225,6 +205,7 @@ const eslintPluginPrettier = {
225
205
const source = sourceCode . text ;
226
206
227
207
return {
208
+ /** @param {unknown } node */
228
209
[ sourceCode . ast . type ] ( node ) {
229
210
if ( ! prettierFormat ) {
230
211
// Prettier is expensive to load, so only load it if needed.
0 commit comments