Skip to content

Allow to use @vue/component to set enable parsing vue objects #123

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 8, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Create `.eslintrc.*` file to configure rules. See also: [http://eslint.org/docs/

Example **.eslintrc.js**:

```javascript
```js
module.exports = {
extends: [
'eslint:recommended',
Expand All @@ -44,7 +44,35 @@ module.exports = {
}
```

## ⚙ Configs
### Attention

All component-related rules are being applied to code that passes any of the following checks:

* `Vue.component()` expression
* `export default {}` in `.vue` or `.jsx` file

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.:

```js
// @vue/component
const CustomComponent = {
name: 'custom-component',
template: '<div></div>'
}
```
```js
Vue.component('AsyncComponent', (resolve, reject) => {
setTimeout(() => {
// @vue/component
resolve({
name: 'async-component',
template: '<div></div>'
})
}, 500)
})
```

## :gear: Configs

This plugin provides two predefined configs:
- `plugin:vue/base` - contains necessary settings for this plugin to work properly
Expand Down
17 changes: 15 additions & 2 deletions lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -431,17 +431,30 @@ module.exports = {
*/
executeOnVueComponent (context, cb) {
const filePath = context.getFilename()
const sourceCode = context.getSourceCode()
const _this = this
const componentComments = sourceCode.getAllComments().filter(comment => /@vue\/component/g.test(comment.value))
const foundNodes = []

const isDuplicateNode = (node) => {
if (foundNodes.some(el => el.loc.start.line === node.loc.start.line)) return true
foundNodes.push(node)
return false
}

return {
ObjectExpression (node) {
if (!componentComments.some(el => el.loc.end.line === node.loc.start.line - 1) || isDuplicateNode(node)) return
cb(node)
},
'ExportDefaultDeclaration:exit' (node) {
// export default {} in .vue || .jsx
if (!_this.isVueComponentFile(node, filePath)) return
if (!_this.isVueComponentFile(node, filePath) || isDuplicateNode(node.declaration)) return
cb(node.declaration)
},
'CallExpression:exit' (node) {
// Vue.component('xxx', {}) || component('xxx', {})
if (!_this.isVueComponent(node)) return
if (!_this.isVueComponent(node) || isDuplicateNode(node.arguments.slice(-1)[0])) return
cb(node.arguments.slice(-1)[0])
}
}
Expand Down
277 changes: 277 additions & 0 deletions tests/lib/utils/vue-component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
/**
* @author Armano
*/
'use strict'

const utils = require('../../../lib/utils/index')

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

const rule = {
create (context) {
return utils.executeOnVueComponent(context, obj => {
context.report({
node: obj,
message: 'Component detected.'
})
})
},
meta: {
fixable: null,
schema: []
}
}

const RuleTester = require('eslint').RuleTester
const parserOptions = {
ecmaVersion: 6,
sourceType: 'module'
}

function makeError (line) {
return {
message: 'Component detected.',
line,
type: 'ObjectExpression'
}
}

function validTests (ext) {
return [
{
filename: `test.${ext}`,
code: `export const foo = {}`,
parserOptions
},
{
filename: `test.${ext}`,
code: `export var foo = {}`,
parserOptions
},
{
filename: `test.${ext}`,
code: `const foo = {}`,
parserOptions
},
{
filename: `test.${ext}`,
code: `var foo = {}`,
parserOptions
},
{
filename: `test.${ext}`,
code: `let foo = {}`,
parserOptions
},
{
filename: `test.${ext}`,
code: `foo({ })`,
parserOptions
},
{
filename: `test.${ext}`,
code: `foo(() => { return {} })`,
parserOptions
},
{
filename: `test.${ext}`,
code: `Vue.component('async-example', function (resolve, reject) { })`,
parserOptions
},
{
filename: `test.${ext}`,
code: `Vue.component('async-example', function (resolve, reject) { resolve({}) })`,
parserOptions
},
{
filename: `test.${ext}`,
code: `new Vue({ })`,
parserOptions
},
{
filename: `test.${ext}`,
code: `{
foo: {}
}`,
parserOptions
}
]
}

function invalidTests (ext) {
return [
{
filename: `test.${ext}`,
code: `
Vue.component('async-example', function (resolve, reject) {
// @vue/component
resolve({})
})
// ${ext}
`,
parserOptions,
errors: [makeError(4)]
},
{
filename: `test.${ext}`,
code: `Vue.component({})`,
parserOptions,
errors: [makeError(1)]
},
{
filename: `test.${ext}`,
code: `
// @vue/component
export default { }
// ${ext}
`,
parserOptions,
errors: [makeError(3)]
},
{
filename: `test.${ext}`,
code: `
/* @vue/component */
export default { }
// ${ext}
`,
parserOptions,
errors: [makeError(3)]
},
{
filename: `test.${ext}`,
code: `
/*
* ext: ${ext}
* @vue/component
*/
export default { }
// ${ext}
`,
parserOptions,
errors: [makeError(6)]
},
{
filename: `test.${ext}`,
code: `
// @vue/component
export default { }
// @vue/component
export var a = { }
// ${ext}
`,
parserOptions,
errors: [makeError(3), makeError(5)]
},
{
filename: `test.${ext}`,
code: `
/* @vue/component */
export const foo = { }
/* @vue/component */
export default { }
// ${ext}
`,
parserOptions,
errors: [makeError(3), makeError(5)]
},
{
filename: `test.${ext}`,
code: `
export default { }
// @vue/component
export let foo = { }
// ${ext}
`,
parserOptions,
errors: (ext === 'js' ? [] : [makeError(2)]).concat([makeError(4)])
},
{
filename: `test.${ext}`,
code: `
let foo = { }
// @vue/component
export let bar = { }
// ${ext}
`,
parserOptions,
errors: [makeError(4)]
},
{
filename: `test.${ext}`,
code: `
export var dar = { }
// @vue/component
foo({ })
bar({ })
// ${ext}
`,
parserOptions,
errors: [makeError(4)]
},
{
filename: `test.${ext}`,
code: `
foo({ })
export default {
test: {},
// @vue/component
foo: { }
}
bar({ })
// ${ext}
`,
parserOptions,
errors: (ext === 'js' ? [] : [makeError(3)]).concat([makeError(6)])
},
{
filename: `test.${ext}`,
code: `
export default {
bar () {
return {}
},
foo () {
// @vue/component
return {}
}
}
// ${ext}
`,
parserOptions,
errors: (ext === 'js' ? [] : [makeError(2)]).concat([makeError(8)])
}
]
}

// ------------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------------

const ruleTester = new RuleTester()
ruleTester.run('vue-component', rule, {

valid: [
{
filename: 'test.js',
code: `export default { }`,
parserOptions
}
].concat(validTests('js')).concat(validTests('jsx')).concat(validTests('vue')),
invalid: [
{
filename: 'test.vue',
code: `export default { }`,
parserOptions,
errors: [makeError(1)]
},
{
filename: 'test.jsx',
code: `export default { }`,
parserOptions,
errors: [makeError(1)]
}
].concat(invalidTests('js')).concat(invalidTests('jsx')).concat(invalidTests('vue'))
})