1- import { createRule , defineWrapperListener , getCoreRule } from "../utils" ;
2- const coreRule = getCoreRule ( "array-bracket-newline" ) ;
3-
1+ // Most source code was copied from ESLint v8.
2+ // MIT License. Copyright OpenJS Foundation and other contributors, <www.openjsf.org>
3+ import type { AST } from "jsonc-eslint-parser" ;
4+ import { createRule } from "../utils" ;
5+ import { getSourceCode } from "eslint-compat-utils" ;
6+ import { isTokenOnSameLine } from "../utils/eslint-ast-utils" ;
7+ import type { Token } from "../types" ;
8+ import { isCommentToken } from "@eslint-community/eslint-utils" ;
9+ type Schema0 =
10+ | ( "always" | "never" | "consistent" )
11+ | {
12+ multiline ?: boolean ;
13+ minItems ?: number | null ;
14+ } ;
415export default createRule ( "array-bracket-newline" , {
516 meta : {
617 docs : {
@@ -10,13 +21,224 @@ export default createRule("array-bracket-newline", {
1021 extensionRule : true ,
1122 layout : true ,
1223 } ,
13- fixable : coreRule . meta ?. fixable ,
14- hasSuggestions : ( coreRule . meta as any ) . hasSuggestions ,
15- schema : coreRule . meta ! . schema ! ,
16- messages : coreRule . meta ! . messages ! ,
17- type : coreRule . meta ! . type ! ,
24+ type : "layout" ,
25+
26+ fixable : "whitespace" ,
27+
28+ schema : [
29+ {
30+ oneOf : [
31+ {
32+ type : "string" ,
33+ enum : [ "always" , "never" , "consistent" ] ,
34+ } ,
35+ {
36+ type : "object" ,
37+ properties : {
38+ multiline : {
39+ type : "boolean" ,
40+ } ,
41+ minItems : {
42+ type : [ "integer" , "null" ] ,
43+ minimum : 0 ,
44+ } ,
45+ } ,
46+ additionalProperties : false ,
47+ } ,
48+ ] ,
49+ } ,
50+ ] ,
51+
52+ messages : {
53+ unexpectedOpeningLinebreak : "There should be no linebreak after '['." ,
54+ unexpectedClosingLinebreak : "There should be no linebreak before ']'." ,
55+ missingOpeningLinebreak : "A linebreak is required after '['." ,
56+ missingClosingLinebreak : "A linebreak is required before ']'." ,
57+ } ,
1858 } ,
1959 create ( context ) {
20- return defineWrapperListener ( coreRule , context , context . options ) ;
60+ const sourceCode = getSourceCode ( context ) ;
61+
62+ /**
63+ * Normalizes a given option value.
64+ * @param option An option value to parse.
65+ * @returns Normalized option object.
66+ */
67+ function normalizeOptionValue ( option : Schema0 ) {
68+ let consistent = false ;
69+ let multiline = false ;
70+ let minItems = 0 ;
71+
72+ if ( option ) {
73+ if ( option === "consistent" ) {
74+ consistent = true ;
75+ minItems = Number . POSITIVE_INFINITY ;
76+ } else if (
77+ option === "always" ||
78+ ( typeof option !== "string" && option . minItems === 0 )
79+ ) {
80+ minItems = 0 ;
81+ } else if ( option === "never" ) {
82+ minItems = Number . POSITIVE_INFINITY ;
83+ } else {
84+ multiline = Boolean ( option . multiline ) ;
85+ minItems = option . minItems || Number . POSITIVE_INFINITY ;
86+ }
87+ } else {
88+ consistent = false ;
89+ multiline = true ;
90+ minItems = Number . POSITIVE_INFINITY ;
91+ }
92+
93+ return { consistent, multiline, minItems } ;
94+ }
95+
96+ /**
97+ * Normalizes a given option value.
98+ * @param options An option value to parse.
99+ * @returns Normalized option object.
100+ */
101+ function normalizeOptions ( options : Schema0 ) {
102+ const value = normalizeOptionValue ( options ) ;
103+
104+ return { JSONArrayExpression : value , JSONArrayPattern : value } ;
105+ }
106+
107+ /**
108+ * Reports that there shouldn't be a linebreak after the first token
109+ * @param node The node to report in the event of an error.
110+ * @param token The token to use for the report.
111+ */
112+ function reportNoBeginningLinebreak ( node : AST . JSONNode , token : Token ) {
113+ context . report ( {
114+ node : node as any ,
115+ loc : token . loc ,
116+ messageId : "unexpectedOpeningLinebreak" ,
117+ fix ( fixer ) {
118+ const nextToken = sourceCode . getTokenAfter ( token , {
119+ includeComments : true ,
120+ } ) ;
121+
122+ if ( ! nextToken || isCommentToken ( nextToken ) ) return null ;
123+
124+ return fixer . removeRange ( [ token . range [ 1 ] , nextToken . range [ 0 ] ] ) ;
125+ } ,
126+ } ) ;
127+ }
128+
129+ /**
130+ * Reports that there shouldn't be a linebreak before the last token
131+ * @param node The node to report in the event of an error.
132+ * @param token The token to use for the report.
133+ */
134+ function reportNoEndingLinebreak ( node : AST . JSONNode , token : Token ) {
135+ context . report ( {
136+ node : node as any ,
137+ loc : token . loc ,
138+ messageId : "unexpectedClosingLinebreak" ,
139+ fix ( fixer ) {
140+ const previousToken = sourceCode . getTokenBefore ( token , {
141+ includeComments : true ,
142+ } ) ;
143+
144+ if ( ! previousToken || isCommentToken ( previousToken ) ) return null ;
145+
146+ return fixer . removeRange ( [ previousToken . range [ 1 ] , token . range [ 0 ] ] ) ;
147+ } ,
148+ } ) ;
149+ }
150+
151+ /**
152+ * Reports that there should be a linebreak after the first token
153+ * @param node The node to report in the event of an error.
154+ * @param token The token to use for the report.
155+ */
156+ function reportRequiredBeginningLinebreak (
157+ node : AST . JSONNode ,
158+ token : Token ,
159+ ) {
160+ context . report ( {
161+ node : node as any ,
162+ loc : token . loc ,
163+ messageId : "missingOpeningLinebreak" ,
164+ fix ( fixer ) {
165+ return fixer . insertTextAfter ( token , "\n" ) ;
166+ } ,
167+ } ) ;
168+ }
169+
170+ /**
171+ * Reports that there should be a linebreak before the last token
172+ * @param node The node to report in the event of an error.
173+ * @param token The token to use for the report.
174+ */
175+ function reportRequiredEndingLinebreak ( node : AST . JSONNode , token : Token ) {
176+ context . report ( {
177+ node : node as any ,
178+ loc : token . loc ,
179+ messageId : "missingClosingLinebreak" ,
180+ fix ( fixer ) {
181+ return fixer . insertTextBefore ( token , "\n" ) ;
182+ } ,
183+ } ) ;
184+ }
185+
186+ /**
187+ * Reports a given node if it violated this rule.
188+ * @param node A node to check. This is an ArrayExpression node or an ArrayPattern node.
189+ */
190+ function check ( node : AST . JSONNode ) {
191+ // @ts -expect-error type cast
192+ const elements = node . elements ;
193+ const normalizedOptions = normalizeOptions ( context . options [ 0 ] ) ;
194+ // @ts -expect-error type cast
195+ const options = normalizedOptions [ node . type ] ;
196+ const openBracket = sourceCode . getFirstToken ( node as any ) ! ;
197+ const closeBracket = sourceCode . getLastToken ( node as any ) ! ;
198+ const firstIncComment = sourceCode . getTokenAfter ( openBracket , {
199+ includeComments : true ,
200+ } ) ! ;
201+ const lastIncComment = sourceCode . getTokenBefore ( closeBracket , {
202+ includeComments : true ,
203+ } ) ! ;
204+ const first = sourceCode . getTokenAfter ( openBracket ) ! ;
205+ const last = sourceCode . getTokenBefore ( closeBracket ) ! ;
206+ const needsLinebreaks =
207+ elements . length >= options . minItems ||
208+ ( options . multiline &&
209+ elements . length > 0 &&
210+ firstIncComment . loc ! . start . line !== lastIncComment . loc ! . end . line ) ||
211+ ( elements . length === 0 &&
212+ firstIncComment . type === "Block" &&
213+ firstIncComment . loc ! . start . line !== lastIncComment . loc ! . end . line &&
214+ firstIncComment === lastIncComment ) ||
215+ ( options . consistent &&
216+ openBracket . loc . end . line !== first . loc . start . line ) ;
217+
218+ /**
219+ * Use tokens or comments to check multiline or not.
220+ * But use only tokens to check whether linebreaks are needed.
221+ * This allows:
222+ * var arr = [ // eslint-disable-line foo
223+ * 'a'
224+ * ]
225+ */
226+
227+ if ( needsLinebreaks ) {
228+ if ( isTokenOnSameLine ( openBracket , first ) )
229+ reportRequiredBeginningLinebreak ( node , openBracket ) ;
230+ if ( isTokenOnSameLine ( last , closeBracket ) )
231+ reportRequiredEndingLinebreak ( node , closeBracket ) ;
232+ } else {
233+ if ( ! isTokenOnSameLine ( openBracket , first ) )
234+ reportNoBeginningLinebreak ( node , openBracket ) ;
235+ if ( ! isTokenOnSameLine ( last , closeBracket ) )
236+ reportNoEndingLinebreak ( node , closeBracket ) ;
237+ }
238+ }
239+
240+ return {
241+ JSONArrayExpression : check ,
242+ } ;
21243 } ,
22244} ) ;
0 commit comments