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'];
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,52 @@ 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+ if ( ! ( 'text' in sourceCode ) ) {
79+ throw new Error (
80+ 'prettier/prettier: non-textual source code is unsupported' ,
81+ ) ;
82+ }
83+
84+ let lineIndexes = lineIndexesCache . get ( sourceCode ) ;
85+
86+ if ( ! lineIndexes ) {
87+ lineIndexes = [
88+ 0 ,
89+ ...sourceCode . text . matchAll ( / \n / g) . map ( match => match . index ) ,
90+ ] ;
91+ lineIndexesCache . set ( sourceCode , lineIndexes ) ;
92+ }
93+
7794 let line = 0 ;
78- while ( line + 1 < lineIndexes . length && lineIndexes [ line + 1 ] < offset ) {
95+ while ( line + 1 < lineIndexes . length && lineIndexes [ line + 1 ] < index ) {
7996 line += 1 ;
8097 }
8198
82- const column = offset - lineIndexes [ line ] ;
83- return { line : line + 1 , column, offset } ;
99+ const column = index - lineIndexes [ line ] ;
100+ return { line : line + 1 , column } ;
84101}
85102
86103/**
87104 * Reports a difference.
88105 *
89- * @param {RuleContext } context - The ESLint rule context.
106+ * @param {Rule. RuleContext } context - The ESLint rule context.
90107 * @param {Difference } difference - The difference object.
91108 * @returns {void }
92109 */
@@ -99,38 +116,7 @@ function reportDifference(context, difference) {
99116 // TODO: Only use property when our eslint peerDependency is >=8.40.0.
100117 const sourceCode = context . sourceCode ?? context . getSourceCode ( ) ;
101118
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- ) ;
119+ const [ start , end ] = range . map ( index => getLocFromIndex ( sourceCode , index ) ) ;
134120
135121 context . report ( {
136122 messageId : operation ,
@@ -147,7 +133,7 @@ function reportDifference(context, difference) {
147133// Module Definition
148134// ------------------------------------------------------------------------------
149135
150- /** @satisfies {ESLint.Plugin } */
136+ /** @type {ESLint.Plugin } */
151137const eslintPluginPrettier = {
152138 meta : { name, version } ,
153139 configs : {
@@ -225,6 +211,7 @@ const eslintPluginPrettier = {
225211 const source = sourceCode . text ;
226212
227213 return {
214+ /** @param {unknown } node */
228215 [ sourceCode . ast . type ] ( node ) {
229216 if ( ! prettierFormat ) {
230217 // Prettier is expensive to load, so only load it if needed.
0 commit comments