Tell us about your environment
4.16.0
4.2.2
v8.1.3
Please show your full configuration:
const isDev = process.env.NODE_ENV === 'development';
// http://eslint.org/docs/user-guide/configuring
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint',
sourceType: 'module'
},
env: {
browser: true,
},
extends: [
'eslint:recommended',
'plugin:vue/recommended', // or 'plugin:vue/base'
'airbnb-base',
],
// required to lint *.vue files
plugins: [
'vue',
],
// check if imports actually resolve
'settings': {
'import/resolver': {
'webpack': {
'config': 'build/webpack.base.conf.js'
}
},
},
// add your custom rules here
'rules': {
// eslint rules
'camelcase': ['error', {
'properties': 'always'
}],
'function-paren-newline': ['error', 'consistent'],
'id-match': ['error', '^(\\${0,1}[a-z]+[a-zA-Z_]*||[A-Z_0-9]+||[pk][12])$', {
'onlyDeclarations': true,
}],
'max-len': ['error', {
'code': 140,
'ignoreTrailingComments': true,
'ignoreStrings': true,
'ignoreTemplateLiterals': true,
'ignoreUrls': true,
'ignoreComments': true
}],
'no-underscore-dangle': 0,
'no-return-assign': 0,
'object-curly-newline': ['error', {
'consistent': true
}],
'one-var': ['error', {
'initialized': 'never',
}],
'one-var-declaration-per-line': ['error', 'initializations'],
'prefer-destructuring': 0,
// allow debugger during development
'no-debugger': isDev ? 0 : 2,
'no-console': isDev ? 0 : 1,
'no-unused-vars': isDev ? 0 : 1,
// don't require .vue extension when importing
'import/extensions': ['error', 'always', {
'js': 'never',
'vue': 'never'
}],
// allow optionalDependencies
'import/no-extraneous-dependencies': ['error', {
'optionalDependencies': ['test/unit/index.js']
}],
// allow single export
'import/prefer-default-export': 'off',
// vue lint configs
'vue/attribute-hyphenation': ['error', 'always'],
'vue/html-end-tags': 'error',
'vue/html-indent': ['error', 2, {
'attribute': 1,
'closeBracket': 0,
'ignores': []
}],
'vue/html-quotes': ['error', 'double'],
'vue/html-self-closing': ['error', {
'html': {
'normal': 'never',
'void': 'never',
'component': 'never'
},
'svg': 'always',
'math': 'always',
}],
'vue/max-attributes-per-line': [2, {
'singleline': 10,
'multiline': {
'max': 2,
'allowFirstLine': false
},
}],
'vue/mustache-interpolation-spacing': ['error', 'always'],
'vue/name-property-casing': ['error', 'kebab-case'],
'vue/no-async-in-computed-properties': 'error',
'vue/no-confusing-v-for-v-if': 'error',
'vue/no-dupe-keys': 'error',
'vue/no-duplicate-attributes': ['error', {
allowCoexistClass: true,
allowCoexistStyle: true,
}],
'vue/no-multi-spaces': 'error',
'vue/no-parsing-error': 'error',
'vue/no-reserved-keys': ['error', {
'reserved': ['$el', '$nextTick', '$route', '$router', 'asyncData'],
'groups': [],
}],
'vue/no-shared-component-data': 'error',
'vue/no-side-effects-in-computed-properties': 'error',
'vue/no-template-key': 'error',
'vue/no-textarea-mustache': 'error',
// 'vue/order-in-components': ['error', {
// 'order': [
// ['name', 'delimiters', 'functional', 'model'],
// ['components', 'directives', 'filters'],
// ['parent', 'mixins', 'extends', 'provide', 'inject'],
// 'el',
// 'template',
// 'props',
// 'propsData',
// 'data',
// 'computed',
// 'watch',
// 'asyncData',
// 'onWechatReady',
// 'LIFECYCLE_HOOKS',
// 'methods',
// 'render',
// 'renderError'
// ],
// }],
'vue/require-component-is': 'error',
'vue/require-default-prop': 'error',
'vue/require-prop-types': 'error',
'vue/require-render-return': 'error',
'vue/require-v-for-key': 'error',
'vue/require-valid-default-prop': 'error',
'vue/return-in-computed-property': 'error',
'vue/this-in-template': ['error', 'never'],
'vue/v-bind-style': ['error', 'shorthand'],
'vue/v-on-style': ['error', 'shorthand'],
'vue/valid-template-root': 'error',
'vue/valid-v-bind': 'error',
'vue/valid-v-cloak': 'error',
'vue/valid-v-else-if': 'error',
'vue/valid-v-else': 'error',
'vue/valid-v-for': 'error',
'vue/valid-v-html': 'error',
'vue/valid-v-if': 'error',
'vue/valid-v-model': 'error',
'vue/valid-v-on': 'error',
'vue/valid-v-once': 'error',
'vue/valid-v-pre': 'error',
'vue/valid-v-show': 'error',
'vue/valid-v-text': 'error',
}
}
What did you do? Please include the actual source code causing the issue.
<div class="pull-down-touching-tip__text">
{{ scope.distance < fireDistance ? '缁х画涓嬫媺鍒锋柊' : '鏉炬墜鍒锋柊' }}
</div>
What did you expect to happen?
No error.
What actually happened? Please include the actual, raw output from ESLint.

