fix(ng,vue): add parens to avoid unexpected `}}` in interpolations (#5657)

master
Ika 2018-12-30 23:03:42 +08:00 committed by GitHub
parent 7c4cebeaa5
commit 3de36e3a2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 2331 additions and 13 deletions

View File

@ -29,17 +29,18 @@ FastPath.prototype.getValue = function getValue() {
};
function getNodeHelper(path, count) {
const s = path.stack;
for (let i = s.length - 1; i >= 0; i -= 2) {
const value = s[i];
const stackIndex = getNodeStackIndexHelper(path.stack, count);
return stackIndex === -1 ? null : path.stack[stackIndex];
}
function getNodeStackIndexHelper(stack, count) {
for (let i = stack.length - 1; i >= 0; i -= 2) {
const value = stack[i];
if (value && !Array.isArray(value) && --count < 0) {
return value;
return i;
}
}
return null;
return -1;
}
FastPath.prototype.getNode = function getNode(count) {
@ -70,6 +71,14 @@ FastPath.prototype.call = function call(callback /*, name1, name2, ... */) {
return result;
};
FastPath.prototype.callParent = function callParent(callback, count) {
const stackIndex = getNodeStackIndexHelper(this.stack, ~~count + 1);
const parentValues = this.stack.splice(stackIndex + 1);
const result = callback(this);
Array.prototype.push.apply(this.stack, parentValues);
return result;
};
// Similar to FastPath.prototype.call, except that the value obtained by
// accessing this.getValue()[name1][name2]... should be array-like. The
// callback will be called with a reference to this path object for each

View File

@ -85,11 +85,16 @@ function embed(path, print, textToDoc, options) {
line,
textToDoc(
node.value,
options.parser === "angular"
? { parser: "__ng_interpolation", trailingComma: "none" }
: options.parser === "vue"
? { parser: "__vue_expression" }
: { parser: "__js_expression" }
Object.assign(
{
__isInHtmlInterpolation: true // to avoid unexpected `}}`
},
options.parser === "angular"
? { parser: "__ng_interpolation", trailingComma: "none" }
: options.parser === "vue"
? { parser: "__vue_expression" }
: { parser: "__js_expression" }
)
)
])
),
@ -1043,7 +1048,10 @@ function printEmbeddedAttributeValue(node, originalTextToDoc, options) {
indent(
concat([
line,
ngTextToDoc(part, { parser: "__ng_interpolation" })
ngTextToDoc(part, {
parser: "__ng_interpolation",
__isInHtmlInterpolation: true // to avoid unexpected `}}`
})
])
),
line,

View File

@ -57,6 +57,16 @@ function needsParens(path, options) {
return false;
}
// to avoid unexpected `}}` in HTML interpolations
if (
options.__isInHtmlInterpolation &&
!options.bracketSpacing &&
endsWithRightBracket(node) &&
isFollowedByRightBracket(path)
) {
return true;
}
// Only statements don't need parentheses.
if (isStatement(node)) {
return false;
@ -688,4 +698,55 @@ function isStatement(node) {
);
}
function endsWithRightBracket(node) {
switch (node.type) {
case "ObjectExpression":
return true;
default:
return false;
}
}
function isFollowedByRightBracket(path) {
const node = path.getValue();
const parent = path.getParentNode();
const name = path.getName();
switch (parent.type) {
case "NGPipeExpression":
if (
typeof name === "number" &&
parent.arguments[name] === node &&
parent.arguments.length - 1 === name
) {
return path.callParent(isFollowedByRightBracket);
}
break;
case "ObjectProperty":
if (name === "value") {
const parentParent = path.getParentNode(1);
return (
parentParent.properties[parentParent.properties.length - 1] === parent
);
}
break;
case "BinaryExpression":
case "LogicalExpression":
if (name === "right") {
return path.callParent(isFollowedByRightBracket);
}
break;
case "ConditionalExpression":
if (name === "alternate") {
return path.callParent(isFollowedByRightBracket);
}
break;
case "UnaryExpression":
if (parent.prefix) {
return path.callParent(isFollowedByRightBracket);
}
break;
}
return false;
}
module.exports = needsParens;

File diff suppressed because it is too large Load Diff

View File

@ -54,3 +54,13 @@
{{ aNormalValue | aPipe }}:
<strong>{{ aReallyReallySuperLongValue | andASuperLongPipeJustToBreakThis }}</strong>
</span>
<p>
{{
'delete'
| translate: {what: ('entities' | translate: {count: array.length})}
}}
</p>
<p>{{ {a:1+{} } }}</p>
<p>{{ {a:a==={} } }}</p>
<p>{{ {a:!{} } }}</p>
<p>{{ {a:a?b:{} } }}</p>

View File

@ -2,3 +2,4 @@ run_spec(__dirname, ["angular"]);
run_spec(__dirname, ["angular"], { trailingComma: "es5" });
run_spec(__dirname, ["angular"], { printWidth: 1 });
run_spec(__dirname, ["angular"], { htmlWhitespaceSensitivity: "ignore" });
run_spec(__dirname, ["angular"], { bracketSpacing: false });