Support formatting for Angular ICU expression (#15777)

Co-authored-by: fisker <lionkay@gmail.com>
This commit is contained in:
SUZUKI Sosukeandfisker authored and GitHub committed 2023-12-14 13:01:54 +09:00
1 parent 23b3ca2f52
commit 153ffd5104
21 files changed
+370 -9

No files matched your search

+19
View File
@@ -0,0 +1,19 @@
#### Support formatting for Angular ICU expression (#15777 by @sosukesuzuki)
Support two kinds of Angular ICU expressions: [`plural`](https://angular.dev/guide/i18n/translation-files#translate-plurals) and [`select`](https://angular.dev/guide/i18n/translation-files#translate-alternate-expressions).
<!-- prettier-ignore -->
```html
<span i18n>
Updated:
{minutes, plural,
=0 {just now}
=1 {one minute ago}
other {{{minutes}} minutes ago}
}
</span>
<span i18n>
The author is {gender, select, male {male} female {female} other {other}}
</span>
```
+1 -1
View File
@@ -43,7 +43,7 @@
"acorn": "8.11.2",
"acorn-jsx": "5.3.2",
"angular-estree-parser": "9.0.0",
"angular-html-parser": "5.0.2",
"angular-html-parser": "5.2.0",
"camelcase": "8.0.0",
"chalk": "5.3.0",
"ci-info": "4.0.0",
+4 -2
View File
@@ -1,6 +1,8 @@
const NODES_KEYS = {
attrs: true,
children: true,
cases: true, // plural and select
expression: true, // for expansionCase
};
const NON_ENUMERABLE_PROPERTIES = new Set(["parent"]);
@@ -136,12 +138,12 @@ class Node {
get prev() {
// @ts-expect-error
return this.parent?.children[this.parent.children.indexOf(this) - 1];
return this.parent?.children?.[this.parent.children.indexOf(this) - 1];
}
get next() {
// @ts-expect-error
return this.parent?.children[this.parent.children.indexOf(this) + 1];
return this.parent?.children?.[this.parent.children.indexOf(this) + 1];
}
// for element and attribute
+7
View File
@@ -10,6 +10,9 @@ const ignoredProperties = new Set([
"tagDefinition",
"tokens",
"valueTokens",
"switchValueSourceSpan",
"expSourceSpan",
"valueSourceSpan",
]);
const embeddedAngularControlFlowBlocks = new Set([
@@ -47,6 +50,10 @@ function clean(ast, newNode) {
}
}
}
if (ast.type === "angularIcuExpression") {
newNode.switchValue = ast.switchValue.trim();
}
}
clean.ignoredProperties = ignoredProperties;
+21
View File
@@ -67,6 +67,16 @@ function normalizeAngularControlFlowBlock(node) {
};
}
function normalizeAngularIcuExpression(node) {
if (node.type === "plural" || node.type === "select") {
node.clause = node.type;
node.type = "angularIcuExpression";
}
if (node.type === "expansionCase") {
node.type = "angularIcuCase";
}
}
/**
* @param {string} input
* @param {ParseOptions} parseOptions
@@ -261,6 +271,16 @@ function ngHtmlParser(input, parseOptions, options) {
visitAll(
new (class extends RecursiveVisitor {
// Angular does not visit to the children of expansionCase
// https://github.com/angular/angular/blob/e3a6bf9b6c3bef03df9bfc8f05b817bc875cbad6/packages/compiler/src/ml_parser/ast.ts#L161
visitExpansionCase(ast, context) {
if (name === "angular") {
// @ts-expect-error
this.visitChildren(context, (visit) => {
visit(ast.expression);
});
}
}
visit(node) {
restoreNameAndValue(node);
addTagDefinition(node);
@@ -374,6 +394,7 @@ function parse(
}
normalizeAngularControlFlowBlock(node);
normalizeAngularIcuExpression(node);
});
return ast;
+62
View File
@@ -0,0 +1,62 @@
import {
softline,
line,
indent,
join,
group,
} from "../../document/builders.js";
import htmlWhitespaceUtils from "../../utils/html-whitespace-utils.js";
import { printClosingTagEnd, printOpeningTagStart } from "./tag.js";
/*
<span i18n>
Updated {minutes, plural,
=0 {just now}
=1 {one minute ago}
other {{{minutes}} minutes ago}
}
</span>
*/
function printAngularIcuExpression(path, options, print) {
const { node } = path;
return [
printOpeningTagStart(node, options),
group([
node.switchValue.trim(),
", ",
node.clause,
node.cases.length > 0
? [",", indent([line, join(line, path.map(print, "cases"))])]
: "",
softline,
]),
printClosingTagEnd(node, options),
];
}
function printAngularIcuCase(path, options, print) {
const { node } = path;
return [
node.value,
" {",
group([
indent([
softline,
path.map(({ node }) => {
// FIXME: We need a fundamental solution.
// Like this: https://github.com/prettier/prettier/blob/c1ebae2e8303841e59bc02e9e719a93e964ab0e9/src/language-html/print-preprocess.js#L254
if (node.type === "text" && !htmlWhitespaceUtils.trim(node.value)) {
return "";
}
return print();
}, "expression"),
]),
softline,
]),
"}",
];
}
export { printAngularIcuExpression, printAngularIcuCase };
+2 -1
View File
@@ -60,7 +60,8 @@ function printElement(path, options, print) {
*/
const shouldHugContent =
node.children.length === 1 &&
node.firstChild.type === "interpolation" &&
(node.firstChild.type === "interpolation" ||
node.firstChild.type === "angularIcuExpression") &&
node.firstChild.isLeadingSpaceSensitive &&
!node.firstChild.hasLeadingSpaces &&
node.lastChild.isTrailingSpaceSensitive &&
+4
View File
@@ -97,6 +97,8 @@ function printClosingTagEndMarker(node, options) {
return "]><!-->";
case "interpolation":
return "}}";
case "angularIcuExpression":
return "}";
case "element":
if (node.isSelfClosing) {
return "/>";
@@ -345,6 +347,8 @@ function printOpeningTagStartMarker(node) {
return "{{";
case "docType":
return node.value === "html" ? "<!doctype" : "<!DOCTYPE";
case "angularIcuExpression":
return "{";
case "element":
if (node.condition) {
return `<!--[if ${node.condition}]><!--><${node.rawName}`;
+9
View File
@@ -26,6 +26,10 @@ import {
printAngularControlFlowBlockParameters,
} from "./print/angular-control-flow-block.js";
import getVisitorKeys from "./get-visitor-keys.js";
import {
printAngularIcuExpression,
printAngularIcuCase,
} from "./print/angular-icu-expression.js";
function genericPrint(path, options, print) {
const { node } = path;
@@ -49,6 +53,11 @@ function genericPrint(path, options, print) {
case "angularControlFlowBlockParameter":
return htmlWhitespaceUtils.trim(node.expression);
case "angularIcuExpression":
return printAngularIcuExpression(path, options, print);
case "angularIcuCase":
return printAngularIcuCase(path, options, print);
case "ieConditionalStartComment":
case "ieConditionalEndComment":
return [printOpeningTagStart(node), printClosingTagEnd(node)];
+2
View File
@@ -14,6 +14,8 @@ const visitorKeys = {
angularControlFlowBlock: ["children", "parameters"],
angularControlFlowBlockParameters: ["children"],
angularControlFlowBlockParameter: [],
angularIcuExpression: ["cases"],
angularIcuCase: ["expression"],
};
export default visitorKeys;
@@ -0,0 +1,198 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`basic-plural-01.html format 1`] = `
====================================options=====================================
parsers: ["angular"]
printWidth: 80
| printWidth
=====================================input======================================
<span i18n>Updated {minutes, plural, =0 {just now} =1 {one minute ago} other {{{minutes}} minutes ago}}</span>
=====================================output=====================================
<span i18n
>Updated
{minutes, plural,
=0 {just now}
=1 {one minute ago}
other {{{minutes}} minutes ago}
}</span>
================================================================================
`;
exports[`basic-plural-02.html format 1`] = `
====================================options=====================================
parsers: ["angular"]
printWidth: 80
| printWidth
=====================================input======================================
{ count, plural,
=0 {{ sex, select,
other {<p>deeply nested</p>}
}}
=other {a lot}
}
{ count123, plural,
=0 {{ sex, select,
other {<p>deeply nested</p>}
}}
=other {a lot}
}
=====================================output=====================================
{count, plural, =0 {{sex, select, other {<p>deeply nested</p>}}} =other {a lot}}
{count123, plural,
=0 {{sex, select, other {<p>deeply nested</p>}}}
=other {a lot}
}
================================================================================
`;
exports[`basic-plural-03.html format 1`] = `
====================================options=====================================
parsers: ["angular"]
printWidth: 80
| printWidth
=====================================input======================================
<foo>
<bar>{VAR_PLURAL, plural, =0 {zero} =1 {un} =2 {deux} other {<pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" type="fmt" dispStart="&lt;b&gt;" dispEnd="&lt;/b&gt;">beaucoup</pc>} }</bar>
<baz>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" type="fmt" dispStart="&lt;b&gt;" dispEnd="&lt;/b&gt;">many</pc>} }</baz>
</foo>
=====================================output=====================================
<foo>
<bar>{VAR_PLURAL, plural,
=0 {zero}
=1 {un}
=2 {deux}
other {
<pc
id="0"
equivStart="START_BOLD_TEXT"
equivEnd="CLOSE_BOLD_TEXT"
type="fmt"
dispStart="&lt;b&gt;"
dispEnd="&lt;/b&gt;"
>beaucoup</pc
>
}
}</bar>
<baz>{VAR_PLURAL, plural,
=0 {zero}
=1 {one}
=2 {two}
other {
<pc
id="0"
equivStart="START_BOLD_TEXT"
equivEnd="CLOSE_BOLD_TEXT"
type="fmt"
dispStart="&lt;b&gt;"
dispEnd="&lt;/b&gt;"
>many</pc
>
}
}</baz>
</foo>
================================================================================
`;
exports[`basic-plural-04.html format 1`] = `
====================================options=====================================
parsers: ["angular"]
printWidth: 80
| printWidth
=====================================input======================================
<foo>
<bar>{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <ph id="0" equiv="INTERPOLATION" disp="{{response.getItemsList().length}}"/> results} }</bar>
</foo>
=====================================output=====================================
<foo>
<bar>{VAR_PLURAL, plural,
=0 {Found no results}
=1 {Found one result}
other {
Found <ph
id="0"
equiv="INTERPOLATION"
disp="{{ response.getItemsList().length }}"
/> results
}
}</bar>
</foo>
================================================================================
`;
exports[`basic-select-01.html format 1`] = `
====================================options=====================================
parsers: ["angular"]
printWidth: 80
| printWidth
=====================================input======================================
<span i18n>The author is {gender, select, male {male} female {female} other {other}}</span>
=====================================output=====================================
<span i18n
>The author is {gender, select, male {male} female {female} other {other}
}</span>
================================================================================
`;
exports[`basic-select-02.html format 1`] = `
====================================options=====================================
parsers: ["angular"]
printWidth: 80
| printWidth
=====================================input======================================
<ng-template>{gender, select, male {male} female {female} other {other}}</ng-template>
<ng-container>{age, select, 10 {ten} 20 {twenty} other {other}}</ng-container>
=====================================output=====================================
<ng-template>{gender, select,
male {male}
female {female}
other {other}
}</ng-template>
<ng-container>{age, select, 10 {ten} 20 {twenty} other {other}}</ng-container>
================================================================================
`;
exports[`combination-01.html format 1`] = `
====================================options=====================================
parsers: ["angular"]
printWidth: 80
| printWidth
=====================================input======================================
<span i18n>Updated: {minutes, plural,
=0 {just now}
=1 {one minute ago}
other {{{minutes}} minutes ago by {gender, select, male {male} female {female} other {other}}}}
</span>
=====================================output=====================================
<span i18n
>Updated:
{minutes, plural,
=0 {just now}
=1 {one minute ago}
other {
{{minutes}} minutes ago by {gender, select,
male {male}
female {female}
other {other}
}
}
}
</span>
================================================================================
`;
@@ -0,0 +1 @@
<span i18n>Updated {minutes, plural, =0 {just now} =1 {one minute ago} other {{{minutes}} minutes ago}}</span>
@@ -0,0 +1,13 @@
{ count, plural,
=0 {{ sex, select,
other {<p>deeply nested</p>}
}}
=other {a lot}
}
{ count123, plural,
=0 {{ sex, select,
other {<p>deeply nested</p>}
}}
=other {a lot}
}
@@ -0,0 +1,4 @@
<foo>
<bar>{VAR_PLURAL, plural, =0 {zero} =1 {un} =2 {deux} other {<pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" type="fmt" dispStart="&lt;b&gt;" dispEnd="&lt;/b&gt;">beaucoup</pc>} }</bar>
<baz>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<pc id="0" equivStart="START_BOLD_TEXT" equivEnd="CLOSE_BOLD_TEXT" type="fmt" dispStart="&lt;b&gt;" dispEnd="&lt;/b&gt;">many</pc>} }</baz>
</foo>
@@ -0,0 +1,3 @@
<foo>
<bar>{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <ph id="0" equiv="INTERPOLATION" disp="{{response.getItemsList().length}}"/> results} }</bar>
</foo>
@@ -0,0 +1 @@
<span i18n>The author is {gender, select, male {male} female {female} other {other}}</span>
@@ -0,0 +1,2 @@
<ng-template>{gender, select, male {male} female {female} other {other}}</ng-template>
<ng-container>{age, select, 10 {ten} 20 {twenty} other {other}}</ng-container>
@@ -0,0 +1,5 @@
<span i18n>Updated: {minutes, plural,
=0 {just now}
=1 {one minute ago}
other {{{minutes}} minutes ago by {gender, select, male {male} female {female} other {other}}}}
</span>
@@ -0,0 +1 @@
run_spec(import.meta, ["angular"]);
@@ -1394,6 +1394,12 @@ exports[`visitor keys html 1`] = `
"angularControlFlowBlockParameters": [
"children",
],
"angularIcuCase": [
"expression",
],
"angularIcuExpression": [
"cases",
],
"attribute": [],
"cdata": [],
"comment": [],
+5 -5
View File
@@ -2150,12 +2150,12 @@ __metadata:
languageName: node
linkType: hard
"angular-html-parser@npm:5.0.2":
version: 5.0.2
resolution: "angular-html-parser@npm:5.0.2"
"angular-html-parser@npm:5.2.0":
version: 5.2.0
resolution: "angular-html-parser@npm:5.2.0"
dependencies:
tslib: ^2.6.2
checksum: 86f8fc123e696b10878759c516d1876e4eff3d7cccf06cd22c2f572f44950e721480b44e2367802005f2638f3a84c7d7b826cd1fa0751387787f255097a5b401
checksum: 12253b32a064f945e4abb5b107287a9de3576b5404161374bd9a237381895de40b88a9b96e830a4a996724865f7ef298cd046869a56cd1acc714794870401e7f
languageName: node
linkType: hard
@@ -7305,7 +7305,7 @@ __metadata:
acorn: 8.11.2
acorn-jsx: 5.3.2
angular-estree-parser: 9.0.0
angular-html-parser: 5.0.2
angular-html-parser: 5.2.0
benchmark: 2.1.4
browserslist-to-esbuild: 1.2.0
c8: 8.0.1