Thank you for the report.
This is a correct error as according to HTML spec:
Anything else step.To make valid HTML, you should use < instead of the <.
Or you can ignore the error by the option of vue/no-parsing-error rule: https://github.com/vuejs/eslint-plugin-vue/blob/master/docs/rules/no-parsing-error.md#wrench-options
No this is NOT html, it's in {{}}, which means it is a inline JavaScript expression.
It's still in HTML.
Thanks for reply. So what should I do to compare two numbers and use conditional operator correctly in vue template?
The expression {{ scope.distance < fireDistance ? 'Pull to refresh' : 'Release to refresh' }} in template, will be rendered into Pull to refresh or Release to refresh due to scope.distance and fireDistance. There is no spec in the result. I can't understand why lint report an error here. 馃槙
To be valid HTML, {{ scope.distance < fireDistance ? 'Pull to refresh' : 'Release to refresh' }}.
But HTML spec allows {{ scope.distance < fireDistance ? 'Pull to refresh' : 'Release to refresh' }} as well due to well-defined error recovery logic. It's the reason why vue/no-parsing-error rule has the options to ignore HTML parse errors.
Actually, {{ scope.distance < fireDistance ? 'Pull to refresh' : 'Release to refresh' }} can not pass the compiler.

It is really a great feature to detect specs in real HTML, and I need this very much. Some members in my team are always careless about use <. They think 'OH IT WORKS!', which makes me feel very annoying. That's why I do not want to disable it globally.
Oh? {{ scope.distance < fireDistance ? 'Pull to refresh' : 'Release to refresh' }} works fine in my environment.
Full file index.vue:
<template>
<div>A is {{ a > b ? 'larger than' : 'smaller or equals with' }} B</div>
</template>
<script>
export default {
data() {
return {
a: Math.random(),
b: Math.random(),
};
},
};
</script>
You can try this code.

