Skip to content

Commit 6906fb1

Browse files
committed
Fix: make mustaches allowing empty (refs vuejs/eslint-plugin-vue#398)
1 parent 7ded104 commit 6906fb1

File tree

5 files changed

+45
-35
lines changed

5 files changed

+45
-35
lines changed

Diff for: docs/ast.md

+1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ interface VSlotScopeExpression <: Expression {
113113

114114
- This is mustaches or directive values.
115115
- If syntax errors exist, `VExpressionContainer#expression` is `null`.
116+
- If it's an empty mustache, `VExpressionContainer#expression` is `null`. (e.g., `{{ /* a comment */ }}`)
116117
- `Reference` is objects but not `Node`. Those are external references which are in the expression.
117118
- `Reference#variable` is the variable which is defined by a `VElement`. If a reference uses a global variable or a member of VM, this is `null`.
118119
- `VForExpression` is an expression node like [ForInStatement] but it has an array as `left` property and does not have `body` property. This is the value of [`v-for` directives].

Diff for: src/script/index.ts

+42-9
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
traverseNodes,
1010
ESLintArrayPattern,
1111
ESLintBlockStatement,
12+
ESLintCallExpression,
1213
ESLintExpression,
1314
ESLintExpressionStatement,
1415
ESLintExtendedProgram,
@@ -17,6 +18,7 @@ import {
1718
ESLintFunctionExpression,
1819
ESLintPattern,
1920
ESLintProgram,
21+
ESLintSpreadElement,
2022
ESLintVariableDeclaration,
2123
ESLintUnaryExpression,
2224
Node,
@@ -169,6 +171,27 @@ function throwEmptyError(
169171
throw err
170172
}
171173

174+
/**
175+
* Throw syntax error for empty.
176+
* @param locationCalculator The location calculator to get line/column.
177+
*/
178+
function throwUnexpectedSpreadElementError(
179+
locationCalculator: LocationCalculator,
180+
node: ESLintSpreadElement,
181+
): never {
182+
const loc = locationCalculator.getLocation(node.start || 0)
183+
const err = new ParseError(
184+
"Unexpected spread element.",
185+
undefined,
186+
0,
187+
loc.line,
188+
loc.column,
189+
)
190+
locationCalculator.fixErrorLocation(err)
191+
192+
throw err
193+
}
194+
172195
/**
173196
* Throw syntax error of outside of code.
174197
* @param locationCalculator The location calculator to get line/column.
@@ -320,26 +343,36 @@ export function parseExpression(
320343
code: string,
321344
locationCalculator: LocationCalculator,
322345
parserOptions: any,
346+
allowEmpty = false,
323347
): ExpressionParseResult {
324-
debug('[script] parse expression: "(%s)"', code)
325-
326-
if (code.trim() === "") {
327-
return throwEmptyError(locationCalculator, "an expression")
328-
}
348+
debug('[script] parse expression: "0(%s)"', code)
329349

330350
try {
331351
const ast = parseScriptFragment(
332-
`(${code})`,
333-
locationCalculator.getSubCalculatorAfter(-1),
352+
`0(${code})`,
353+
locationCalculator.getSubCalculatorAfter(-2),
334354
parserOptions,
335355
).ast
336-
const references = analyzeExternalReferences(ast, parserOptions)
337-
const expression = (ast.body[0] as ESLintExpressionStatement).expression
338356
const tokens = ast.tokens || []
339357
const comments = ast.comments || []
358+
const references = analyzeExternalReferences(ast, parserOptions)
359+
const statement = ast.body[0] as ESLintExpressionStatement
360+
const callExpression = statement.expression as ESLintCallExpression
361+
const expression = callExpression.arguments[0]
362+
363+
if (!allowEmpty && !expression) {
364+
return throwEmptyError(locationCalculator, "an expression")
365+
}
366+
if (expression && expression.type === "SpreadElement") {
367+
return throwUnexpectedSpreadElementError(
368+
locationCalculator.getSubCalculatorAfter(-2),
369+
expression,
370+
)
371+
}
340372

341373
// Remvoe parens.
342374
tokens.shift()
375+
tokens.shift()
343376
tokens.pop()
344377

345378
return { expression, tokens, comments, references, variables: [] }

Diff for: src/template/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -466,9 +466,10 @@ export function processMustache(
466466
mustache.value,
467467
locationCalculator,
468468
parserOptions,
469+
true,
469470
)
470471

471-
node.expression = ret.expression
472+
node.expression = ret.expression || null
472473
node.references = ret.references
473474
if (ret.expression != null) {
474475
ret.expression.parent = node

Diff for: test/fixtures/ast/error-message-empty/ast.json

-24
Original file line numberDiff line numberDiff line change
@@ -1100,24 +1100,6 @@
11001100
},
11011101
"value": "{{"
11021102
},
1103-
{
1104-
"type": "HTMLWhitespace",
1105-
"range": [
1106-
26,
1107-
27
1108-
],
1109-
"loc": {
1110-
"start": {
1111-
"line": 3,
1112-
"column": 6
1113-
},
1114-
"end": {
1115-
"line": 3,
1116-
"column": 7
1117-
}
1118-
},
1119-
"value": " "
1120-
},
11211103
{
11221104
"type": "VExpressionEnd",
11231105
"range": [
@@ -2129,12 +2111,6 @@
21292111
],
21302112
"comments": [],
21312113
"errors": [
2132-
{
2133-
"message": "Expected to be an expression, but got empty.",
2134-
"index": 26,
2135-
"lineNumber": 3,
2136-
"column": 6
2137-
},
21382114
{
21392115
"message": "Expected to be an expression, but got empty.",
21402116
"index": 65,

Diff for: test/fixtures/ast/error-message-empty/token-ranges.json

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
"}}",
77
"\n ",
88
"{{",
9-
" ",
109
"}}",
1110
"\n ",
1211
"<div",

0 commit comments

Comments
 (0)