5
5
'use strict'
6
6
7
7
const utils = require ( '../utils' )
8
+ const Traverser = require ( 'eslint/lib/util/traverser' )
8
9
9
10
const defaultOrder = [
10
11
'el' ,
@@ -56,6 +57,75 @@ function getOrderMap (order) {
56
57
return orderMap
57
58
}
58
59
60
+ function isComma ( node ) {
61
+ return node . type === 'Punctuator' && node . value === ','
62
+ }
63
+
64
+ const ARITHMETIC_OPERATORS = [ '+' , '-' , '*' , '/' , '%' , '**' ]
65
+ const BITWISE_OPERATORS = [ '&' , '|' , '^' , '~' , '<<' , '>>' , '>>>' ]
66
+ const COMPARISON_OPERATORS = [ '==' , '!=' , '===' , '!==' , '>' , '>=' , '<' , '<=' ]
67
+ const RELATIONAL_OPERATORS = [ 'in' , 'instanceof' ]
68
+ const ALL_BINARY_OPERATORS = [ ] . concat (
69
+ ARITHMETIC_OPERATORS ,
70
+ BITWISE_OPERATORS ,
71
+ COMPARISON_OPERATORS ,
72
+ RELATIONAL_OPERATORS
73
+ )
74
+ const LOGICAL_OPERATORS = [ '&&' , '||' ]
75
+
76
+ /*
77
+ * Result `true` if the node is sure that there are no side effects
78
+ *
79
+ * Currently known side effects types
80
+ *
81
+ * node.type === 'CallExpression'
82
+ * node.type === 'NewExpression'
83
+ * node.type === 'UpdateExpression'
84
+ * node.type === 'AssignmentExpression'
85
+ * node.type === 'TaggedTemplateExpression'
86
+ * node.type === 'UnaryExpression' && node.operator === 'delete'
87
+ *
88
+ * @param {ASTNode } node target node
89
+ * @param {Object } visitorKeys sourceCode.visitorKey
90
+ * @returns {Boolean } no side effects
91
+ */
92
+ function isNotSideEffectsNode ( node , visitorKeys ) {
93
+ let result = true
94
+ new Traverser ( ) . traverse ( node , {
95
+ visitorKeys,
96
+ enter ( node , parent ) {
97
+ if (
98
+ node . type === 'FunctionExpression' ||
99
+ node . type === 'Identifier' ||
100
+ node . type === 'Literal' ||
101
+ // es2015
102
+ node . type === 'ArrowFunctionExpression' ||
103
+ node . type === 'TemplateElement'
104
+ ) {
105
+ // no side effects node
106
+ this . skip ( )
107
+ } else if (
108
+ node . type !== 'Property' &&
109
+ node . type !== 'ObjectExpression' &&
110
+ node . type !== 'ArrayExpression' &&
111
+ ( node . type !== 'UnaryExpression' || [ '!' , '~' , '+' , '-' , 'typeof' ] . indexOf ( node . operator ) < 0 ) &&
112
+ ( node . type !== 'BinaryExpression' || ALL_BINARY_OPERATORS . indexOf ( node . operator ) < 0 ) &&
113
+ ( node . type !== 'LogicalExpression' || LOGICAL_OPERATORS . indexOf ( node . operator ) < 0 ) &&
114
+ node . type !== 'MemberExpression' &&
115
+ node . type !== 'ConditionalExpression' &&
116
+ // es2015
117
+ node . type !== 'SpreadElement' &&
118
+ node . type !== 'TemplateLiteral'
119
+ ) {
120
+ // Can not be sure that a node has no side effects
121
+ result = false
122
+ this . break ( )
123
+ }
124
+ }
125
+ } )
126
+ return result
127
+ }
128
+
59
129
// ------------------------------------------------------------------------------
60
130
// Rule Definition
61
131
// ------------------------------------------------------------------------------
@@ -67,7 +137,7 @@ module.exports = {
67
137
category : 'recommended' ,
68
138
url : 'https://github.com/vuejs/eslint-plugin-vue/blob/v4.2.2/docs/rules/order-in-components.md'
69
139
} ,
70
- fixable : null ,
140
+ fixable : 'code' , // null or "code" or "whitespace"
71
141
schema : [
72
142
{
73
143
type : 'object' ,
@@ -86,6 +156,7 @@ module.exports = {
86
156
const order = options . order || defaultOrder
87
157
const extendedOrder = order . map ( property => groups [ property ] || property )
88
158
const orderMap = getOrderMap ( extendedOrder )
159
+ const sourceCode = context . getSourceCode ( )
89
160
90
161
function checkOrder ( propertiesNodes , orderMap ) {
91
162
const properties = propertiesNodes
@@ -109,6 +180,35 @@ module.exports = {
109
180
name : property . name ,
110
181
firstUnorderedPropertyName : firstUnorderedProperty . name ,
111
182
line
183
+ } ,
184
+ fix ( fixer ) {
185
+ const propertyNode = property . parent
186
+ const firstUnorderedPropertyNode = firstUnorderedProperty . parent
187
+ const hasSideEffectsPossibility = propertiesNodes
188
+ . slice (
189
+ propertiesNodes . indexOf ( firstUnorderedPropertyNode ) ,
190
+ propertiesNodes . indexOf ( propertyNode ) + 1
191
+ )
192
+ . some ( ( property ) => ! isNotSideEffectsNode ( property , sourceCode . visitorKeys ) )
193
+ if ( hasSideEffectsPossibility ) {
194
+ return undefined
195
+ }
196
+ const comma = sourceCode . getTokenAfter ( propertyNode )
197
+ const hasAfterComma = isComma ( comma )
198
+
199
+ const codeStart = sourceCode . getTokenBefore ( propertyNode ) . range [ 1 ] // to include comments
200
+ const codeEnd = hasAfterComma ? comma . range [ 1 ] : propertyNode . range [ 1 ]
201
+
202
+ const propertyCode = sourceCode . text . slice ( codeStart , codeEnd ) + ( hasAfterComma ? '' : ',' )
203
+ const insertTarget = sourceCode . getTokenBefore ( firstUnorderedPropertyNode )
204
+ // If we can upgrade requirements to `eslint@>4.1.0`, this code can be replaced by:
205
+ // return [
206
+ // fixer.removeRange([codeStart, codeEnd]),
207
+ // fixer.insertTextAfter(insertTarget, propertyCode)
208
+ // ]
209
+ const insertStart = insertTarget . range [ 1 ]
210
+ const newCode = propertyCode + sourceCode . text . slice ( insertStart , codeStart )
211
+ return fixer . replaceTextRange ( [ insertStart , codeEnd ] , newCode )
112
212
}
113
213
} )
114
214
}
0 commit comments