handlebars: Support escaping a mustache with a backslash (#8634)
* Add a failing test * Support escaping a mustache with `\` It is documented in handlebarjs documentation here: https://handlebarsjs.com/guide/expressions.html#escaping-handlebars-expressions * Preprocess TextNode to adjust the need of backslashes Given that template: ```hbs a non-escaped mustache: \\\{{helper}} ``` Glimmer parser will produce two body nodes: - a TextNode where `node.chars === "a non-escaped mustache: \\\\"` - a MustacheStatement for `{{helper}}` In order to correctly print the input, we need to adjust the TextNode to `node.chars === "a non-escaped mustache: \\\\\\"`, namely we have to add a backslash that is removed by Glimmer parser. This is this commit purpose. * Add changelog note
This commit is contained in:
1 parent
05262d5e4e
commit
bff049fd96
5 files changed
+118
-1
No files matched your search
@@ -0,0 +1,19 @@
|
||||
#### Support escaping a mustache with a backslash ([#8634](https://github.com/prettier/prettier/pull/8634) by [@dcyriller](https://github.com/dcyriller)
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```hbs
|
||||
{{!-- Input --}}
|
||||
\{{mustache}}
|
||||
\\{{mustache}}
|
||||
\\\{{mustache}}
|
||||
|
||||
{{!-- Prettier stable --}}
|
||||
{{mustache}}
|
||||
\{{mustache}}
|
||||
\\{{mustache}}
|
||||
|
||||
{{!-- Prettier master --}}
|
||||
\{{mustache}}
|
||||
\\{{mustache}}
|
||||
\\\{{mustache}}
|
||||
```
|
||||
@@ -0,0 +1,34 @@
|
||||
"use strict";
|
||||
|
||||
const transform = require("@glimmer/syntax").traverse;
|
||||
|
||||
const PREPROCESS_PIPELINE = [addBackslash];
|
||||
|
||||
/* VISITORS */
|
||||
|
||||
/*
|
||||
*/
|
||||
function addBackslash(/* options*/) {
|
||||
return {
|
||||
TextNode(node) {
|
||||
/* from the following template: `non-escaped mustache \\{{helper}}`
|
||||
* glimmer parser will produce an AST missing a backslash
|
||||
* so here we add it back
|
||||
* */
|
||||
if (node.chars.includes("\\")) {
|
||||
node.chars = node.chars.replace(/\\/, "\\\\");
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function preprocess(ast, options) {
|
||||
for (const fn of PREPROCESS_PIPELINE) {
|
||||
const visitor = fn(options);
|
||||
transform(ast, visitor);
|
||||
}
|
||||
|
||||
return ast;
|
||||
}
|
||||
|
||||
module.exports = preprocess;
|
||||
@@ -1,6 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
const clean = require("./clean");
|
||||
const preprocess = require("./preprocess");
|
||||
|
||||
const {
|
||||
concat,
|
||||
@@ -251,9 +252,18 @@ function print(path, options, print) {
|
||||
}
|
||||
}
|
||||
|
||||
let text = n.chars;
|
||||
/* if `{{my-component}}` (or any text starting with a mustache)
|
||||
* makes it to the TextNode,
|
||||
* it means it was escaped,
|
||||
* so let's print it escaped, ie.; `\{{my-component}}` */
|
||||
if (text.startsWith("{{") && text.includes("}}")) {
|
||||
text = "\\" + text;
|
||||
}
|
||||
|
||||
return concat([
|
||||
...generateHardlines(leadingLineBreaksCount, maxLineBreaksToPreserve),
|
||||
n.chars.replace(/^\s+/g, leadingSpace).replace(/\s+$/, trailingSpace),
|
||||
text.replace(/^\s+/g, leadingSpace).replace(/\s+$/, trailingSpace),
|
||||
...generateHardlines(trailingLineBreaksCount, maxLineBreaksToPreserve),
|
||||
]);
|
||||
}
|
||||
@@ -652,4 +662,5 @@ function locationToOffset(source, line, column) {
|
||||
module.exports = {
|
||||
print,
|
||||
massageAstNode: clean,
|
||||
preprocess,
|
||||
};
|
||||
@@ -389,3 +389,50 @@ printWidth: 80
|
||||
</div>
|
||||
================================================================================
|
||||
`;
|
||||
|
||||
exports[`escaped.hbs - {"singleQuote":true} format 1`] = `
|
||||
====================================options=====================================
|
||||
parsers: ["glimmer"]
|
||||
printWidth: 80
|
||||
singleQuote: true
|
||||
| printWidth
|
||||
=====================================input======================================
|
||||
an escaped mustache:
|
||||
\\{{my-component}}
|
||||
a non-escaped mustache:
|
||||
\\\\{{my-component}}
|
||||
another non-escaped mustache:
|
||||
\\\\\\{{my-component}}
|
||||
|
||||
=====================================output=====================================
|
||||
an escaped mustache:
|
||||
\\{{my-component}}
|
||||
a non-escaped mustache:
|
||||
\\\\{{my-component}}
|
||||
another non-escaped mustache:
|
||||
\\\\\\{{my-component}}
|
||||
================================================================================
|
||||
`;
|
||||
|
||||
exports[`escaped.hbs format 1`] = `
|
||||
====================================options=====================================
|
||||
parsers: ["glimmer"]
|
||||
printWidth: 80
|
||||
| printWidth
|
||||
=====================================input======================================
|
||||
an escaped mustache:
|
||||
\\{{my-component}}
|
||||
a non-escaped mustache:
|
||||
\\\\{{my-component}}
|
||||
another non-escaped mustache:
|
||||
\\\\\\{{my-component}}
|
||||
|
||||
=====================================output=====================================
|
||||
an escaped mustache:
|
||||
\\{{my-component}}
|
||||
a non-escaped mustache:
|
||||
\\\\{{my-component}}
|
||||
another non-escaped mustache:
|
||||
\\\\\\{{my-component}}
|
||||
================================================================================
|
||||
`;
|
||||
@@ -0,0 +1,6 @@
|
||||
an escaped mustache:
|
||||
\{{my-component}}
|
||||
a non-escaped mustache:
|
||||
\\{{my-component}}
|
||||
another non-escaped mustache:
|
||||
\\\{{my-component}}
|
||||
Reference in new issue
Block a user