Escape Quotes in Glimmer String Literals (#5143)

master
Mahir Shah 2018-10-05 13:19:33 -07:00 committed by Simon Lydell
parent e9cde7d8d4
commit 30ec30daa9
4 changed files with 703 additions and 1 deletions

View File

@ -249,7 +249,7 @@ function print(path, options, print) {
return concat(["<!--", n.value, "-->"]);
}
case "StringLiteral": {
return `"${n.value}"`;
return printStringLiteral(n.value, options);
}
case "NumberLiteral": {
return String(n.value);
@ -267,6 +267,50 @@ function print(path, options, print) {
}
}
/**
* Prints a string literal with the correct surrounding quotes based on
* `options.singleQuote` and the number of escaped quotes contained in
* the string literal. This function is the glimmer equivalent of `printString`
* in `common/util`, but has differences because of the way escaped characters
* are treated in hbs string literals.
* @param {string} stringLiteral - the string literal value
* @param {object} options - the prettier options object
*/
function printStringLiteral(stringLiteral, options) {
const double = { quote: '"', regex: /"/g };
const single = { quote: "'", regex: /'/g };
const preferred = options.singleQuote ? single : double;
const alternate = preferred === single ? double : single;
let shouldUseAlternateQuote = false;
// If `stringLiteral` contains at least one of the quote preferred for
// enclosing the string, we might want to enclose with the alternate quote
// instead, to minimize the number of escaped quotes.
if (
stringLiteral.includes(preferred.quote) ||
stringLiteral.includes(alternate.quote)
) {
const numPreferredQuotes = (stringLiteral.match(preferred.regex) || [])
.length;
const numAlternateQuotes = (stringLiteral.match(alternate.regex) || [])
.length;
shouldUseAlternateQuote = numPreferredQuotes > numAlternateQuotes;
}
const enclosingQuote = shouldUseAlternateQuote ? alternate : preferred;
const escapedStringLiteral = stringLiteral.replace(
enclosingQuote.regex,
`\\${enclosingQuote.quote}`
);
return `${enclosingQuote.quote}${escapedStringLiteral}${
enclosingQuote.quote
}`;
}
function printPath(path, print) {
return path.call(print, "path");
}

View File

@ -134,6 +134,140 @@ exports[`block-statement.hbs - glimmer-verify 1`] = `
</MyComponent>
`;
exports[`block-statement.hbs - glimmer-verify 2`] = `
{{#block param hashKey=hashValue as |blockParam|}}
Hello
{{/block}}
{{#block almost80CharacterLongPositionalParamThatIsFirstAlmost80Chars helloWorldParam key=here}}
{{/block}}
{{#block param param param param param param param hashKey=hashValue as |blockParam|}}
Hello
{{/block}}
{{#block param param param param param param param hashKey=HashValue hashKey=hashValue}}
Hello
{{/block}}
{{#block param param param param param param param param param param param param param}}
Hello
{{/block}}
{{#block hashKey=HashValue hashKey=hashValue hashKey=HashValue hashKey=hashValue hashKey=HashValue}}
Hello
{{/block}}
{{#block}}
{{#block}}
hello
{{/block}}
{{/block}}
{{#block}}
{{#block param}}
hello
{{/block}}
{{/block}}
{{#block param}}
{{#block param}}
hello
{{/block}}
{{/block}}
{{#block}}
hello
{{/block}}
<MyComponent as |firstName|>
{{firstName}}
</MyComponent>
<MyComponent as |firstName lastName|>
{{firstName}} {{lastName}}
</MyComponent>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{{#block param hashKey=hashValue as |blockParam|}}
Hello
{{/block}}
{{#block
almost80CharacterLongPositionalParamThatIsFirstAlmost80Chars
helloWorldParam
key=here
}}{{/block}}
{{#block
param
param
param
param
param
param
param
hashKey=hashValue as |blockParam|
}}
Hello
{{/block}}
{{#block
param
param
param
param
param
param
param
hashKey=HashValue
hashKey=hashValue
}}
Hello
{{/block}}
{{#block
param
param
param
param
param
param
param
param
param
param
param
param
param
}}
Hello
{{/block}}
{{#block
hashKey=HashValue
hashKey=hashValue
hashKey=HashValue
hashKey=hashValue
hashKey=HashValue
}}
Hello
{{/block}}
{{#block}}{{#block}}hello{{/block}}{{/block}}
{{#block}}
{{#block param}}
hello
{{/block}}
{{/block}}
{{#block param}}
{{#block param}}
hello
{{/block}}
{{/block}}
{{#block}}hello{{/block}}
<MyComponent as |firstName|>
{{firstName}}
</MyComponent>
<MyComponent as |firstName lastName|>
{{firstName}}
{{lastName}}
</MyComponent>
`;
exports[`component.hbs - glimmer-verify 1`] = `
<UserGreeting @name="Ricardo" @greeting="Olá" />
{{@greeting}}, {{@name}}!
@ -178,6 +312,50 @@ exports[`component.hbs - glimmer-verify 1`] = `
</div>
`;
exports[`component.hbs - glimmer-verify 2`] = `
<UserGreeting @name="Ricardo" @greeting="Olá" />
{{@greeting}}, {{@name}}!
<Form as |f|>
<f.input @title="hello" />
<f.input>hello</f.input>
</Form>
<this.label @title="hello" />
<button onclick={{action next}}>Next</button>
<button disabled class="disabled"></button>
<button disabled=disabled class="disabled"></button>
<img alt="" />
<div ...attributes>Hello</div>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<UserGreeting @name="Ricardo" @greeting="Olá" />
{{@greeting}}
,
{{@name}}
!
<Form as |f|>
<f.input @title="hello" />
<f.input>
hello
</f.input>
</Form>
<this.label @title="hello" />
<button onclick={{action next}}>
Next
</button>
<button disabled class="disabled"></button>
<button disabled="disabled" class="disabled"></button>
<img alt="" />
<div ...attributes>
Hello
</div>
`;
exports[`concat-statement.hbs - glimmer-verify 1`] = `
<div class="hello {{if goodbye true}}">
Hello
@ -229,6 +407,57 @@ exports[`concat-statement.hbs - glimmer-verify 1`] = `
<div class="class-p class-q"></div>
`;
exports[`concat-statement.hbs - glimmer-verify 2`] = `
<div class="hello {{if goodbye true}}">
Hello
</div>
<div class="hello {{if goodbye true}} {{if goodbye false}} {{if goodbye true}} {{if goodbye false}} {{if goodbye true}}">
Hello
</div>
<a href="/{{url}}/{{url}}"></a>
<div class=" class-a{{myClass}}"></div>
<div class=" class-b {{myClass}}"></div>
<div class=" {{myClass}}class-c"></div>
<div class=" {{myClass}} class-d"></div>
<div class=" class-e{{myClass}} class-f"></div>
<div class=" class-g{{myClass}}class-h "></div>
<div class=" class-i {{myClass}}class-j"></div>
<div class="class-k {{myClass}} class-l"></div>
<div class=" class-m {{myClass}} class-n {{myClass}}class-o "></div>
<div class=" class-p class-q"></div>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<div class="hello {{if goodbye true}}">
Hello
</div>
<div
class="hello
{{if goodbye true}}
{{if goodbye false}}
{{if goodbye true}}
{{if goodbye false}}
{{if goodbye true}}"
>
Hello
</div>
<a href="/{{url}}/{{url}}"></a>
<div class="class-a{{myClass}}"></div>
<div class="class-b {{myClass}}"></div>
<div class="{{myClass}}class-c"></div>
<div class="{{myClass}} class-d"></div>
<div class="class-e{{myClass}} class-f"></div>
<div class="class-g{{myClass}}class-h"></div>
<div class="class-i {{myClass}}class-j"></div>
<div class="class-k {{myClass}} class-l"></div>
<div class="class-m {{myClass}} class-n {{myClass}}class-o"></div>
<div class="class-p class-q"></div>
`;
exports[`element-modifier-statement.hbs - glimmer-verify 1`] = `
<div {{hello param hash=key}} {{goodbye param}}>
Hello
@ -295,6 +524,72 @@ exports[`element-modifier-statement.hbs - glimmer-verify 1`] = `
</div>
`;
exports[`element-modifier-statement.hbs - glimmer-verify 2`] = `
<div {{hello param hash=key}} {{goodbye param}}>
Hello
</div>
<div {{hello param param param param param param param param param param param param}}>
Hello
</div>
<div {{hello hashPair=value hashPair=value hashPair=value hashPair=value hashPair=value}}>
Hello
</div>
<div {{hello param param param param hashPair=value hashPair=value hashPair=value hashPair=value hashPair=value}}>
Hello
</div>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<div {{hello param hash=key}} {{goodbye param}}>
Hello
</div>
<div
{{hello
param
param
param
param
param
param
param
param
param
param
param
param
}}
>
Hello
</div>
<div
{{hello
hashPair=value
hashPair=value
hashPair=value
hashPair=value
hashPair=value
}}
>
Hello
</div>
<div
{{hello
param
param
param
param
hashPair=value
hashPair=value
hashPair=value
hashPair=value
hashPair=value
}}
>
Hello
</div>
`;
exports[`element-node.hbs - glimmer-verify 1`] = `
<div class="attribute" {{modifier}} {{! comment}}>
Hello
@ -361,6 +656,72 @@ exports[`element-node.hbs - glimmer-verify 1`] = `
<img />
`;
exports[`element-node.hbs - glimmer-verify 2`] = `
<div class="attribute" {{modifier}} {{! comment}}>
Hello
</div>
<div>
Hello
</div>
<div>
hi
</div>
<div>
A long enough string to trigger a line break that would prevent wrapping.
</div>
<div>
A long enough string to trigger a line break that would prevent wrapping more.
</div>
<div>
A long enough string to trigger a line break that would prevent wrapping more and more.
</div>
<div>
{{#block}}
{{hello}}
{{/block}}
</div>
<div>
{{hello}}
</div>
<div></div>
<img />
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<div class="attribute" {{modifier}} {{! comment}}>
Hello
</div>
<div>
Hello
</div>
<div>
hi
</div>
<div>
A long enough string to trigger a line break that would prevent wrapping.
</div>
<div>
A long enough string to trigger a line break that would prevent wrapping more.
</div>
<div>
A long enough string to trigger a line break that would prevent wrapping more and more.
</div>
<div>
{{#block}}{{hello}}{{/block}}
</div>
<div>
{{hello}}
</div>
<div></div>
<img />
`;
exports[`else-if.hbs - glimmer-verify 1`] = `
{{#if a}}
b
@ -496,6 +857,141 @@ exports[`else-if.hbs - glimmer-verify 1`] = `
{{/if}}
`;
exports[`else-if.hbs - glimmer-verify 2`] = `
{{#if a}}
b
{{else if c}}
d
{{else}}
e
{{/if}}
{{#if a}}
b
{{else if c}}
d
{{else}}
hello
{{#if f}}
g
{{/if}}
e
{{/if}}
{{#if a}}
b
{{else if c}}
d
{{else if e}}
f
{{else if g}}
h
{{else}}
j
{{/if}}
<div>
{{#if a}}
b
{{else if c}}
d
{{else}}
e
{{/if}}
</div>
<div>
<div>
{{#if a}}
b
{{else if c}}
d
{{else}}
e
{{/if}}
</div>
</div>
{{#if a}}
b
{{else}}
{{#each c as |d|}}
e
{{/each}}
{{/if}}
{{#if a}}
{{#if b}}
ab
{{else if c}}
ac
{{/if}}
{{/if}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{{#if a}}
b
{{else if c}}
d
{{else}}
e
{{/if}}
{{#if a}}
b
{{else if c}}
d
{{else}}
hello
{{#if f}}
g
{{/if}}
e
{{/if}}
{{#if a}}
b
{{else if c}}
d
{{else if e}}
f
{{else if g}}
h
{{else}}
j
{{/if}}
<div>
{{#if a}}
b
{{else if c}}
d
{{else}}
e
{{/if}}
</div>
<div>
<div>
{{#if a}}
b
{{else if c}}
d
{{else}}
e
{{/if}}
</div>
</div>
{{#if a}}
b
{{else}}
{{#each c as |d|}}
e
{{/each}}
{{/if}}
{{#if a}}
{{#if b}}
ab
{{else if c}}
ac
{{/if}}
{{/if}}
`;
exports[`literals.hbs - glimmer-verify 1`] = `
{{mustache true}}
{{mustache 5}}
@ -514,6 +1010,24 @@ exports[`literals.hbs - glimmer-verify 1`] = `
{{!-- Mustache Comment }} --}}
`;
exports[`literals.hbs - glimmer-verify 2`] = `
{{mustache true}}
{{mustache 5}}
{{mustache undefined}}
{{mustache null}}
<!-- hello world -->
{{! Mustache Comment}}
{{!-- Mustache Comment }} --}}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{{mustache true}}
{{mustache 5}}
{{mustache undefined}}
{{mustache null}}
<!-- hello world -->
{{! Mustache Comment}}
{{!-- Mustache Comment }} --}}
`;
exports[`loop.hbs - glimmer-verify 1`] = `
<ul>
{{#each speakers key="@index" as |speaker|}}
@ -530,6 +1044,54 @@ exports[`loop.hbs - glimmer-verify 1`] = `
</ul>
`;
exports[`loop.hbs - glimmer-verify 2`] = `
<ul>
{{#each speakers key="@index" as |speaker|}}
<li>{{speaker}}</li>
{{/each}}
</ul>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<ul>
{{#each speakers key='@index' as |speaker|}}
<li>
{{speaker}}
</li>
{{/each}}
</ul>
`;
exports[`string-literals.hbs - glimmer-verify 1`] = `
{{"abc"}}
{{'abc'}}
{{" \\" \\" ' more double quote than single quote "}}
{{' \\' \\' " more single quote than double quote '}}
{{' " \\' \\" \\\\ '}}
{{" \\" \\' ' \\\\ "}}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{{"abc"}}
{{"abc"}}
{{' " " \\' more double quote than single quote '}}
{{" ' ' \\" more single quote than double quote "}}
{{' " \\' \\" \\\\ '}}
{{" \\" \\' ' \\\\ "}}
`;
exports[`string-literals.hbs - glimmer-verify 2`] = `
{{"abc"}}
{{'abc'}}
{{" \\" \\" ' more double quote than single quote "}}
{{' \\' \\' " more single quote than double quote '}}
{{' " \\' \\" \\\\ '}}
{{" \\" \\' ' \\\\ "}}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{{'abc'}}
{{'abc'}}
{{' " " \\' more double quote than single quote '}}
{{" ' ' \\" more single quote than double quote "}}
{{' " \\' \\" \\\\ '}}
{{" \\" \\' ' \\\\ "}}
`;
exports[`sub-expressions.hbs - glimmer-verify 1`] = `
<div
{{mustache
@ -618,3 +1180,92 @@ exports[`sub-expressions.hbs - glimmer-verify 1`] = `
)
}}
`;
exports[`sub-expressions.hbs - glimmer-verify 2`] = `
<div
{{mustache
(concat
(service)
(helper param hashPair=Value)
(largeNameHelper param param param param hashPair=value hashPair=value hashPair=Value)
hashPair=(helper param param param param param param hashPair=value hashPair=value hashPair=value)
hashPair=(does not need a line break due to being under 80 chars long)
)
}}
></div>
{{#block
(concat
(service)
(helper param hashPair=Value)
(largeNameHelper param param param param hashPair=value hashPair=value hashPair=Value)
hashPair=(helper param param param param param param hashPair=value hashPair=value hashPair=value)
hashPair=(does not need a line break due to being under 80 chars long)
)
}}
{{/block}}
{{foobar-sub-component/foobar-foo
hook="stringLiteral"
foo=
(t
(concat "stringLiteral" (get blockParam "stringLiteral") hash=hash hash=hash)
foo=(simple-helper (hash hashKey=blockParam.foo assignParam=blockParam.bar))
)
}}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<div
{{mustache
(concat
(service)
(helper param hashPair=Value)
(largeNameHelper
param param param param hashPair=value hashPair=value hashPair=Value
)
hashPair=(helper
param
param
param
param
param
param
hashPair=value
hashPair=value
hashPair=value
)
hashPair=(does not need a line break due to being under 80 chars long)
)
}}
></div>
{{#block
(concat
(service)
(helper param hashPair=Value)
(largeNameHelper
param param param param hashPair=value hashPair=value hashPair=Value
)
hashPair=(helper
param
param
param
param
param
param
hashPair=value
hashPair=value
hashPair=value
)
hashPair=(does not need a line break due to being under 80 chars long)
)
}}{{/block}}
{{foobar-sub-component/foobar-foo
hook='stringLiteral'
foo=(t
(concat
'stringLiteral' (get blockParam 'stringLiteral') hash=hash hash=hash
)
foo=(simple-helper (hash hashKey=blockParam.foo assignParam=blockParam.bar))
)
}}
`;

View File

@ -1 +1,2 @@
run_spec(__dirname, ["glimmer"]);
run_spec(__dirname, ["glimmer"], { singleQuote: true });

View File

@ -0,0 +1,6 @@
{{"abc"}}
{{'abc'}}
{{" \" \" ' more double quote than single quote "}}
{{' \' \' " more single quote than double quote '}}
{{' " \' \" \\ '}}
{{" \" \' ' \\ "}}