Skip to content

Commit

Permalink
Make the indent rules supports ECMAScript 2020
Browse files Browse the repository at this point in the history
  • Loading branch information
ota-meshi committed May 31, 2020
1 parent 67b3e79 commit 6cfd8b2
Show file tree
Hide file tree
Showing 26 changed files with 341 additions and 25 deletions.
2 changes: 1 addition & 1 deletion docs/rules/html-indent.md
Expand Up @@ -15,7 +15,7 @@ description: enforce consistent indentation in `<template>`
This rule enforces a consistent indentation style in `<template>`. The default style is 2 spaces.

- This rule checks all tags, also all expressions in directives and mustaches.
- In the expressions, this rule supports ECMAScript 2017 syntaxes. It ignores unknown AST nodes, but it might be confused by non-standard syntaxes.
- In the expressions, this rule supports ECMAScript 2020 syntaxes. It ignores unknown AST nodes, but it might be confused by non-standard syntaxes.

<eslint-code-block fix :rules="{'vue/html-indent': ['error']}">

Expand Down
88 changes: 68 additions & 20 deletions lib/utils/indent-common.js
Expand Up @@ -50,6 +50,7 @@ const KNOWN_NODES = new Set([
'IfStatement',
'ImportDeclaration',
'ImportDefaultSpecifier',
'ImportExpression',
'ImportNamespaceSpecifier',
'ImportSpecifier',
'LabeledStatement',
Expand All @@ -61,6 +62,8 @@ const KNOWN_NODES = new Set([
'NewExpression',
'ObjectExpression',
'ObjectPattern',
'OptionalCallExpression',
'OptionalMemberExpression',
'Program',
'Property',
'RestElement',
Expand Down Expand Up @@ -99,6 +102,7 @@ const KNOWN_NODES = new Set([
'VStartTag',
'VText'
])

const LT_CHAR = /[\r\n\u2028\u2029]/
const LINES = /[^\r\n\u2028\u2029]+(?:$|\r\n|[\r\n\u2028\u2029])/g
const BLOCK_COMMENT_PREFIX = /^\s*\*/
Expand Down Expand Up @@ -290,6 +294,14 @@ function isSemicolon(token) {
function isComma(token) {
return token != null && token.type === 'Punctuator' && token.value === ','
}
/**
* Check whether the given token is a wildcard.
* @param {Token} token The token to check.
* @returns {boolean} `true` if the token is a wildcard.
*/
function isWildcard(token) {
return token != null && token.type === 'Punctuator' && token.value === '*'
}

/**
* Check whether the given token is a whitespace.
Expand Down Expand Up @@ -704,7 +716,11 @@ module.exports.defineVisitor = function create(
}
return true
}
if (t === 'CallExpression' || t === 'NewExpression') {
if (
t === 'CallExpression' ||
t === 'NewExpression' ||
t === 'OptionalCallExpression'
) {
const openParen = tokenStore.getTokenAfter(
parent.callee,
isNotRightParen
Expand Down Expand Up @@ -1290,7 +1306,7 @@ module.exports.defineVisitor = function create(
}
},

CallExpression(node) {
'CallExpression, OptionalCallExpression'(node) {
const firstToken = tokenStore.getFirstToken(node)
const rightToken = tokenStore.getLastToken(node)
const leftToken = tokenStore.getTokenAfter(node.callee, isLeftParen)
Expand All @@ -1299,6 +1315,15 @@ module.exports.defineVisitor = function create(
processNodeList(node.arguments, leftToken, rightToken, 1)
},

ImportExpression(node) {
const firstToken = tokenStore.getFirstToken(node)
const rightToken = tokenStore.getLastToken(node)
const leftToken = tokenStore.getTokenAfter(firstToken, isLeftParen)

setOffset(leftToken, 1, firstToken)
processNodeList([node.source], leftToken, rightToken, 1)
},

CatchClause(node) {
const firstToken = tokenStore.getFirstToken(node)
const bodyToken = tokenStore.getFirstToken(node.body)
Expand Down Expand Up @@ -1379,7 +1404,20 @@ module.exports.defineVisitor = function create(
if (isSemicolon(last(tokens))) {
tokens.pop()
}
setOffset(tokens, 1, firstToken)
if (!node.exported) {
setOffset(tokens, 1, firstToken)
} else {
// export * as foo from "mod"
const starToken = tokens.find(isWildcard)
const asToken = tokenStore.getTokenAfter(starToken)
const exportedToken = tokenStore.getTokenAfter(asToken)
const afterTokens = tokens.slice(tokens.indexOf(exportedToken) + 1)

setOffset(starToken, 1, firstToken)
setOffset(asToken, 1, starToken)
setOffset(exportedToken, 1, starToken)
setOffset(afterTokens, 1, firstToken)
}
},

ExportDefaultDeclaration(node) {
Expand All @@ -1397,20 +1435,25 @@ module.exports.defineVisitor = function create(
const declarationToken = tokenStore.getFirstToken(node, 1)
setOffset(declarationToken, 1, exportToken)
} else {
// export {foo, bar}; or export {foo, bar} from "mod";
const leftParenToken = tokenStore.getFirstToken(node, 1)
const rightParenToken = tokenStore.getLastToken(node, isRightBrace)
setOffset(leftParenToken, 0, exportToken)
processNodeList(node.specifiers, leftParenToken, rightParenToken, 1)

const maybeFromToken = tokenStore.getTokenAfter(rightParenToken)
if (
maybeFromToken != null &&
sourceCode.getText(maybeFromToken) === 'from'
) {
const fromToken = maybeFromToken
const nameToken = tokenStore.getTokenAfter(fromToken)
setOffset([fromToken, nameToken], 1, exportToken)
const firstSpecifier = node.specifiers[0]
if (!firstSpecifier || firstSpecifier.type === 'ExportSpecifier') {
// export {foo, bar}; or export {foo, bar} from "mod";
const leftParenToken = tokenStore.getFirstToken(node, 1)
const rightParenToken = tokenStore.getLastToken(node, isRightBrace)
setOffset(leftParenToken, 0, exportToken)
processNodeList(node.specifiers, leftParenToken, rightParenToken, 1)

const maybeFromToken = tokenStore.getTokenAfter(rightParenToken)
if (
maybeFromToken != null &&
sourceCode.getText(maybeFromToken) === 'from'
) {
const fromToken = maybeFromToken
const nameToken = tokenStore.getTokenAfter(fromToken)
setOffset([fromToken, nameToken], 1, exportToken)
}
} else {
// maybe babel-eslint
}
}
},
Expand All @@ -1423,7 +1466,9 @@ module.exports.defineVisitor = function create(

'ForInStatement, ForOfStatement'(node) {
const forToken = tokenStore.getFirstToken(node)
const leftParenToken = tokenStore.getTokenAfter(forToken)
const awaitToken =
(node.await && tokenStore.getTokenAfter(forToken)) || null
const leftParenToken = tokenStore.getTokenAfter(awaitToken || forToken)
const leftToken = tokenStore.getTokenAfter(leftParenToken)
const inToken = tokenStore.getTokenAfter(leftToken, isNotRightParen)
const rightToken = tokenStore.getTokenAfter(inToken)
Expand All @@ -1432,6 +1477,9 @@ module.exports.defineVisitor = function create(
isNotLeftParen
)

if (awaitToken != null) {
setOffset(awaitToken, 0, forToken)
}
setOffset(leftParenToken, 1, forToken)
setOffset(leftToken, 1, leftParenToken)
setOffset(inToken, 1, leftToken)
Expand Down Expand Up @@ -1623,7 +1671,7 @@ module.exports.defineVisitor = function create(
}
},

ImportNamespaceSpecifier(node) {
'ImportNamespaceSpecifier, ExportNamespaceSpecifier'(node) {
const tokens = tokenStore.getTokens(node)
const firstToken = tokens.shift()
setOffset(tokens, 1, firstToken)
Expand All @@ -1637,7 +1685,7 @@ module.exports.defineVisitor = function create(
setOffset([colonToken, bodyToken], 1, labelToken)
},

'MemberExpression, MetaProperty'(node) {
'MemberExpression, MetaProperty, OptionalMemberExpression'(node) {
const objectToken = tokenStore.getFirstToken(node)
if (node.computed) {
const leftBracketToken = tokenStore.getTokenBefore(
Expand Down
4 changes: 2 additions & 2 deletions package.json
Expand Up @@ -56,7 +56,7 @@
},
"devDependencies": {
"@types/node": "^13.13.5",
"@typescript-eslint/parser": "^2.31.0",
"@typescript-eslint/parser": "^3.0.2",
"@vuepress/plugin-pwa": "^1.4.1",
"babel-eslint": "^10.1.0",
"chai": "^4.2.0",
Expand All @@ -72,7 +72,7 @@
"mocha": "^7.1.2",
"nyc": "^15.0.1",
"prettier": "^2.0.5",
"typescript": "^3.8.3",
"typescript": "^3.9.3",
"vue-eslint-editor": "^1.1.0",
"vuepress": "^1.4.1"
},
Expand Down
7 changes: 7 additions & 0 deletions tests/fixtures/html-indent/nullish-coalescing-operator-01.vue
@@ -0,0 +1,7 @@
<!--{}-->
<template>
<div v-bind:a="a
??
b
"/>
</template>
11 changes: 11 additions & 0 deletions tests/fixtures/html-indent/optional-chaining-01.vue
@@ -0,0 +1,11 @@
<!--{"parserOptions": {"parser":"@typescript-eslint/parser"}}-->
<!-- TODO If ESLint supports the new syntax by default, remove the parser specification and test. -->

<template>
<div v-bind:a="
obj
?.aaa
?.bbb
?.ccc
"/>
</template>
10 changes: 10 additions & 0 deletions tests/fixtures/html-indent/optional-chaining-02.vue
@@ -0,0 +1,10 @@
<!--{"parserOptions": {"parser":"@typescript-eslint/parser"}}-->
<!-- TODO If ESLint supports the new syntax by default, remove the parser specification and test. -->
<template>
<div v-bind:a="
obj?.
aaa?.
bbb?.
ccc
"/>
</template>
10 changes: 10 additions & 0 deletions tests/fixtures/html-indent/optional-chaining-03.vue
@@ -0,0 +1,10 @@
<!--{"parserOptions": {"parser":"@typescript-eslint/parser"}}-->
<!-- TODO If ESLint supports the new syntax by default, remove the parser specification and test. -->
<template>
<div v-bind:a="
obj?.
[aaa]?.
[bbb]
?.[ccc]
"/>
</template>
16 changes: 16 additions & 0 deletions tests/fixtures/html-indent/optional-chaining-04.vue
@@ -0,0 +1,16 @@
<!--{"parserOptions": {"parser":"@typescript-eslint/parser"}}-->
<!-- TODO If ESLint supports the new syntax by default, remove the parser specification and test. -->
<template>
<div v-bind:a="
obj?.
(
aaa
)?.
(
bbb
)
?.(
ccc
)
"/>
</template>
7 changes: 7 additions & 0 deletions tests/fixtures/script-indent/bigint-01.vue
@@ -0,0 +1,7 @@
<!--{}-->
<script>
var a =
10n
+
5n
</script>
16 changes: 16 additions & 0 deletions tests/fixtures/script-indent/export-all-declaration-02.vue
@@ -0,0 +1,16 @@
<!--{}-->
<script>
export
*
as
foo
from
"mod"
;
export
*
as
bar
from
"mod"
</script>
15 changes: 15 additions & 0 deletions tests/fixtures/script-indent/for-await-of-01.vue
@@ -0,0 +1,15 @@
<!--{}-->
<script>
async function fn() {
for
await
(
a
of
b
)
{
;
}
}
</script>
7 changes: 7 additions & 0 deletions tests/fixtures/script-indent/import-expression-01.vue
@@ -0,0 +1,7 @@
<!--{}-->
<script>
const fs = import
(
'fs'
)
</script>
8 changes: 8 additions & 0 deletions tests/fixtures/script-indent/import-expression-02.vue
@@ -0,0 +1,8 @@
<!--{}-->
<script>
const module = import
(
m.
n
)
</script>
11 changes: 11 additions & 0 deletions tests/fixtures/script-indent/import-expression-03.vue
@@ -0,0 +1,11 @@
<!--{}-->
<script>
async function fn() {
return await
import
(
m.
n
)
}
</script>
@@ -0,0 +1,6 @@
<!--{}-->
<script>
var v = a
??
b
</script>
12 changes: 12 additions & 0 deletions tests/fixtures/script-indent/object-expression-04.vue
@@ -0,0 +1,12 @@
<!--{}-->
<script>
var obj = {
a:
1,
...obj1,
b:
2,
...
obj2,
}
</script>
8 changes: 8 additions & 0 deletions tests/fixtures/script-indent/optional-chaining-01.vue
@@ -0,0 +1,8 @@
<!--{"parserOptions": {"parser":"@typescript-eslint/parser"}}-->
<!-- TODO If ESLint supports the new syntax by default, remove the parser specification and test. -->
<script>
obj
?.aaa
?.bbb
?.ccc
</script>
8 changes: 8 additions & 0 deletions tests/fixtures/script-indent/optional-chaining-02.vue
@@ -0,0 +1,8 @@
<!--{"parserOptions": {"parser":"@typescript-eslint/parser"}}-->
<!-- TODO If ESLint supports the new syntax by default, remove the parser specification and test. -->
<script>
obj?.
aaa?.
bbb?.
ccc
</script>
8 changes: 8 additions & 0 deletions tests/fixtures/script-indent/optional-chaining-03.vue
@@ -0,0 +1,8 @@
<!--{"parserOptions": {"parser":"@typescript-eslint/parser"}}-->
<!-- TODO If ESLint supports the new syntax by default, remove the parser specification and test. -->
<script>
obj?.
[aaa]?.
[bbb]
?.[ccc]
</script>
14 changes: 14 additions & 0 deletions tests/fixtures/script-indent/optional-chaining-04.vue
@@ -0,0 +1,14 @@
<!--{"parserOptions": {"parser":"@typescript-eslint/parser"}}-->
<!-- TODO If ESLint supports the new syntax by default, remove the parser specification and test. -->
<script>
obj?.
(
aaa
)?.
(
bbb
)
?.(
ccc
)
</script>

0 comments on commit 6cfd8b2

Please sign in to comment.