Here's the expected result.
I know that HTML syntax is very tolerant. Browsers/Parsers work fine even if HTML parse error. But HTML parse errors still exist, so the rule reports it and has the options which are to ignore HTML parse errors.
OK, I opened an issue to add option which ignores HTML syntax errors in mustaches in vue-eslint-parser repo.
I re-thought about this, I decided that I don't add that option.
It messes the code easily by removing a space. Inside of mustaches is still HTML.
<!-- Oops, this is not a mustache, there is `<fireDistance>` tag. -->
{{ scope.distance <fireDistance ? 'Pull to refresh' : 'Release to refresh' }}
I'd like to recommend to use < (note it requires semicolon) for <.
Thank you.
Thanks, I'll use < inside the mustache.
It's still in HTML.
By that logic, anything inside of a <script> tag is HTML.
Interpolated JS should absolutely not be treated as HTML. Being able to use > but having to use < instead of < is absolutely ridiculous, I can't believe that's an acceptable solution.
By that logic, anything inside of a
So wouldn't it follow that {{ ends at the first }}?
@Nfinished https://jsfiddle.net/67vmen98/
Not quite what I'm talking about.
https://jsfiddle.net/krvftv74/2/
It's kind of funny that my example is throwing HTML linter errors, but my point is the Vue linter should know better.
@mysticatea Please disable as default, its really annoying :(
For reference, add this to your eslintrc.js (eslint config) file to remove notice:
rules: {
'vue/no-parsing-error': [2, {
"invalid-first-character-of-tag-name": false
}]
}
Same problem, use >instead of <
<a v-for="item in items" :key="item._id">
{{ (item.name.length > 6) ? item.name.slice(0,6) + '...' : item.name }}
</a>

This is still a thing, and it is absurd.
The contents of {{ }} is _executed as a JavaScript expression_. It isn't HTML by any stretch of the imagination. Having to escape html reserved characters is very unnatural in what is essentially a script.
Let JavaScript expressions be expressions. Fix this bug.
I completely agree with @Smilebags - Vue template is something more than a pure HTML and the linter should obey this fact. Everything inside a mustache and the mustache itself is and should be practically and theoretically invisible for the HTML parser.
I think we can all agree there are no HTML tags in {{ a < b ? 1 : 2 }}.
So it follows that seeing an "invalid-first-character-of-__tag__-name" is a mistake.
Either the warning is valid or it isn't. And seeing how there aren't any tags in that statement, the warning is not valid.
I think we all understand the situation that leads to the warning being generated today, but the _why_ doesn't really change the _should_.
Is the rule invalid-first-character-of-tag-name useful ? Perhaps it can be removed if it's too challenging to fix it.
Quick hotfix to this
with error:

no error:

the rule invalid-first-character-of-tag-name should not be parsed inside mustache tags, inside it is javascript not html, dont matter what is your justification for this, its a compiler warning mustache tags are not suposed to be rendered in browser, so it doesnt make sense
when can fix it ?
LOL, still a problem
still a problem
Can we please get someone to re-evaluate this ticket? Based on the responses from @mysticatea it's quite evident that they either have a fundamental misunderstanding of how string interpolation works in Vue, or the purpose behind string interpolation in Vue.
Indeed, the problem is in vue-eslint-parser and not in eslint-plugin-vue. Here is the patch to fix the problem
vue-eslint-parser v7.0.0.patch.txt
You can use the following options:
{
// ...
"rules": {
// ...
"vue/no-parsing-error": ["error", {"invalid-first-character-of-tag-name": false}]
// ...
}
// ...
}
You can check with [DEMO].
This is broken if you are using @babel/plugin-proposal-optional-chaining too.
<span>
{{ user?.name }}
</span>
throws an error.
but this works perfectly fine
<span>
{{ user && user.name }}
</span>
or even with lodash
<span>
{{ _.get(user, 'name') }}
</span>
I think the argument that everything inside {{ }} must be valid HTML is not acceptable at this point, because whatever inside is not HTML.
@mysticatea can you reopen this issue? It's affecting other cases which will bring back the same discussion again
No. the inside of {{ }} is HTML in Vue.js 2.x. That's the reason that I rewrote HTML parser for Vue.js 3 from scratch.
This is just a fact. I don't think the reopen makes sense.
In Vue.js 3 support, this behavior will change, I think. Because the inside of {{ }} in Vue.js 3 is not HTML.
Go in packages.json and add this code
rules: {
"vue/no-parsing-error": [2, {
"invalid-first-character-of-tag-name": false
}]
}
Go in packages.json and add this code
It is not packages.json but .eslintrc.js
use v-text ( or v-html )
<!-- will pass -->
<div v-text="a < b ? 'c' : 'd'"></div>
<!-- will fail -->
md5-591535cd38b9b1946c331cc240c092ae
@bumprat Are you sure your v-text will be compiled as expression rather than plain text ?
@bumprat Are you sure your
v-textwill be compiled as expression rather than plain text ?
check v-text documentation
v-text
Updates the element鈥檚 textContent.Internally, {{ Mustache }} interpolations are also compiled as a v-text directive on a textNode.
No. the inside of {{ }} is HTML in Vue.js 2.x. That's the reason that I rewrote HTML parser for Vue.js 3 from scratch.
This is just a fact. I don't think the reopen makes sense.
In Vue.js 3 support, this behavior will change, I think. Because the inside of {{ }} in Vue.js 3 is not HTML.
@mysticatea Just run into the issue with Vue.js 3. Is it possible to reopen the issue?
same problem here.
It can be fixed by add "vue/no-parsing-error": [2, { "invalid-first-character-of-tag-name": false }]" in .eslintrc.js .
However, I hope a better way to fix it.
All other arguments about syntax aside, if JavaScript expressions (be them ternary or otherwise) are supported and encouraged by VueJS, it should probably not throw an error in a VueJS linter. alt; is not a JavaScript operator and anyone's opinion to the contrary is diverging standard.
Most helpful comment
This is still a thing, and it is absurd.
The contents of {{ }} is _executed as a JavaScript expression_. It isn't HTML by any stretch of the imagination. Having to escape html reserved characters is very unnatural in what is essentially a script.
Let JavaScript expressions be expressions. Fix this bug.