Skip to content

Commit 4d0c1d3

Browse files
armano2michalsnik
authored andcommitted
Allow to use @vue/component to set enable parsing vue objects (#123)
* Allow to use @vue/component to set enable parsing vue objects fixes #109 * Add support for eslint 4.x LineComment and BlockComment was removed in 4.0.0 * Remove unessesery function
1 parent 3db7b13 commit 4d0c1d3

File tree

3 files changed

+322
-4
lines changed

3 files changed

+322
-4
lines changed

README.md

+30-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Create `.eslintrc.*` file to configure rules. See also: [http://eslint.org/docs/
3131

3232
Example **.eslintrc.js**:
3333

34-
```javascript
34+
```js
3535
module.exports = {
3636
extends: [
3737
'eslint:recommended',
@@ -44,7 +44,35 @@ module.exports = {
4444
}
4545
```
4646

47-
## ⚙ Configs
47+
### Attention
48+
49+
All component-related rules are being applied to code that passes any of the following checks:
50+
51+
* `Vue.component()` expression
52+
* `export default {}` in `.vue` or `.jsx` file
53+
54+
If you however want to take advantage of our rules in any of your custom objects that are Vue components, you might need to use special comment `// @vue/component` that marks object in the next line as a Vue component in any file, e.g.:
55+
56+
```js
57+
// @vue/component
58+
const CustomComponent = {
59+
name: 'custom-component',
60+
template: '<div></div>'
61+
}
62+
```
63+
```js
64+
Vue.component('AsyncComponent', (resolve, reject) => {
65+
setTimeout(() => {
66+
// @vue/component
67+
resolve({
68+
name: 'async-component',
69+
template: '<div></div>'
70+
})
71+
}, 500)
72+
})
73+
```
74+
75+
## :gear: Configs
4876

4977
This plugin provides two predefined configs:
5078
- `plugin:vue/base` - contains necessary settings for this plugin to work properly

lib/utils/index.js

+15-2
Original file line numberDiff line numberDiff line change
@@ -431,17 +431,30 @@ module.exports = {
431431
*/
432432
executeOnVueComponent (context, cb) {
433433
const filePath = context.getFilename()
434+
const sourceCode = context.getSourceCode()
434435
const _this = this
436+
const componentComments = sourceCode.getAllComments().filter(comment => /@vue\/component/g.test(comment.value))
437+
const foundNodes = []
438+
439+
const isDuplicateNode = (node) => {
440+
if (foundNodes.some(el => el.loc.start.line === node.loc.start.line)) return true
441+
foundNodes.push(node)
442+
return false
443+
}
435444

436445
return {
446+
ObjectExpression (node) {
447+
if (!componentComments.some(el => el.loc.end.line === node.loc.start.line - 1) || isDuplicateNode(node)) return
448+
cb(node)
449+
},
437450
'ExportDefaultDeclaration:exit' (node) {
438451
// export default {} in .vue || .jsx
439-
if (!_this.isVueComponentFile(node, filePath)) return
452+
if (!_this.isVueComponentFile(node, filePath) || isDuplicateNode(node.declaration)) return
440453
cb(node.declaration)
441454
},
442455
'CallExpression:exit' (node) {
443456
// Vue.component('xxx', {}) || component('xxx', {})
444-
if (!_this.isVueComponent(node)) return
457+
if (!_this.isVueComponent(node) || isDuplicateNode(node.arguments.slice(-1)[0])) return
445458
cb(node.arguments.slice(-1)[0])
446459
}
447460
}

tests/lib/utils/vue-component.js

+277
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
/**
2+
* @author Armano
3+
*/
4+
'use strict'
5+
6+
const utils = require('../../../lib/utils/index')
7+
8+
// ------------------------------------------------------------------------------
9+
// Requirements
10+
// ------------------------------------------------------------------------------
11+
12+
const rule = {
13+
create (context) {
14+
return utils.executeOnVueComponent(context, obj => {
15+
context.report({
16+
node: obj,
17+
message: 'Component detected.'
18+
})
19+
})
20+
},
21+
meta: {
22+
fixable: null,
23+
schema: []
24+
}
25+
}
26+
27+
const RuleTester = require('eslint').RuleTester
28+
const parserOptions = {
29+
ecmaVersion: 6,
30+
sourceType: 'module'
31+
}
32+
33+
function makeError (line) {
34+
return {
35+
message: 'Component detected.',
36+
line,
37+
type: 'ObjectExpression'
38+
}
39+
}
40+
41+
function validTests (ext) {
42+
return [
43+
{
44+
filename: `test.${ext}`,
45+
code: `export const foo = {}`,
46+
parserOptions
47+
},
48+
{
49+
filename: `test.${ext}`,
50+
code: `export var foo = {}`,
51+
parserOptions
52+
},
53+
{
54+
filename: `test.${ext}`,
55+
code: `const foo = {}`,
56+
parserOptions
57+
},
58+
{
59+
filename: `test.${ext}`,
60+
code: `var foo = {}`,
61+
parserOptions
62+
},
63+
{
64+
filename: `test.${ext}`,
65+
code: `let foo = {}`,
66+
parserOptions
67+
},
68+
{
69+
filename: `test.${ext}`,
70+
code: `foo({ })`,
71+
parserOptions
72+
},
73+
{
74+
filename: `test.${ext}`,
75+
code: `foo(() => { return {} })`,
76+
parserOptions
77+
},
78+
{
79+
filename: `test.${ext}`,
80+
code: `Vue.component('async-example', function (resolve, reject) { })`,
81+
parserOptions
82+
},
83+
{
84+
filename: `test.${ext}`,
85+
code: `Vue.component('async-example', function (resolve, reject) { resolve({}) })`,
86+
parserOptions
87+
},
88+
{
89+
filename: `test.${ext}`,
90+
code: `new Vue({ })`,
91+
parserOptions
92+
},
93+
{
94+
filename: `test.${ext}`,
95+
code: `{
96+
foo: {}
97+
}`,
98+
parserOptions
99+
}
100+
]
101+
}
102+
103+
function invalidTests (ext) {
104+
return [
105+
{
106+
filename: `test.${ext}`,
107+
code: `
108+
Vue.component('async-example', function (resolve, reject) {
109+
// @vue/component
110+
resolve({})
111+
})
112+
// ${ext}
113+
`,
114+
parserOptions,
115+
errors: [makeError(4)]
116+
},
117+
{
118+
filename: `test.${ext}`,
119+
code: `Vue.component({})`,
120+
parserOptions,
121+
errors: [makeError(1)]
122+
},
123+
{
124+
filename: `test.${ext}`,
125+
code: `
126+
// @vue/component
127+
export default { }
128+
// ${ext}
129+
`,
130+
parserOptions,
131+
errors: [makeError(3)]
132+
},
133+
{
134+
filename: `test.${ext}`,
135+
code: `
136+
/* @vue/component */
137+
export default { }
138+
// ${ext}
139+
`,
140+
parserOptions,
141+
errors: [makeError(3)]
142+
},
143+
{
144+
filename: `test.${ext}`,
145+
code: `
146+
/*
147+
* ext: ${ext}
148+
* @vue/component
149+
*/
150+
export default { }
151+
// ${ext}
152+
`,
153+
parserOptions,
154+
errors: [makeError(6)]
155+
},
156+
{
157+
filename: `test.${ext}`,
158+
code: `
159+
// @vue/component
160+
export default { }
161+
// @vue/component
162+
export var a = { }
163+
// ${ext}
164+
`,
165+
parserOptions,
166+
errors: [makeError(3), makeError(5)]
167+
},
168+
{
169+
filename: `test.${ext}`,
170+
code: `
171+
/* @vue/component */
172+
export const foo = { }
173+
/* @vue/component */
174+
export default { }
175+
// ${ext}
176+
`,
177+
parserOptions,
178+
errors: [makeError(3), makeError(5)]
179+
},
180+
{
181+
filename: `test.${ext}`,
182+
code: `
183+
export default { }
184+
// @vue/component
185+
export let foo = { }
186+
// ${ext}
187+
`,
188+
parserOptions,
189+
errors: (ext === 'js' ? [] : [makeError(2)]).concat([makeError(4)])
190+
},
191+
{
192+
filename: `test.${ext}`,
193+
code: `
194+
let foo = { }
195+
// @vue/component
196+
export let bar = { }
197+
// ${ext}
198+
`,
199+
parserOptions,
200+
errors: [makeError(4)]
201+
},
202+
{
203+
filename: `test.${ext}`,
204+
code: `
205+
export var dar = { }
206+
// @vue/component
207+
foo({ })
208+
bar({ })
209+
// ${ext}
210+
`,
211+
parserOptions,
212+
errors: [makeError(4)]
213+
},
214+
{
215+
filename: `test.${ext}`,
216+
code: `
217+
foo({ })
218+
export default {
219+
test: {},
220+
// @vue/component
221+
foo: { }
222+
}
223+
bar({ })
224+
// ${ext}
225+
`,
226+
parserOptions,
227+
errors: (ext === 'js' ? [] : [makeError(3)]).concat([makeError(6)])
228+
},
229+
{
230+
filename: `test.${ext}`,
231+
code: `
232+
export default {
233+
bar () {
234+
return {}
235+
},
236+
foo () {
237+
// @vue/component
238+
return {}
239+
}
240+
}
241+
// ${ext}
242+
`,
243+
parserOptions,
244+
errors: (ext === 'js' ? [] : [makeError(2)]).concat([makeError(8)])
245+
}
246+
]
247+
}
248+
249+
// ------------------------------------------------------------------------------
250+
// Tests
251+
// ------------------------------------------------------------------------------
252+
253+
const ruleTester = new RuleTester()
254+
ruleTester.run('vue-component', rule, {
255+
256+
valid: [
257+
{
258+
filename: 'test.js',
259+
code: `export default { }`,
260+
parserOptions
261+
}
262+
].concat(validTests('js')).concat(validTests('jsx')).concat(validTests('vue')),
263+
invalid: [
264+
{
265+
filename: 'test.vue',
266+
code: `export default { }`,
267+
parserOptions,
268+
errors: [makeError(1)]
269+
},
270+
{
271+
filename: 'test.jsx',
272+
code: `export default { }`,
273+
parserOptions,
274+
errors: [makeError(1)]
275+
}
276+
].concat(invalidTests('js')).concat(invalidTests('jsx')).concat(invalidTests('vue'))
277+
})

0 commit comments

Comments
 (0)