Skip to content

Commit 3b3ca18

Browse files
authored
Merge pull request #1720 from glimmerjs/printer-quote-bug
Printer quoting bug
2 parents 9dcad59 + 656817c commit 3b3ca18

File tree

2 files changed

+48
-7
lines changed

2 files changed

+48
-7
lines changed

packages/@glimmer/syntax/lib/generation/printer.ts

+17-7
Original file line numberDiff line numberDiff line change
@@ -293,22 +293,32 @@ export default class Printer {
293293

294294
AttrNodeValue(value: ASTv1.AttrNode['value']): void {
295295
if (value.type === 'TextNode') {
296-
this.buffer += '"';
297-
this.TextNode(value, true);
298-
this.buffer += '"';
296+
let quote: '"' | "'" = '"';
297+
if (this.options.entityEncoding === 'raw') {
298+
if (value.chars.includes('"') && !value.chars.includes("'")) {
299+
quote = "'";
300+
}
301+
}
302+
this.buffer += quote;
303+
this.TextNode(value, quote);
304+
this.buffer += quote;
299305
} else {
300306
this.Node(value);
301307
}
302308
}
303309

304-
TextNode(text: ASTv1.TextNode, isAttr?: boolean): void {
310+
TextNode(text: ASTv1.TextNode, isInAttr?: "'" | '"'): void {
305311
if (this.handledByOverride(text)) {
306312
return;
307313
}
308314

309315
if (this.options.entityEncoding === 'raw') {
310-
this.buffer += text.chars;
311-
} else if (isAttr) {
316+
if (isInAttr && text.chars.includes(isInAttr)) {
317+
this.buffer += escapeAttrValue(text.chars);
318+
} else {
319+
this.buffer += text.chars;
320+
}
321+
} else if (isInAttr) {
312322
this.buffer += escapeAttrValue(text.chars);
313323
} else {
314324
this.buffer += escapeText(text.chars);
@@ -393,7 +403,7 @@ export default class Printer {
393403
this.buffer += '"';
394404
concat.parts.forEach((part) => {
395405
if (part.type === 'TextNode') {
396-
this.TextNode(part, true);
406+
this.TextNode(part, '"');
397407
} else {
398408
this.Node(part);
399409
}

packages/@glimmer/syntax/test/generation/print-test.ts

+31
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,38 @@ QUnit.module('[glimmer-syntax] Code generation - source -> source', () => {
152152
' {{#foo}}\n {{bar}}\n {{/foo}}',
153153

154154
`<span class="stampFont" style="font-family: 'stampfont'">&#xf000;</span>`,
155+
156+
// preserves single quoting when it avoids introducing &quot;
157+
`<div class='He said "yes"'></div>`,
155158
].forEach(buildTest);
159+
160+
test('falls back to using entity encoding when necessary for correctness', (assert) => {
161+
let ast = parse(`<div class='transform-target'></div>`, {
162+
mode: 'codemod',
163+
parseOptions: { ignoreStandalone: true },
164+
plugins: {
165+
ast: [
166+
function (_env) {
167+
return {
168+
name: 'test',
169+
visitor: {
170+
TextNode(x) {
171+
if (x.chars === 'transform-target') {
172+
x.chars = 'He said "can\'t"';
173+
}
174+
},
175+
},
176+
};
177+
},
178+
],
179+
},
180+
});
181+
182+
assert.strictEqual(
183+
print(ast, { entityEncoding: 'raw' }),
184+
`<div class="He said &quot;can't&quot;"></div>`
185+
);
186+
});
156187
});
157188

158189
QUnit.module('[glimmer-syntax] Code generation - override', () => {

0 commit comments

Comments
 (0)