3
3
* @author Loren Klingman
4
4
* Original ESLint sort-keys by Toru Nagashima
5
5
*/
6
- // @ts -nocheck
7
6
'use strict'
8
7
9
8
// ------------------------------------------------------------------------------
@@ -121,14 +120,22 @@ module.exports = {
121
120
} ,
122
121
additionalProperties : false
123
122
}
124
- ]
123
+ ] ,
124
+ messages : {
125
+ sortKeys :
126
+ "Expected object keys to be in {{natural}}{{insensitive}}{{order}}ending order. '{{thisName}}' should be before '{{prevName}}'."
127
+ }
125
128
} ,
126
- /** @param {RuleContext } context */
129
+ /**
130
+ * @param {RuleContext } context - The rule context.
131
+ * @returns {RuleListener } AST event handlers.
132
+ */
127
133
create ( context ) {
128
134
// Parse options.
129
135
const options = context . options [ 1 ]
130
136
const order = context . options [ 0 ] || 'asc'
131
137
138
+ /** @type {string[] } */
132
139
const ignoreGrandchildrenOf = ( options &&
133
140
options . ignoreGrandchildrenOf ) || [
134
141
'computed' ,
@@ -137,127 +144,128 @@ module.exports = {
137
144
'props' ,
138
145
'watch'
139
146
]
147
+ /** @type {string[] } */
140
148
const ignoreChildrenOf = ( options && options . ignoreChildrenOf ) || [ 'model' ]
141
149
const insensitive = options && options . caseSensitive === false
142
150
const minKeys = options && options . minKeys
143
151
const natural = options && options . natural
144
152
const isValidOrder =
145
153
isValidOrders [ order + ( insensitive ? 'I' : '' ) + ( natural ? 'N' : '' ) ]
146
154
147
- // The stack to save the previous property's name for each object literals.
148
- let stack = null
149
-
150
- let errors = [ ]
151
- const names = { }
152
-
153
155
/**
154
- * @param {boolean } isVue
156
+ * @typedef {object } ObjectStack
157
+ * @property {ObjectStack } upper
158
+ * @property {string | null } prevName
159
+ * @property {number } numKeys
160
+ * @property {Property | null } currentProperty
161
+ * @property {boolean } isVueObject
162
+ * @property {boolean } withinVueObject
163
+ * @property {string | null } withinVuePropName
164
+ * @property {number } withinVuePropChainLevel
165
+ * @property {boolean } ignore
155
166
*/
156
- const reportErrors = ( isVue ) => {
157
- if ( isVue ) {
158
- errors = errors . filter ( ( error ) => {
159
- let parentIsRoot = ! error . hasUpper
160
- let grandParentIsRoot = ! error . grandparent
161
- let greatGrandparentIsRoot = ! error . greatGrandparent
162
167
163
- const stackPrevChar = stack && stack . prevChar
164
- if ( stackPrevChar ) {
165
- parentIsRoot = stackPrevChar === error . parent
166
- grandParentIsRoot = stackPrevChar === error . grandparent
167
- greatGrandparentIsRoot = stackPrevChar === error . greatGrandparent
168
- }
168
+ /**
169
+ * The stack to save the previous property's name for each object literals.
170
+ * @type {ObjectStack }
171
+ */
172
+ let stack
169
173
170
- if ( parentIsRoot ) {
171
- return false
172
- } else if ( grandParentIsRoot ) {
173
- return (
174
- ! error . parentIsProperty ||
175
- ! ignoreChildrenOf . includes ( names [ error . parent ] )
176
- )
177
- } else if ( greatGrandparentIsRoot ) {
178
- return (
179
- ! error . parentIsProperty ||
180
- ! ignoreGrandchildrenOf . includes ( names [ error . grandparent ] )
181
- )
182
- }
183
- return true
184
- } )
174
+ /**
175
+ * @param {Property | null } prop
176
+ * @param {Expression } expr
177
+ */
178
+ function isPropertyChain ( prop , expr ) {
179
+ let value = expr
180
+ while ( value . parent . type === 'TSAsExpression' ) {
181
+ value = value . parent
185
182
}
186
- errors . forEach ( ( error ) => error . errors . forEach ( ( e ) => context . report ( e ) ) )
187
- errors = [ ]
183
+ return prop === value . parent && prop . value === value
188
184
}
189
185
190
- const sortTests = {
191
- /** @param {ObjectExpression } node */
186
+ return {
192
187
ObjectExpression ( node ) {
193
- if ( ! stack ) {
194
- reportErrors ( false )
188
+ const isVueObject = utils . getVueObjectType ( context , node ) != null
189
+ let withinVueObject = isVueObject
190
+ /** @type {string | null } */
191
+ let withinVuePropName = null
192
+ let withinVuePropChainLevel = NaN
193
+ if ( ! isVueObject && stack ) {
194
+ if ( stack . withinVueObject && stack . currentProperty ) {
195
+ const isChain = isPropertyChain ( stack . currentProperty , node )
196
+ if ( isChain ) {
197
+ if ( stack . isVueObject ) {
198
+ withinVuePropName = utils . getStaticPropertyName (
199
+ stack . currentProperty
200
+ )
201
+ withinVuePropChainLevel = 1
202
+ } else {
203
+ withinVuePropName = stack . withinVuePropName
204
+ withinVuePropChainLevel = stack . withinVuePropChainLevel + 1
205
+ }
206
+ withinVueObject = true
207
+ } else {
208
+ withinVueObject = false
209
+ }
210
+ }
211
+ }
212
+
213
+ let ignore = isVueObject
214
+
215
+ if ( withinVueObject && withinVuePropName != null ) {
216
+ if ( withinVuePropChainLevel === 1 ) {
217
+ if ( ignoreChildrenOf . includes ( withinVuePropName ) ) {
218
+ ignore = true
219
+ }
220
+ } else if ( withinVuePropChainLevel === 2 ) {
221
+ if ( ignoreGrandchildrenOf . includes ( withinVuePropName ) ) {
222
+ ignore = true
223
+ }
224
+ }
195
225
}
196
226
stack = {
197
227
upper : stack ,
198
- prevChar : null ,
199
228
prevName : null ,
200
229
numKeys : node . properties . length ,
201
- parentIsProperty : node . parent . type === 'Property' ,
202
- errors : [ ]
230
+ currentProperty : null ,
231
+ isVueObject,
232
+ withinVueObject,
233
+ withinVuePropName,
234
+ withinVuePropChainLevel,
235
+ ignore
203
236
}
204
237
} ,
205
- /** @param {ObjectExpression } node */
206
- 'ObjectExpression:exit' ( node ) {
207
- errors . push ( {
208
- errors : stack . errors ,
209
- hasUpper : ! ! stack . upper ,
210
- parentIsProperty : node . parent . type === 'Property' ,
211
- parent : stack . upper && stack . upper . prevChar ,
212
- grandparent :
213
- stack . upper && stack . upper . upper && stack . upper . upper . prevChar ,
214
- greatGrandparent :
215
- stack . upper &&
216
- stack . upper . upper &&
217
- stack . upper . upper . upper &&
218
- stack . upper . upper . upper . prevChar
219
- } )
238
+ 'ObjectExpression:exit' ( ) {
220
239
stack = stack . upper
221
240
} ,
222
- /** @param {SpreadElement } node */
223
241
SpreadElement ( node ) {
224
242
if ( node . parent . type === 'ObjectExpression' ) {
225
243
stack . prevName = null
226
- stack . prevChar = null
227
244
}
228
245
} ,
229
- 'Program:exit' ( ) {
230
- reportErrors ( false )
231
- } ,
232
- /** @param {Property } node */
233
- Property ( node ) {
234
- if ( node . parent . type === 'ObjectPattern' ) {
246
+ 'ObjectExpression > Property' ( node ) {
247
+ stack . currentProperty = node
248
+
249
+ if ( stack . ignore ) {
235
250
return
236
251
}
237
-
238
252
const prevName = stack . prevName
239
253
const numKeys = stack . numKeys
240
254
const thisName = getPropertyName ( node )
241
255
242
256
if ( thisName !== null ) {
243
257
stack . prevName = thisName
244
- stack . prevChar = node . range [ 0 ]
245
- if ( Object . prototype . hasOwnProperty . call ( names , node . range [ 0 ] ) ) {
246
- throw new Error ( 'Name clash' )
247
- }
248
- names [ node . range [ 0 ] ] = thisName
249
258
}
250
259
251
260
if ( prevName === null || thisName === null || numKeys < minKeys ) {
252
261
return
253
262
}
254
263
255
264
if ( ! isValidOrder ( prevName , thisName ) ) {
256
- stack . errors . push ( {
265
+ context . report ( {
257
266
node,
258
267
loc : node . key . loc ,
259
- message :
260
- "Expected object keys to be in {{natural}}{{insensitive}}{{order}}ending order. '{{thisName}}' should be before '{{prevName}}'." ,
268
+ messageId : 'sortKeys' ,
261
269
data : {
262
270
thisName,
263
271
prevName,
@@ -269,11 +277,5 @@ module.exports = {
269
277
}
270
278
}
271
279
}
272
-
273
- const execOnVue = utils . executeOnVue ( context , ( ) => {
274
- reportErrors ( true )
275
- } )
276
-
277
- return utils . compositingVisitors ( sortTests , execOnVue )
278
280
}
279
281
}
0 commit comments