Skip to content

Commit 3d8ae19

Browse files
author
Robert Jackson
committed
Maintain relative order of attributes and comments within an ElementNode
(cherry picked from commit 1a54cba) (cherry picked from commit 8fa7141)
1 parent 47a1659 commit 3d8ae19

File tree

6 files changed

+62
-18
lines changed

6 files changed

+62
-18
lines changed

packages/@glimmer/syntax/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export * from './lib/traversal/visitor';
2121
export { default as Path } from './lib/traversal/path';
2222
export { default as Walker } from './lib/traversal/walker';
2323
export { default as print } from './lib/generation/print';
24+
export { sortByLoc } from './lib/generation/util';
2425

2526
// errors
2627
export { default as SyntaxError } from './lib/errors/syntax-error';

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

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
Template,
2828
} from '../types/nodes';
2929
import { voidMap } from '../parser/tokenizer-event-handlers';
30-
import { escapeText, escapeAttrValue } from './util';
30+
import { escapeText, escapeAttrValue, sortByLoc } from './util';
3131

3232
const NON_WHITESPACE = /\S/;
3333

@@ -242,23 +242,21 @@ export default class Printer {
242242

243243
OpenElementNode(el: ElementNode): void {
244244
this.buffer += `<${el.tag}`;
245-
if (el.attributes.length) {
246-
el.attributes.forEach(attr => {
247-
this.buffer += ' ';
248-
this.AttrNode(attr);
249-
});
250-
}
251-
if (el.modifiers.length) {
252-
el.modifiers.forEach(mod => {
253-
this.buffer += ' ';
254-
this.ElementModifierStatement(mod);
255-
});
256-
}
257-
if (el.comments.length) {
258-
el.comments.forEach(comment => {
259-
this.buffer += ' ';
260-
this.MustacheCommentStatement(comment);
261-
});
245+
const parts = [...el.attributes, ...el.modifiers, ...el.comments].sort(sortByLoc);
246+
247+
for (const part of parts) {
248+
this.buffer += ' ';
249+
switch (part.type) {
250+
case 'AttrNode':
251+
this.AttrNode(part);
252+
break;
253+
case 'ElementModifierStatement':
254+
this.ElementModifierStatement(part);
255+
break;
256+
case 'MustacheCommentStatement':
257+
this.MustacheCommentStatement(part);
258+
break;
259+
}
262260
}
263261
if (el.blockParams.length) {
264262
this.BlockParams(el.blockParams);

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import * as AST from '../types/nodes';
2+
13
const enum Char {
24
NBSP = 0xa0,
35
QUOT = 0x22,
@@ -53,3 +55,32 @@ export function escapeText(text: string) {
5355
}
5456
return text;
5557
}
58+
59+
export function isSynthetic(node: AST.Node): boolean {
60+
if (node && node.loc) {
61+
return node.loc.source === '(synthetic)';
62+
}
63+
64+
return false;
65+
}
66+
67+
export function sortByLoc(a: AST.Node, b: AST.Node): -1 | 0 | 1 {
68+
// be conservative about the location where a new node is inserted
69+
if (isSynthetic(a) || isSynthetic(b)) {
70+
return 0;
71+
}
72+
73+
if (a.loc.start.line < b.loc.start.line) {
74+
return -1;
75+
}
76+
77+
if (a.loc.start.line === b.loc.start.line && a.loc.start.column < b.loc.start.column) {
78+
return -1;
79+
}
80+
81+
if (a.loc.start.line === b.loc.start.line && a.loc.start.column === b.loc.start.column) {
82+
return 0;
83+
}
84+
85+
return 1;
86+
}

packages/@glimmer/syntax/lib/parser/handlebars-node-visitors.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ export abstract class HandlebarsNodeVisitors extends Parser {
194194

195195
switch (tokenizer.state) {
196196
case TokenizerState.beforeAttributeName:
197+
case TokenizerState.afterAttributeName:
197198
this.currentStartTag.comments.push(comment);
198199
break;
199200

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ let templates = [
5454

5555
// unescaped
5656
'{{{unescaped}}}',
57+
58+
// Comment in Angle Bracket component
59+
'<Foo {{!-- This is a comment --}} attribute></Foo>',
5760
];
5861

5962
QUnit.module('[glimmer-syntax] Code generation', function() {

packages/@glimmer/syntax/test/parser-node-test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,16 @@ test('a Handlebars comment in proper element space', function() {
538538
);
539539
});
540540

541+
test('a Handlebars comment after a valueless attribute', function() {
542+
let t = '<input foo {{! comment }}>';
543+
astEqual(
544+
t,
545+
b.program([
546+
b.element('input', ['attrs', ['foo', '']], ['comments', b.mustacheComment(' comment ')]),
547+
])
548+
);
549+
});
550+
541551
test('a Handlebars comment in invalid element space', function(assert) {
542552
assert.throws(() => {
543553
parse('\nbefore <div \n a{{! some comment }} data-foo="bar"></div> after');

0 commit comments

Comments
 (0)