Preserve Angular control flow syntax in ngNonBindable directive (#20066)

Co-authored-by: fisker <lionkay@gmail.com>
This commit is contained in:
Максим Ивановandfisker authored and GitHub committed 2026-09-20 08:43:36 +08:00
1 parent 1a15312010
commit 0292df6bf3
6 files changed
+239 -1

No files matched your search

+20
View File
@@ -0,0 +1,20 @@
#### Preserve Angular syntax inside elements with `ngNonBindable` directive (#20066 by @splincode)
<!-- prettier-ignore -->
```html
<!-- Input -->
<span ngNonBindable>@if(foo){bar}</span>
<span ngNonBindable>@let foo=bar+baz;</span>
<!-- Prettier stable -->
<span ngNonBindable>
@if (foo) {
bar
}
</span>
<span ngNonBindable>@let foo = bar + baz;</span>
<!-- Prettier main -->
<span ngNonBindable>@if(foo){bar}</span>
<span ngNonBindable>@let foo=bar+baz;</span>
```
+83
View File
@@ -13,6 +13,7 @@ import {
} from "./utilities/index.js";
const PREPROCESS_PIPELINE = [
convertAngularNonBindableNodesToText,
removeIgnorableFirstLf,
mergeIfConditionalStartEndCommentIntoElementOpeningTag,
mergeCdataIntoText,
@@ -97,6 +98,88 @@ function mergeIfConditionalStartEndCommentIntoElementOpeningTag(
});
}
function convertAngularNonBindableNodesToText(ast, options) {
if (
options.parser !== "angular" ||
!options.originalText.includes("ngNonBindable")
) {
return;
}
ast.walk((node) => {
if (
node.kind === "element" &&
Object.hasOwn(node.attrMap, "ngNonBindable")
) {
convertAngularNonBindableChildren(node, options);
}
});
}
// TODO: Avoid recursive call
function convertAngularNonBindableChildren(node, options) {
const { children } = node;
if (!children) {
return;
}
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (child.kind === "angularControlFlowBlock") {
convertAngularNonBindableChildren(child, options);
const replacements = [];
let { start } = child.sourceSpan;
for (const blockChild of child.children) {
if (start.offset < blockChild.sourceSpan.start.offset) {
const sourceSpan = new ParseSourceSpan(
start,
blockChild.sourceSpan.start,
);
replacements.push(createOriginalTextNode(sourceSpan, options));
}
replacements.push(blockChild);
start = blockChild.sourceSpan.end;
}
if (start.offset < child.sourceSpan.end.offset) {
const sourceSpan = new ParseSourceSpan(start, child.sourceSpan.end);
replacements.push(createOriginalTextNode(sourceSpan, options));
}
children.splice(
i,
1,
...replacements.map((replacement) => node.createChild(replacement)),
);
i += replacements.length - 1;
continue;
}
if (child.kind === "angularLetDeclaration") {
children[i] = node.createChild(
createOriginalTextNode(child.sourceSpan, options),
);
continue;
}
convertAngularNonBindableChildren(child, options);
}
}
function createOriginalTextNode(sourceSpan, options) {
return {
kind: "text",
value: options.originalText.slice(
sourceSpan.start.offset,
sourceSpan.end.offset,
),
sourceSpan,
};
}
function mergeNodeIntoText(ast, shouldMerge, getValue) {
ast.walk((node) => {
if (node.children) {
@@ -1,6 +1,62 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`ng-non-bindable.html format 1`] = `
exports[`control-flow.html format 1`] = `
====================================options=====================================
parsers: ["angular"]
printWidth: 80 (default) |
=====================================input======================================
<div ngNonBindable> @if ( foo ){ bar } </div>
<div ngNonBindable> _if ( foo )[ bar ] </div>
<span ngNonBindable> @if ( foo ){ bar } </span>
<span ngNonBindable> _if ( foo )[ bar ] </span>
<pre ngNonBindable> @if ( foo ){ bar } </pre>
<pre ngNonBindable> _if ( foo )[ bar ] </pre>
<div ngNonBindable> text @if ( foo ){ bar } text </div>
<span ngNonBindable> text @if ( foo ){ bar } text </span>
<pre ngNonBindable> text @if ( foo ){ bar } text </pre>
<div ngNonBindable> @if ( foo ){ <span>
<IMG
src="./foo.png" style=" color: red
"
/>
</span> } </div>
<div ngNonBindable> @if (foo ){<span>text
</span>} </div>
<div ngNonBindable> <div> @if ( foo ){ bar } </div></div>
=====================================output=====================================
<div ngNonBindable>@if ( foo ){ bar }</div>
<div ngNonBindable>_if ( foo )[ bar ]</div>
<span ngNonBindable> @if ( foo ){ bar } </span>
<span ngNonBindable> _if ( foo )[ bar ] </span>
<pre ngNonBindable> @if ( foo ){ bar } </pre>
<pre ngNonBindable> _if ( foo )[ bar ] </pre>
<div ngNonBindable>text @if ( foo ){ bar } text</div>
<span ngNonBindable> text @if ( foo ){ bar } text </span>
<pre ngNonBindable> text @if ( foo ){ bar } text </pre>
<div ngNonBindable>
@if ( foo ){
<span>
<IMG src="./foo.png" style="color: red" />
</span>
}
</div>
<div ngNonBindable>@if (foo ){<span>text </span>}</div>
<div ngNonBindable><div>@if ( foo ){ bar }</div></div>
================================================================================
`;
exports[`interpolation.html format 1`] = `
====================================options=====================================
parsers: ["angular"]
printWidth: 80 (default) |
@@ -20,6 +76,8 @@ parsers: ["angular"]
}
</span>
<pre ngNonBindable>{{foo +bar}}</pre>
=====================================output=====================================
<span ngNonBindable>{{foo +bar}}</span>
@@ -36,5 +94,44 @@ parsers: ["angular"]
}
</span>
<pre ngNonBindable>{{foo +bar}}</pre>
================================================================================
`;
exports[`let-declaration.html format 1`] = `
====================================options=====================================
parsers: ["angular"]
printWidth: 80 (default) |
=====================================input======================================
<div ngNonBindable> @let foo = bar+baz; </div>
<div ngNonBindable> _let foo = bar+baz; </div>
<span ngNonBindable> @let foo = bar+baz; </span>
<span ngNonBindable> _let foo = bar+baz; </span>
<pre ngNonBindable> @let foo = bar+baz; </pre>
<pre ngNonBindable> _let foo = bar+baz; </pre>
<div ngNonBindable> text @let foo = bar+baz; text </div>
<span ngNonBindable> text @let foo = bar+baz; text </span>
<pre ngNonBindable> text @let foo = bar+baz; text </pre>
<span ngNonBindable>@if(foo){bar}</span>
<div ngNonBindable> <div> @let foo = bar+baz; </div></div>
=====================================output=====================================
<div ngNonBindable>@let foo = bar+baz;</div>
<div ngNonBindable>_let foo = bar+baz;</div>
<span ngNonBindable> @let foo = bar+baz; </span>
<span ngNonBindable> _let foo = bar+baz; </span>
<pre ngNonBindable> @let foo = bar+baz; </pre>
<pre ngNonBindable> _let foo = bar+baz; </pre>
<div ngNonBindable>text @let foo = bar+baz; text</div>
<span ngNonBindable> text @let foo = bar+baz; text </span>
<pre ngNonBindable> text @let foo = bar+baz; text </pre>
<span ngNonBindable>@if(foo){bar}</span>
<div ngNonBindable><div>@let foo = bar+baz;</div></div>
================================================================================
`;
@@ -0,0 +1,23 @@
<div ngNonBindable> @if ( foo ){ bar } </div>
<div ngNonBindable> _if ( foo )[ bar ] </div>
<span ngNonBindable> @if ( foo ){ bar } </span>
<span ngNonBindable> _if ( foo )[ bar ] </span>
<pre ngNonBindable> @if ( foo ){ bar } </pre>
<pre ngNonBindable> _if ( foo )[ bar ] </pre>
<div ngNonBindable> text @if ( foo ){ bar } text </div>
<span ngNonBindable> text @if ( foo ){ bar } text </span>
<pre ngNonBindable> text @if ( foo ){ bar } text </pre>
<div ngNonBindable> @if ( foo ){ <span>
<IMG
src="./foo.png" style=" color: red
"
/>
</span> } </div>
<div ngNonBindable> @if (foo ){<span>text
</span>} </div>
<div ngNonBindable> <div> @if ( foo ){ bar } </div></div>
@@ -12,3 +12,5 @@
<span>{{foo +bar}}</span>
}
</span>
<pre ngNonBindable>{{foo +bar}}</pre>
@@ -0,0 +1,13 @@
<div ngNonBindable> @let foo = bar+baz; </div>
<div ngNonBindable> _let foo = bar+baz; </div>
<span ngNonBindable> @let foo = bar+baz; </span>
<span ngNonBindable> _let foo = bar+baz; </span>
<pre ngNonBindable> @let foo = bar+baz; </pre>
<pre ngNonBindable> _let foo = bar+baz; </pre>
<div ngNonBindable> text @let foo = bar+baz; text </div>
<span ngNonBindable> text @let foo = bar+baz; text </span>
<pre ngNonBindable> text @let foo = bar+baz; text </pre>
<span ngNonBindable>@if(foo){bar}</span>
<div ngNonBindable> <div> @let foo = bar+baz; </div></div>