Revert "refactor(compiler): expose token parts in Text nodes (#42062)" (#43033)

This reverts commit 8a54896a91.

PR Close #43033
This commit is contained in:
atscott 2021-08-03 14:49:01 -07:00
parent 6f05dd8062
commit ea5ed4e4d4
9 changed files with 124 additions and 240 deletions

View File

@ -396,12 +396,12 @@ class _Visitor implements html.Visitor {
if (nodes.length == 0) { if (nodes.length == 0) {
translatedAttributes.push(new html.Attribute( translatedAttributes.push(new html.Attribute(
attr.name, '', attr.sourceSpan, undefined /* keySpan */, undefined /* valueSpan */, attr.name, '', attr.sourceSpan, undefined /* keySpan */, undefined /* valueSpan */,
undefined /* valueTokens */, undefined /* i18n */)); undefined /* i18n */));
} else if (nodes[0] instanceof html.Text) { } else if (nodes[0] instanceof html.Text) {
const value = (nodes[0] as html.Text).value; const value = (nodes[0] as html.Text).value;
translatedAttributes.push(new html.Attribute( translatedAttributes.push(new html.Attribute(
attr.name, value, attr.sourceSpan, undefined /* keySpan */, attr.name, value, attr.sourceSpan, undefined /* keySpan */,
undefined /* valueSpan */, undefined /* valueTokens */, undefined /* i18n */)); undefined /* valueSpan */, undefined /* i18n */));
} else { } else {
this._reportError( this._reportError(
el, el,

View File

@ -9,7 +9,6 @@
import {AstPath} from '../ast_path'; import {AstPath} from '../ast_path';
import {I18nMeta} from '../i18n/i18n_ast'; import {I18nMeta} from '../i18n/i18n_ast';
import {ParseSourceSpan} from '../parse_util'; import {ParseSourceSpan} from '../parse_util';
import {Token} from './lexer';
interface BaseNode { interface BaseNode {
sourceSpan: ParseSourceSpan; sourceSpan: ParseSourceSpan;
@ -24,8 +23,7 @@ export abstract class NodeWithI18n implements BaseNode {
} }
export class Text extends NodeWithI18n { export class Text extends NodeWithI18n {
constructor( constructor(public value: string, sourceSpan: ParseSourceSpan, i18n?: I18nMeta) {
public value: string, sourceSpan: ParseSourceSpan, public tokens: Token[], i18n?: I18nMeta) {
super(sourceSpan, i18n); super(sourceSpan, i18n);
} }
override visit(visitor: Visitor, context: any): any { override visit(visitor: Visitor, context: any): any {
@ -57,8 +55,8 @@ export class ExpansionCase implements BaseNode {
export class Attribute extends NodeWithI18n { export class Attribute extends NodeWithI18n {
constructor( constructor(
public name: string, public value: string, sourceSpan: ParseSourceSpan, public name: string, public value: string, sourceSpan: ParseSourceSpan,
readonly keySpan: ParseSourceSpan|undefined, public valueSpan: ParseSourceSpan|undefined, readonly keySpan: ParseSourceSpan|undefined, public valueSpan?: ParseSourceSpan,
public valueTokens: Token[]|undefined, i18n: I18nMeta|undefined) { i18n?: I18nMeta) {
super(sourceSpan, i18n); super(sourceSpan, i18n);
} }
override visit(visitor: Visitor, context: any): any { override visit(visitor: Visitor, context: any): any {

View File

@ -8,7 +8,6 @@
import * as html from './ast'; import * as html from './ast';
import {NGSP_UNICODE} from './entities'; import {NGSP_UNICODE} from './entities';
import {Token, TokenType} from './lexer';
import {ParseTreeResult} from './parser'; import {ParseTreeResult} from './parser';
export const PRESERVE_WS_ATTR_NAME = 'ngPreserveWhitespaces'; export const PRESERVE_WS_ATTR_NAME = 'ngPreserveWhitespaces';
@ -75,13 +74,8 @@ export class WhitespaceVisitor implements html.Visitor {
(context.prev instanceof html.Expansion || context.next instanceof html.Expansion); (context.prev instanceof html.Expansion || context.next instanceof html.Expansion);
if (isNotBlank || hasExpansionSibling) { if (isNotBlank || hasExpansionSibling) {
// Process the whitespace in the tokens of this Text node return new html.Text(
const tokens = text.tokens.map( replaceNgsp(text.value).replace(WS_REPLACE_REGEXP, ' '), text.sourceSpan, text.i18n);
token => token.type === TokenType.TEXT ? createTextTokenAfterWhitespaceProcessing(token) :
token);
// Process the whitespace of the value of this Text node
const value = processWhitespace(text.value);
return new html.Text(value, text.sourceSpan, tokens, text.i18n);
} }
return null; return null;
@ -100,14 +94,6 @@ export class WhitespaceVisitor implements html.Visitor {
} }
} }
function createTextTokenAfterWhitespaceProcessing(token: Token): Token {
return new Token(token.type, [processWhitespace(token.parts[0])], token.sourceSpan);
}
function processWhitespace(text: string): string {
return replaceNgsp(text).replace(WS_REPLACE_REGEXP, ' ');
}
export function removeWhitespaces(htmlAstWithErrors: ParseTreeResult): ParseTreeResult { export function removeWhitespaces(htmlAstWithErrors: ParseTreeResult): ParseTreeResult {
return new ParseTreeResult( return new ParseTreeResult(
html.visitAll(new WhitespaceVisitor(), htmlAstWithErrors.rootNodes), html.visitAll(new WhitespaceVisitor(), htmlAstWithErrors.rootNodes),

View File

@ -102,15 +102,14 @@ function _expandPluralForm(ast: html.Expansion, errors: ParseError[]): html.Elem
errors.push(...expansionResult.errors); errors.push(...expansionResult.errors);
return new html.Element( return new html.Element(
`ng-template`, `ng-template`, [new html.Attribute(
[new html.Attribute( 'ngPluralCase', `${c.value}`, c.valueSourceSpan, undefined /* keySpan */,
'ngPluralCase', `${c.value}`, c.valueSourceSpan, undefined /* keySpan */, undefined /* valueSpan */, undefined /* i18n */)],
undefined /* valueSpan */, undefined /* valueTokens */, undefined /* i18n */)],
expansionResult.nodes, c.sourceSpan, c.sourceSpan, c.sourceSpan); expansionResult.nodes, c.sourceSpan, c.sourceSpan, c.sourceSpan);
}); });
const switchAttr = new html.Attribute( const switchAttr = new html.Attribute(
'[ngPlural]', ast.switchValue, ast.switchValueSourceSpan, undefined /* keySpan */, '[ngPlural]', ast.switchValue, ast.switchValueSourceSpan, undefined /* keySpan */,
undefined /* valueSpan */, undefined /* valueTokens */, undefined /* i18n */); undefined /* valueSpan */, undefined /* i18n */);
return new html.Element( return new html.Element(
'ng-container', [switchAttr], children, ast.sourceSpan, ast.sourceSpan, ast.sourceSpan); 'ng-container', [switchAttr], children, ast.sourceSpan, ast.sourceSpan, ast.sourceSpan);
} }
@ -124,23 +123,21 @@ function _expandDefaultForm(ast: html.Expansion, errors: ParseError[]): html.Ele
if (c.value === 'other') { if (c.value === 'other') {
// other is the default case when no values match // other is the default case when no values match
return new html.Element( return new html.Element(
`ng-template`, `ng-template`, [new html.Attribute(
[new html.Attribute( 'ngSwitchDefault', '', c.valueSourceSpan, undefined /* keySpan */,
'ngSwitchDefault', '', c.valueSourceSpan, undefined /* keySpan */, undefined /* valueSpan */, undefined /* i18n */)],
undefined /* valueSpan */, undefined /* valueTokens */, undefined /* i18n */)],
expansionResult.nodes, c.sourceSpan, c.sourceSpan, c.sourceSpan); expansionResult.nodes, c.sourceSpan, c.sourceSpan, c.sourceSpan);
} }
return new html.Element( return new html.Element(
`ng-template`, `ng-template`, [new html.Attribute(
[new html.Attribute( 'ngSwitchCase', `${c.value}`, c.valueSourceSpan, undefined /* keySpan */,
'ngSwitchCase', `${c.value}`, c.valueSourceSpan, undefined /* keySpan */, undefined /* valueSpan */, undefined /* i18n */)],
undefined /* valueSpan */, undefined /* valueTokens */, undefined /* i18n */)],
expansionResult.nodes, c.sourceSpan, c.sourceSpan, c.sourceSpan); expansionResult.nodes, c.sourceSpan, c.sourceSpan, c.sourceSpan);
}); });
const switchAttr = new html.Attribute( const switchAttr = new html.Attribute(
'[ngSwitch]', ast.switchValue, ast.switchValueSourceSpan, undefined /* keySpan */, '[ngSwitch]', ast.switchValue, ast.switchValueSourceSpan, undefined /* keySpan */,
undefined /* valueSpan */, undefined /* valueTokens */, undefined /* i18n */); undefined /* valueSpan */, undefined /* i18n */);
return new html.Element( return new html.Element(
'ng-container', [switchAttr], children, ast.sourceSpan, ast.sourceSpan, ast.sourceSpan); 'ng-container', [switchAttr], children, ast.sourceSpan, ast.sourceSpan, ast.sourceSpan);
} }

View File

@ -216,7 +216,6 @@ class _TreeBuilder {
} }
private _consumeText(token: lex.Token) { private _consumeText(token: lex.Token) {
const tokens = [token];
const startSpan = token.sourceSpan; const startSpan = token.sourceSpan;
let text = token.parts[0]; let text = token.parts[0];
if (text.length > 0 && text[0] == '\n') { if (text.length > 0 && text[0] == '\n') {
@ -224,15 +223,14 @@ class _TreeBuilder {
if (parent != null && parent.children.length == 0 && if (parent != null && parent.children.length == 0 &&
this.getTagDefinition(parent.name).ignoreFirstLf) { this.getTagDefinition(parent.name).ignoreFirstLf) {
text = text.substring(1); text = text.substring(1);
tokens[0] = {type: token.type, sourceSpan: token.sourceSpan, parts: [text]};
} }
} }
// For now recombine text, interpolation and entity tokens
while (this._peek.type === lex.TokenType.INTERPOLATION || while (this._peek.type === lex.TokenType.INTERPOLATION ||
this._peek.type === lex.TokenType.TEXT || this._peek.type === lex.TokenType.TEXT ||
this._peek.type === lex.TokenType.ENCODED_ENTITY) { this._peek.type === lex.TokenType.ENCODED_ENTITY) {
token = this._advance(); token = this._advance();
tokens.push(token);
if (token.type === lex.TokenType.INTERPOLATION) { if (token.type === lex.TokenType.INTERPOLATION) {
// For backward compatibility we decode HTML entities that appear in interpolation // For backward compatibility we decode HTML entities that appear in interpolation
// expressions. This is arguably a bug, but it could be a considerable breaking change to // expressions. This is arguably a bug, but it could be a considerable breaking change to
@ -250,8 +248,8 @@ class _TreeBuilder {
const endSpan = token.sourceSpan; const endSpan = token.sourceSpan;
this._addToParent(new html.Text( this._addToParent(new html.Text(
text, text,
new ParseSourceSpan(startSpan.start, endSpan.end, startSpan.fullStart, startSpan.details), new ParseSourceSpan(
tokens)); startSpan.start, endSpan.end, startSpan.fullStart, startSpan.details)));
} }
} }
@ -374,17 +372,16 @@ class _TreeBuilder {
// Consume the attribute value // Consume the attribute value
let value = ''; let value = '';
const valueTokens: lex.Token[] = [];
let valueStartSpan: ParseSourceSpan|undefined = undefined; let valueStartSpan: ParseSourceSpan|undefined = undefined;
let valueEnd: ParseLocation|undefined = undefined; let valueEnd: ParseLocation|undefined = undefined;
if (this._peek.type === lex.TokenType.ATTR_VALUE_TEXT) { if (this._peek.type === lex.TokenType.ATTR_VALUE_TEXT) {
valueStartSpan = this._peek.sourceSpan; valueStartSpan = this._peek.sourceSpan;
valueEnd = this._peek.sourceSpan.end; valueEnd = this._peek.sourceSpan.end;
// For now recombine text, interpolation and entity tokens
while (this._peek.type === lex.TokenType.ATTR_VALUE_TEXT || while (this._peek.type === lex.TokenType.ATTR_VALUE_TEXT ||
this._peek.type === lex.TokenType.ATTR_VALUE_INTERPOLATION || this._peek.type === lex.TokenType.ATTR_VALUE_INTERPOLATION ||
this._peek.type === lex.TokenType.ENCODED_ENTITY) { this._peek.type === lex.TokenType.ENCODED_ENTITY) {
const valueToken = this._advance(); let valueToken = this._advance();
valueTokens.push(valueToken);
if (valueToken.type === lex.TokenType.ATTR_VALUE_INTERPOLATION) { if (valueToken.type === lex.TokenType.ATTR_VALUE_INTERPOLATION) {
// For backward compatibility we decode HTML entities that appear in interpolation // For backward compatibility we decode HTML entities that appear in interpolation
// expressions. This is arguably a bug, but it could be a considerable breaking change to // expressions. This is arguably a bug, but it could be a considerable breaking change to
@ -411,8 +408,7 @@ class _TreeBuilder {
return new html.Attribute( return new html.Attribute(
fullName, value, fullName, value,
new ParseSourceSpan(attrName.sourceSpan.start, attrEnd, attrName.sourceSpan.fullStart), new ParseSourceSpan(attrName.sourceSpan.start, attrEnd, attrName.sourceSpan.fullStart),
attrName.sourceSpan, valueSpan, valueTokens.length > 0 ? valueTokens : undefined, attrName.sourceSpan, valueSpan);
undefined);
} }
private _getParentElement(): html.Element|null { private _getParentElement(): html.Element|null {

View File

@ -52,16 +52,12 @@ class _Humanizer implements html.Visitor {
} }
visitAttribute(attribute: html.Attribute, context: any): any { visitAttribute(attribute: html.Attribute, context: any): any {
const valueTokens = attribute.valueTokens ?? []; const res = this._appendContext(attribute, [html.Attribute, attribute.name, attribute.value]);
const res = this._appendContext(attribute, [
html.Attribute, attribute.name, attribute.value, ...valueTokens.map(token => token.parts)
]);
this.result.push(res); this.result.push(res);
} }
visitText(text: html.Text, context: any): any { visitText(text: html.Text, context: any): any {
const res = this._appendContext( const res = this._appendContext(text, [html.Text, text.value, this.elDepth]);
text, [html.Text, text.value, this.elDepth, ...text.tokens.map(token => token.parts)]);
this.result.push(res); this.result.push(res);
} }

View File

@ -24,31 +24,31 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
describe('parse', () => { describe('parse', () => {
describe('text nodes', () => { describe('text nodes', () => {
it('should parse root level text nodes', () => { it('should parse root level text nodes', () => {
expect(humanizeDom(parser.parse('a', 'TestComp'))).toEqual([[html.Text, 'a', 0, ['a']]]); expect(humanizeDom(parser.parse('a', 'TestComp'))).toEqual([[html.Text, 'a', 0]]);
}); });
it('should parse text nodes inside regular elements', () => { it('should parse text nodes inside regular elements', () => {
expect(humanizeDom(parser.parse('<div>a</div>', 'TestComp'))).toEqual([ expect(humanizeDom(parser.parse('<div>a</div>', 'TestComp'))).toEqual([
[html.Element, 'div', 0], [html.Text, 'a', 1, ['a']] [html.Element, 'div', 0], [html.Text, 'a', 1]
]); ]);
}); });
it('should parse text nodes inside <ng-template> elements', () => { it('should parse text nodes inside <ng-template> elements', () => {
expect(humanizeDom(parser.parse('<ng-template>a</ng-template>', 'TestComp'))).toEqual([ expect(humanizeDom(parser.parse('<ng-template>a</ng-template>', 'TestComp'))).toEqual([
[html.Element, 'ng-template', 0], [html.Text, 'a', 1, ['a']] [html.Element, 'ng-template', 0], [html.Text, 'a', 1]
]); ]);
}); });
it('should parse CDATA', () => { it('should parse CDATA', () => {
expect(humanizeDom(parser.parse('<![CDATA[text]]>', 'TestComp'))).toEqual([ expect(humanizeDom(parser.parse('<![CDATA[text]]>', 'TestComp'))).toEqual([
[html.Text, 'text', 0, ['text']] [html.Text, 'text', 0]
]); ]);
}); });
it('should normalize line endings within CDATA', () => { it('should normalize line endings within CDATA', () => {
const parsed = parser.parse('<![CDATA[ line 1 \r\n line 2 ]]>', 'TestComp'); const parsed = parser.parse('<![CDATA[ line 1 \r\n line 2 ]]>', 'TestComp');
expect(humanizeDom(parsed)).toEqual([ expect(humanizeDom(parsed)).toEqual([
[html.Text, ' line 1 \n line 2 ', 0, [' line 1 \n line 2 ']], [html.Text, ' line 1 \n line 2 ', 0],
]); ]);
expect(parsed.errors).toEqual([]); expect(parsed.errors).toEqual([]);
}); });
@ -76,8 +76,8 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
expect(humanizeDom(parser.parse('<link rel="author license" href="/about">', 'TestComp'))) expect(humanizeDom(parser.parse('<link rel="author license" href="/about">', 'TestComp')))
.toEqual([ .toEqual([
[html.Element, 'link', 0], [html.Element, 'link', 0],
[html.Attribute, 'rel', 'author license', ['author license']], [html.Attribute, 'rel', 'author license'],
[html.Attribute, 'href', '/about', ['/about']], [html.Attribute, 'href', '/about'],
]); ]);
}); });
@ -106,9 +106,9 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
it('should close void elements on text nodes', () => { it('should close void elements on text nodes', () => {
expect(humanizeDom(parser.parse('<p>before<br>after</p>', 'TestComp'))).toEqual([ expect(humanizeDom(parser.parse('<p>before<br>after</p>', 'TestComp'))).toEqual([
[html.Element, 'p', 0], [html.Element, 'p', 0],
[html.Text, 'before', 1, ['before']], [html.Text, 'before', 1],
[html.Element, 'br', 1], [html.Element, 'br', 1],
[html.Text, 'after', 1, ['after']], [html.Text, 'after', 1],
]); ]);
}); });
@ -116,9 +116,9 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
expect(humanizeDom(parser.parse('<div><p>1<p>2</div>', 'TestComp'))).toEqual([ expect(humanizeDom(parser.parse('<div><p>1<p>2</div>', 'TestComp'))).toEqual([
[html.Element, 'div', 0], [html.Element, 'div', 0],
[html.Element, 'p', 1], [html.Element, 'p', 1],
[html.Text, '1', 2, ['1']], [html.Text, '1', 2],
[html.Element, 'p', 1], [html.Element, 'p', 1],
[html.Text, '2', 2, ['2']], [html.Text, '2', 2],
]); ]);
}); });
@ -200,12 +200,12 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
'TestComp'))) 'TestComp')))
.toEqual([ .toEqual([
[html.Element, 'p', 0], [html.Element, 'p', 0],
[html.Text, '\n', 1, ['\n']], [html.Text, '\n', 1],
[html.Element, 'textarea', 0], [html.Element, 'textarea', 0],
[html.Element, 'pre', 0], [html.Element, 'pre', 0],
[html.Text, '\n', 1, ['\n']], [html.Text, '\n', 1],
[html.Element, 'listing', 0], [html.Element, 'listing', 0],
[html.Text, '\n', 1, ['\n']], [html.Text, '\n', 1],
]); ]);
}); });
@ -214,28 +214,28 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
parsed = parser.parse('<title> line 1 \r\n line 2 </title>', 'TestComp'); parsed = parser.parse('<title> line 1 \r\n line 2 </title>', 'TestComp');
expect(humanizeDom(parsed)).toEqual([ expect(humanizeDom(parsed)).toEqual([
[html.Element, 'title', 0], [html.Element, 'title', 0],
[html.Text, ' line 1 \n line 2 ', 1, [' line 1 \n line 2 ']], [html.Text, ' line 1 \n line 2 ', 1],
]); ]);
expect(parsed.errors).toEqual([]); expect(parsed.errors).toEqual([]);
parsed = parser.parse('<script> line 1 \r\n line 2 </script>', 'TestComp'); parsed = parser.parse('<script> line 1 \r\n line 2 </script>', 'TestComp');
expect(humanizeDom(parsed)).toEqual([ expect(humanizeDom(parsed)).toEqual([
[html.Element, 'script', 0], [html.Element, 'script', 0],
[html.Text, ' line 1 \n line 2 ', 1, [' line 1 \n line 2 ']], [html.Text, ' line 1 \n line 2 ', 1],
]); ]);
expect(parsed.errors).toEqual([]); expect(parsed.errors).toEqual([]);
parsed = parser.parse('<div> line 1 \r\n line 2 </div>', 'TestComp'); parsed = parser.parse('<div> line 1 \r\n line 2 </div>', 'TestComp');
expect(humanizeDom(parsed)).toEqual([ expect(humanizeDom(parsed)).toEqual([
[html.Element, 'div', 0], [html.Element, 'div', 0],
[html.Text, ' line 1 \n line 2 ', 1, [' line 1 \n line 2 ']], [html.Text, ' line 1 \n line 2 ', 1],
]); ]);
expect(parsed.errors).toEqual([]); expect(parsed.errors).toEqual([]);
parsed = parser.parse('<span> line 1 \r\n line 2 </span>', 'TestComp'); parsed = parser.parse('<span> line 1 \r\n line 2 </span>', 'TestComp');
expect(humanizeDom(parsed)).toEqual([ expect(humanizeDom(parsed)).toEqual([
[html.Element, 'span', 0], [html.Element, 'span', 0],
[html.Text, ' line 1 \n line 2 ', 1, [' line 1 \n line 2 ']], [html.Text, ' line 1 \n line 2 ', 1],
]); ]);
expect(parsed.errors).toEqual([]); expect(parsed.errors).toEqual([]);
}); });
@ -245,22 +245,8 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
it('should parse attributes on regular elements case sensitive', () => { it('should parse attributes on regular elements case sensitive', () => {
expect(humanizeDom(parser.parse('<div kEy="v" key2=v2></div>', 'TestComp'))).toEqual([ expect(humanizeDom(parser.parse('<div kEy="v" key2=v2></div>', 'TestComp'))).toEqual([
[html.Element, 'div', 0], [html.Element, 'div', 0],
[html.Attribute, 'kEy', 'v', ['v']], [html.Attribute, 'kEy', 'v'],
[html.Attribute, 'key2', 'v2', ['v2']], [html.Attribute, 'key2', 'v2'],
]);
});
it('should parse attributes containing interpolation', () => {
expect(humanizeDom(parser.parse('<div foo="1{{message}}2"></div>', 'TestComp'))).toEqual([
[html.Element, 'div', 0],
[html.Attribute, 'foo', '1{{message}}2', ['1'], ['{{', 'message', '}}'], ['2']]
]);
});
it('should parse attributes containing encoded entities', () => {
expect(humanizeDom(parser.parse('<div foo="&amp;"></div>', 'TestComp'))).toEqual([
[html.Element, 'div', 0],
[html.Attribute, 'foo', '&', [''], ['&', '&amp;'], ['']],
]); ]);
}); });
@ -273,10 +259,7 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
html.Element, 'div', 0, '<div foo="{{&amp;}}"></div>', '<div foo="{{&amp;}}">', html.Element, 'div', 0, '<div foo="{{&amp;}}"></div>', '<div foo="{{&amp;}}">',
'</div>' '</div>'
], ],
[ [html.Attribute, 'foo', '{{&}}', 'foo="{{&amp;}}"']
html.Attribute, 'foo', '{{&}}', [''], ['{{', '&amp;', '}}'], [''],
'foo="{{&amp;}}"'
]
]); ]);
}); });
@ -285,7 +268,7 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
parser.parse('<div key=" \r\n line 1 \r\n line 2 "></div>', 'TestComp'); parser.parse('<div key=" \r\n line 1 \r\n line 2 "></div>', 'TestComp');
expect(humanizeDom(result)).toEqual([ expect(humanizeDom(result)).toEqual([
[html.Element, 'div', 0], [html.Element, 'div', 0],
[html.Attribute, 'key', ' \n line 1 \n line 2 ', [' \n line 1 \n line 2 ']], [html.Attribute, 'key', ' \n line 1 \n line 2 '],
]); ]);
expect(result.errors).toEqual([]); expect(result.errors).toEqual([]);
}); });
@ -300,7 +283,7 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
it('should parse attributes on svg elements case sensitive', () => { it('should parse attributes on svg elements case sensitive', () => {
expect(humanizeDom(parser.parse('<svg viewBox="0"></svg>', 'TestComp'))).toEqual([ expect(humanizeDom(parser.parse('<svg viewBox="0"></svg>', 'TestComp'))).toEqual([
[html.Element, ':svg:svg', 0], [html.Element, ':svg:svg', 0],
[html.Attribute, 'viewBox', '0', ['0']], [html.Attribute, 'viewBox', '0'],
]); ]);
}); });
@ -308,14 +291,14 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
expect(humanizeDom(parser.parse('<ng-template k="v"></ng-template>', 'TestComp'))) expect(humanizeDom(parser.parse('<ng-template k="v"></ng-template>', 'TestComp')))
.toEqual([ .toEqual([
[html.Element, 'ng-template', 0], [html.Element, 'ng-template', 0],
[html.Attribute, 'k', 'v', ['v']], [html.Attribute, 'k', 'v'],
]); ]);
}); });
it('should support namespace', () => { it('should support namespace', () => {
expect(humanizeDom(parser.parse('<svg:use xlink:href="Port" />', 'TestComp'))).toEqual([ expect(humanizeDom(parser.parse('<svg:use xlink:href="Port" />', 'TestComp'))).toEqual([
[html.Element, ':svg:use', 0], [html.Element, ':svg:use', 0],
[html.Attribute, ':xlink:href', 'Port', ['Port']], [html.Attribute, ':xlink:href', 'Port'],
]); ]);
}); });
}); });
@ -342,23 +325,23 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
expect(humanizeDom(parsed)).toEqual([ expect(humanizeDom(parsed)).toEqual([
[html.Element, 'div', 0], [html.Element, 'div', 0],
[html.Text, 'before', 1, ['before']], [html.Text, 'before', 1],
[html.Expansion, 'messages.length', 'plural', 1], [html.Expansion, 'messages.length', 'plural', 1],
[html.ExpansionCase, '=0', 2], [html.ExpansionCase, '=0', 2],
[html.ExpansionCase, '=1', 2], [html.ExpansionCase, '=1', 2],
[html.Text, 'after', 1, ['after']], [html.Text, 'after', 1],
]); ]);
const cases = (<any>parsed.rootNodes[0]).children[1].cases; const cases = (<any>parsed.rootNodes[0]).children[1].cases;
expect(humanizeDom(new ParseTreeResult(cases[0].expression, []))).toEqual([ expect(humanizeDom(new ParseTreeResult(cases[0].expression, []))).toEqual([
[html.Text, 'You have ', 0, ['You have ']], [html.Text, 'You have ', 0],
[html.Element, 'b', 0], [html.Element, 'b', 0],
[html.Text, 'no', 1, ['no']], [html.Text, 'no', 1],
[html.Text, ' messages', 0, [' messages']], [html.Text, ' messages', 0],
]); ]);
expect(humanizeDom(new ParseTreeResult(cases[1].expression, []))).toEqual([ expect(humanizeDom(new ParseTreeResult(cases[1].expression, []))).toEqual([
[html.Text, 'One {{message}}', 0, ['One '], ['{{', 'message', '}}'], ['']] [html.Text, 'One {{message}}', 0]
]); ]);
}); });
@ -380,20 +363,20 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
expect(humanizeDom(parsed)).toEqual([ expect(humanizeDom(parsed)).toEqual([
[html.Element, 'div', 0], [html.Element, 'div', 0],
[html.Text, '\n ', 1, ['\n ']], [html.Text, '\n ', 1],
[html.Expansion, '\n messages.length', 'plural', 1], [html.Expansion, '\n messages.length', 'plural', 1],
[html.ExpansionCase, '=0', 2], [html.ExpansionCase, '=0', 2],
[html.ExpansionCase, '=1', 2], [html.ExpansionCase, '=1', 2],
[html.Text, '\n', 1, ['\n']], [html.Text, '\n', 1],
]); ]);
const cases = (<any>parsed.rootNodes[0]).children[1].cases; const cases = (<any>parsed.rootNodes[0]).children[1].cases;
expect(humanizeDom(new ParseTreeResult(cases[0].expression, []))).toEqual([ expect(humanizeDom(new ParseTreeResult(cases[0].expression, []))).toEqual([
[html.Text, 'You have \nno\n messages', 0, ['You have \nno\n messages']], [html.Text, 'You have \nno\n messages', 0],
]); ]);
expect(humanizeDom(new ParseTreeResult(cases[1].expression, []))).toEqual([ expect(humanizeDom(new ParseTreeResult(cases[1].expression, []))).toEqual([
[html.Text, 'One {{message}}', 0, ['One '], ['{{', 'message', '}}'], ['']] [html.Text, 'One {{message}}', 0]
]); ]);
expect(parsed.errors).toEqual([]); expect(parsed.errors).toEqual([]);
@ -413,20 +396,20 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
expect(humanizeDom(parsed)).toEqual([ expect(humanizeDom(parsed)).toEqual([
[html.Element, 'div', 0], [html.Element, 'div', 0],
[html.Text, '\n ', 1, ['\n ']], [html.Text, '\n ', 1],
[html.Expansion, '\r\n messages.length', 'plural', 1], [html.Expansion, '\r\n messages.length', 'plural', 1],
[html.ExpansionCase, '=0', 2], [html.ExpansionCase, '=0', 2],
[html.ExpansionCase, '=1', 2], [html.ExpansionCase, '=1', 2],
[html.Text, '\n', 1, ['\n']], [html.Text, '\n', 1],
]); ]);
const cases = (<any>parsed.rootNodes[0]).children[1].cases; const cases = (<any>parsed.rootNodes[0]).children[1].cases;
expect(humanizeDom(new ParseTreeResult(cases[0].expression, []))).toEqual([ expect(humanizeDom(new ParseTreeResult(cases[0].expression, []))).toEqual([
[html.Text, 'You have \nno\n messages', 0, ['You have \nno\n messages']], [html.Text, 'You have \nno\n messages', 0],
]); ]);
expect(humanizeDom(new ParseTreeResult(cases[1].expression, []))).toEqual([ expect(humanizeDom(new ParseTreeResult(cases[1].expression, []))).toEqual([
[html.Text, 'One {{message}}', 0, ['One '], ['{{', 'message', '}}'], ['']] [html.Text, 'One {{message}}', 0]
]); ]);
expect(parsed.errors).toEqual([]); expect(parsed.errors).toEqual([]);
@ -450,20 +433,20 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
expect(humanizeDom(parsed)).toEqual([ expect(humanizeDom(parsed)).toEqual([
[html.Element, 'div', 0], [html.Element, 'div', 0],
[html.Text, '\n ', 1, ['\n ']], [html.Text, '\n ', 1],
[html.Expansion, '\n messages.length', 'plural', 1], [html.Expansion, '\n messages.length', 'plural', 1],
[html.ExpansionCase, '=0', 2], [html.ExpansionCase, '=0', 2],
[html.ExpansionCase, '=1', 2], [html.ExpansionCase, '=1', 2],
[html.Text, '\n', 1, ['\n']], [html.Text, '\n', 1],
]); ]);
const cases = (<any>parsed.rootNodes[0]).children[1].cases; const cases = (<any>parsed.rootNodes[0]).children[1].cases;
expect(humanizeDom(new ParseTreeResult(cases[0].expression, []))).toEqual([ expect(humanizeDom(new ParseTreeResult(cases[0].expression, []))).toEqual([
[html.Text, 'You have \nno\n messages', 0, ['You have \nno\n messages']], [html.Text, 'You have \nno\n messages', 0],
]); ]);
expect(humanizeDom(new ParseTreeResult(cases[1].expression, []))).toEqual([ expect(humanizeDom(new ParseTreeResult(cases[1].expression, []))).toEqual([
[html.Text, 'One {{message}}', 0, ['One '], ['{{', 'message', '}}'], ['']] [html.Text, 'One {{message}}', 0]
]); ]);
expect(parsed.errors).toEqual([]); expect(parsed.errors).toEqual([]);
@ -483,20 +466,20 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
expect(humanizeDom(parsed)).toEqual([ expect(humanizeDom(parsed)).toEqual([
[html.Element, 'div', 0], [html.Element, 'div', 0],
[html.Text, '\n ', 1, ['\n ']], [html.Text, '\n ', 1],
[html.Expansion, '\r\n messages.length', 'plural', 1], [html.Expansion, '\r\n messages.length', 'plural', 1],
[html.ExpansionCase, '=0', 2], [html.ExpansionCase, '=0', 2],
[html.ExpansionCase, '=1', 2], [html.ExpansionCase, '=1', 2],
[html.Text, '\n', 1, ['\n']], [html.Text, '\n', 1],
]); ]);
const cases = (<any>parsed.rootNodes[0]).children[1].cases; const cases = (<any>parsed.rootNodes[0]).children[1].cases;
expect(humanizeDom(new ParseTreeResult(cases[0].expression, []))).toEqual([ expect(humanizeDom(new ParseTreeResult(cases[0].expression, []))).toEqual([
[html.Text, 'You have \nno\n messages', 0, ['You have \nno\n messages']], [html.Text, 'You have \nno\n messages', 0],
]); ]);
expect(humanizeDom(new ParseTreeResult(cases[1].expression, []))).toEqual([ expect(humanizeDom(new ParseTreeResult(cases[1].expression, []))).toEqual([
[html.Text, 'One {{message}}', 0, ['One '], ['{{', 'message', '}}'], ['']] [html.Text, 'One {{message}}', 0]
]); ]);
expect(parsed.errors).toEqual([]); expect(parsed.errors).toEqual([]);
@ -529,7 +512,7 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
expect(humanizeDom(new ParseTreeResult(firstCase.expression, []))).toEqual([ expect(humanizeDom(new ParseTreeResult(firstCase.expression, []))).toEqual([
[html.Expansion, 'p.gender', 'select', 0], [html.Expansion, 'p.gender', 'select', 0],
[html.ExpansionCase, 'male', 1], [html.ExpansionCase, 'male', 1],
[html.Text, ' ', 0, [' ']], [html.Text, ' ', 0],
]); ]);
}); });
@ -557,10 +540,10 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
const expansion = parsed.rootNodes[0] as html.Expansion; const expansion = parsed.rootNodes[0] as html.Expansion;
expect(humanizeDom(new ParseTreeResult(expansion.cases[0].expression, []))).toEqual([ expect(humanizeDom(new ParseTreeResult(expansion.cases[0].expression, []))).toEqual([
[html.Text, 'zero \n ', 0, ['zero \n ']], [html.Text, 'zero \n ', 0],
[html.Expansion, '\n p.gender', 'select', 0], [html.Expansion, '\n p.gender', 'select', 0],
[html.ExpansionCase, 'male', 1], [html.ExpansionCase, 'male', 1],
[html.Text, '\n ', 0, ['\n ']], [html.Text, '\n ', 0],
]); ]);
expect(parsed.errors).toEqual([]); expect(parsed.errors).toEqual([]);
@ -586,10 +569,10 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
const expansion = parsed.rootNodes[0] as html.Expansion; const expansion = parsed.rootNodes[0] as html.Expansion;
expect(humanizeDom(new ParseTreeResult(expansion.cases[0].expression, []))).toEqual([ expect(humanizeDom(new ParseTreeResult(expansion.cases[0].expression, []))).toEqual([
[html.Text, 'zero \n ', 0, ['zero \n ']], [html.Text, 'zero \n ', 0],
[html.Expansion, '\r\n p.gender', 'select', 0], [html.Expansion, '\r\n p.gender', 'select', 0],
[html.ExpansionCase, 'male', 1], [html.ExpansionCase, 'male', 1],
[html.Text, '\n ', 0, ['\n ']], [html.Text, '\n ', 0],
]); ]);
expect(parsed.errors).toEqual([]); expect(parsed.errors).toEqual([]);
@ -615,10 +598,10 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
const expansion = parsed.rootNodes[0] as html.Expansion; const expansion = parsed.rootNodes[0] as html.Expansion;
expect(humanizeDom(new ParseTreeResult(expansion.cases[0].expression, []))).toEqual([ expect(humanizeDom(new ParseTreeResult(expansion.cases[0].expression, []))).toEqual([
[html.Text, 'zero \n ', 0, ['zero \n ']], [html.Text, 'zero \n ', 0],
[html.Expansion, '\r\n p.gender', 'select', 0], [html.Expansion, '\r\n p.gender', 'select', 0],
[html.ExpansionCase, 'male', 1], [html.ExpansionCase, 'male', 1],
[html.Text, '\n ', 0, ['\n ']], [html.Text, '\n ', 0],
]); ]);
@ -687,11 +670,11 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
'<div [prop]="v1" (e)="do()" attr="v2" noValue>\na\n</div>', '<div [prop]="v1" (e)="do()" attr="v2" noValue>\na\n</div>',
'<div [prop]="v1" (e)="do()" attr="v2" noValue>', '</div>' '<div [prop]="v1" (e)="do()" attr="v2" noValue>', '</div>'
], ],
[html.Attribute, '[prop]', 'v1', ['v1'], '[prop]="v1"'], [html.Attribute, '[prop]', 'v1', '[prop]="v1"'],
[html.Attribute, '(e)', 'do()', ['do()'], '(e)="do()"'], [html.Attribute, '(e)', 'do()', '(e)="do()"'],
[html.Attribute, 'attr', 'v2', ['v2'], 'attr="v2"'], [html.Attribute, 'attr', 'v2', 'attr="v2"'],
[html.Attribute, 'noValue', '', 'noValue'], [html.Attribute, 'noValue', '', 'noValue'],
[html.Text, '\na\n', 1, ['\na\n'], '\na\n'], [html.Text, '\na\n', 1, '\na\n'],
]); ]);
}); });
@ -712,9 +695,7 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
'{{&amp;}}' + '{{&amp;}}' +
'{{&#x25BE;}}' + '{{&#x25BE;}}' +
'{{&#9662;}}' + '{{&#9662;}}' +
'{{&unknown;}}' +
'{{&amp (no semi-colon)}}' + '{{&amp (no semi-colon)}}' +
'{{&#xyz; (invalid hex)}}' +
'{{&#25BE; (invalid decimal)}}', '{{&#25BE; (invalid decimal)}}',
'TestComp'))) 'TestComp')))
.toEqual([[ .toEqual([[
@ -722,48 +703,17 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
'{{&}}' + '{{&}}' +
'{{\u25BE}}' + '{{\u25BE}}' +
'{{\u25BE}}' + '{{\u25BE}}' +
'{{&unknown;}}' +
'{{&amp (no semi-colon)}}' + '{{&amp (no semi-colon)}}' +
'{{&#xyz; (invalid hex)}}' +
'{{&#25BE; (invalid decimal)}}', '{{&#25BE; (invalid decimal)}}',
0, 0,
[''],
['{{', '&amp;', '}}'],
[''],
['{{', '&#x25BE;', '}}'],
[''],
['{{', '&#9662;', '}}'],
[''],
['{{', '&unknown;', '}}'],
[''],
['{{', '&amp (no semi-colon)', '}}'],
[''],
['{{', '&#xyz; (invalid hex)', '}}'],
[''],
['{{', '&#25BE; (invalid decimal)', '}}'],
[''],
'{{&amp;}}' + '{{&amp;}}' +
'{{&#x25BE;}}' + '{{&#x25BE;}}' +
'{{&#9662;}}' + '{{&#9662;}}' +
'{{&unknown;}}' +
'{{&amp (no semi-colon)}}' + '{{&amp (no semi-colon)}}' +
'{{&#xyz; (invalid hex)}}' +
'{{&#25BE; (invalid decimal)}}', '{{&#25BE; (invalid decimal)}}',
]]); ]]);
}); });
it('should support interpolations in text', () => {
expect(
humanizeDomSourceSpans(parser.parse('<div> pre {{ value }} post </div>', 'TestComp')))
.toEqual([
[html.Element, 'div', 0, '<div> pre {{ value }} post </div>', '<div>', '</div>'],
[
html.Text, ' pre {{ value }} post ', 1, [' pre '], ['{{', ' value ', '}}'],
[' post '], ' pre {{ value }} post '
],
]);
});
it('should not set the end source span for void elements', () => { it('should not set the end source span for void elements', () => {
expect(humanizeDomSourceSpans(parser.parse('<div><br></div>', 'TestComp'))).toEqual([ expect(humanizeDomSourceSpans(parser.parse('<div><br></div>', 'TestComp'))).toEqual([
[html.Element, 'div', 0, '<div><br></div>', '<div>', '</div>'], [html.Element, 'div', 0, '<div><br></div>', '<div>', '</div>'],
@ -808,10 +758,10 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
html.Element, 'input', 0, '<input type="text" />', '<input type="text" />', html.Element, 'input', 0, '<input type="text" />', '<input type="text" />',
'<input type="text" />' '<input type="text" />'
], ],
[html.Attribute, 'type', 'text', ['text'], 'type="text"'], [html.Attribute, 'type', 'text', 'type="text"'],
[html.Text, '\n\n\n ', 0, ['\n\n\n '], ''], [html.Text, '\n\n\n ', 0, ''],
[html.Element, 'span', 0, '<span>\n</span>', '<span>', '</span>'], [html.Element, 'span', 0, '<span>\n</span>', '<span>', '</span>'],
[html.Text, '\n', 1, ['\n'], ''], [html.Text, '\n', 1, ''],
]); ]);
}); });
@ -824,9 +774,9 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
.toEqual([ .toEqual([
[html.Element, 'div', 0, '<div><li>A<li>B</div>', '<div>', '</div>'], [html.Element, 'div', 0, '<div><li>A<li>B</div>', '<div>', '</div>'],
[html.Element, 'li', 1, '<li>', '<li>', null], [html.Element, 'li', 1, '<li>', '<li>', null],
[html.Text, 'A', 2, ['A'], 'A'], [html.Text, 'A', 2, 'A'],
[html.Element, 'li', 1, '<li>', '<li>', null], [html.Element, 'li', 1, '<li>', '<li>', null],
[html.Text, 'B', 2, ['B'], 'B'], [html.Text, 'B', 2, 'B'],
]); ]);
}); });
@ -864,7 +814,7 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
describe('visitor', () => { describe('visitor', () => {
it('should visit text nodes', () => { it('should visit text nodes', () => {
const result = humanizeDom(parser.parse('text', 'TestComp')); const result = humanizeDom(parser.parse('text', 'TestComp'));
expect(result).toEqual([[html.Text, 'text', 0, ['text']]]); expect(result).toEqual([[html.Text, 'text', 0]]);
}); });
it('should visit element nodes', () => { it('should visit element nodes', () => {
@ -874,7 +824,7 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
it('should visit attribute nodes', () => { it('should visit attribute nodes', () => {
const result = humanizeDom(parser.parse('<div id="foo"></div>', 'TestComp')); const result = humanizeDom(parser.parse('<div id="foo"></div>', 'TestComp'));
expect(result).toContain([html.Attribute, 'id', 'foo', ['foo']]); expect(result).toContain([html.Attribute, 'id', 'foo']);
}); });
it('should visit all nodes', () => { it('should visit all nodes', () => {
@ -982,7 +932,7 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
expect(humanizeNodes(rootNodes, true)).toEqual([ expect(humanizeNodes(rootNodes, true)).toEqual([
[html.Element, 'div', 0, '<div class="hi" sty', '<div class="hi" sty', null], [html.Element, 'div', 0, '<div class="hi" sty', '<div class="hi" sty', null],
[html.Attribute, 'class', 'hi', ['hi'], 'class="hi"'], [html.Attribute, 'class', 'hi', 'class="hi"'],
[html.Attribute, 'sty', '', 'sty'], [html.Attribute, 'sty', '', 'sty'],
[html.Element, 'span', 0, '<span></span>', '<span>', '</span>'], [html.Element, 'span', 0, '<span></span>', '<span>', '</span>'],
]); ]);
@ -997,7 +947,7 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
expect(humanizeNodes(rootNodes, true)).toEqual([ expect(humanizeNodes(rootNodes, true)).toEqual([
[html.Element, 'div', 0, '<div ', '<div ', null], [html.Element, 'div', 0, '<div ', '<div ', null],
[html.Text, '"', 0, ['"'], '"'], [html.Text, '"', 0, '"'],
[html.Element, 'span', 0, '<span></span>', '<span>', '</span>'], [html.Element, 'span', 0, '<span></span>', '<span>', '</span>'],
]); ]);

View File

@ -7,7 +7,6 @@
*/ */
import * as html from '../../src/ml_parser/ast'; import * as html from '../../src/ml_parser/ast';
import {NGSP_UNICODE} from '../../src/ml_parser/entities';
import {HtmlParser} from '../../src/ml_parser/html_parser'; import {HtmlParser} from '../../src/ml_parser/html_parser';
import {PRESERVE_WS_ATTR_NAME, removeWhitespaces} from '../../src/ml_parser/html_whitespaces'; import {PRESERVE_WS_ATTR_NAME, removeWhitespaces} from '../../src/ml_parser/html_whitespaces';
import {TokenizeOptions} from '../../src/ml_parser/lexer'; import {TokenizeOptions} from '../../src/ml_parser/lexer';
@ -53,86 +52,48 @@ import {humanizeDom} from './ast_spec_utils';
expect(parseAndRemoveWS('<div><span>foo</span>&ngsp;<span>bar</span></div>')).toEqual([ expect(parseAndRemoveWS('<div><span>foo</span>&ngsp;<span>bar</span></div>')).toEqual([
[html.Element, 'div', 0], [html.Element, 'div', 0],
[html.Element, 'span', 1], [html.Element, 'span', 1],
[html.Text, 'foo', 2, ['foo']], [html.Text, 'foo', 2],
[html.Text, ' ', 1, [''], [NGSP_UNICODE, '&ngsp;'], ['']], [html.Text, ' ', 1],
[html.Element, 'span', 1], [html.Element, 'span', 1],
[html.Text, 'bar', 2, ['bar']], [html.Text, 'bar', 2],
]); ]);
}); });
it('should replace multiple whitespaces with one space', () => { it('should replace multiple whitespaces with one space', () => {
expect(parseAndRemoveWS('\n\n\nfoo\t\t\t')).toEqual([[html.Text, ' foo ', 0, [' foo ']]]); expect(parseAndRemoveWS('\n\n\nfoo\t\t\t')).toEqual([[html.Text, ' foo ', 0]]);
expect(parseAndRemoveWS(' \n foo \t ')).toEqual([[html.Text, ' foo ', 0, [' foo ']]]); expect(parseAndRemoveWS(' \n foo \t ')).toEqual([[html.Text, ' foo ', 0]]);
}); });
it('should not replace &nbsp;', () => { it('should not replace &nbsp;', () => {
expect(parseAndRemoveWS('&nbsp;')).toEqual([ expect(parseAndRemoveWS('&nbsp;')).toEqual([[html.Text, '\u00a0', 0]]);
[html.Text, '\u00a0', 0, [''], ['\u00a0', '&nbsp;'], ['']]
]);
}); });
it('should not replace sequences of &nbsp;', () => { it('should not replace sequences of &nbsp;', () => {
expect(parseAndRemoveWS('&nbsp;&nbsp;foo&nbsp;&nbsp;')).toEqual([[ expect(parseAndRemoveWS('&nbsp;&nbsp;foo&nbsp;&nbsp;')).toEqual([
html.Text, [html.Text, '\u00a0\u00a0foo\u00a0\u00a0', 0]
'\u00a0\u00a0foo\u00a0\u00a0', ]);
0,
[''],
['\u00a0', '&nbsp;'],
[''],
['\u00a0', '&nbsp;'],
['foo'],
['\u00a0', '&nbsp;'],
[''],
['\u00a0', '&nbsp;'],
[''],
]]);
}); });
it('should not replace single tab and newline with spaces', () => { it('should not replace single tab and newline with spaces', () => {
expect(parseAndRemoveWS('\nfoo')).toEqual([[html.Text, '\nfoo', 0, ['\nfoo']]]); expect(parseAndRemoveWS('\nfoo')).toEqual([[html.Text, '\nfoo', 0]]);
expect(parseAndRemoveWS('\tfoo')).toEqual([[html.Text, '\tfoo', 0, ['\tfoo']]]); expect(parseAndRemoveWS('\tfoo')).toEqual([[html.Text, '\tfoo', 0]]);
}); });
it('should preserve single whitespaces between interpolations', () => { it('should preserve single whitespaces between interpolations', () => {
expect(parseAndRemoveWS(`{{fooExp}} {{barExp}}`)).toEqual([[ expect(parseAndRemoveWS(`{{fooExp}} {{barExp}}`)).toEqual([
html.Text, [html.Text, '{{fooExp}} {{barExp}}', 0],
'{{fooExp}} {{barExp}}', ]);
0,
[''],
['{{', 'fooExp', '}}'],
[' '],
['{{', 'barExp', '}}'],
[''],
]]);
expect(parseAndRemoveWS(`{{fooExp}}\t{{barExp}}`)).toEqual([ expect(parseAndRemoveWS(`{{fooExp}}\t{{barExp}}`)).toEqual([
[ [html.Text, '{{fooExp}}\t{{barExp}}', 0],
html.Text,
'{{fooExp}}\t{{barExp}}',
0,
[''],
['{{', 'fooExp', '}}'],
['\t'],
['{{', 'barExp', '}}'],
[''],
],
]); ]);
expect(parseAndRemoveWS(`{{fooExp}}\n{{barExp}}`)).toEqual([ expect(parseAndRemoveWS(`{{fooExp}}\n{{barExp}}`)).toEqual([
[ [html.Text, '{{fooExp}}\n{{barExp}}', 0],
html.Text,
'{{fooExp}}\n{{barExp}}',
0,
[''],
['{{', 'fooExp', '}}'],
['\n'],
['{{', 'barExp', '}}'],
[''],
],
]); ]);
}); });
it('should preserve whitespaces around interpolations', () => { it('should preserve whitespaces around interpolations', () => {
expect(parseAndRemoveWS(` {{exp}} `)).toEqual([ expect(parseAndRemoveWS(` {{exp}} `)).toEqual([
[html.Text, ' {{exp}} ', 0, [' '], ['{{', 'exp', '}}'], [' ']] [html.Text, ' {{exp}} ', 0],
]); ]);
}); });
@ -140,10 +101,10 @@ import {humanizeDom} from './ast_spec_utils';
expect(parseAndRemoveWS(`<span> {a, b, =4 {c}} </span>`, {tokenizeExpansionForms: true})) expect(parseAndRemoveWS(`<span> {a, b, =4 {c}} </span>`, {tokenizeExpansionForms: true}))
.toEqual([ .toEqual([
[html.Element, 'span', 0], [html.Element, 'span', 0],
[html.Text, ' ', 1, [' ']], [html.Text, ' ', 1],
[html.Expansion, 'a', 'b', 1], [html.Expansion, 'a', 'b', 1],
[html.ExpansionCase, '=4', 2], [html.ExpansionCase, '=4', 2],
[html.Text, ' ', 1, [' ']], [html.Text, ' ', 1],
]); ]);
}); });
@ -151,17 +112,17 @@ import {humanizeDom} from './ast_spec_utils';
expect(parseAndRemoveWS(`<pre><strong>foo</strong>\n<strong>bar</strong></pre>`)).toEqual([ expect(parseAndRemoveWS(`<pre><strong>foo</strong>\n<strong>bar</strong></pre>`)).toEqual([
[html.Element, 'pre', 0], [html.Element, 'pre', 0],
[html.Element, 'strong', 1], [html.Element, 'strong', 1],
[html.Text, 'foo', 2, ['foo']], [html.Text, 'foo', 2],
[html.Text, '\n', 1, ['\n']], [html.Text, '\n', 1],
[html.Element, 'strong', 1], [html.Element, 'strong', 1],
[html.Text, 'bar', 2, ['bar']], [html.Text, 'bar', 2],
]); ]);
}); });
it('should skip whitespace trimming in <textarea>', () => { it('should skip whitespace trimming in <textarea>', () => {
expect(parseAndRemoveWS(`<textarea>foo\n\n bar</textarea>`)).toEqual([ expect(parseAndRemoveWS(`<textarea>foo\n\n bar</textarea>`)).toEqual([
[html.Element, 'textarea', 0], [html.Element, 'textarea', 0],
[html.Text, 'foo\n\n bar', 1, ['foo\n\n bar']], [html.Text, 'foo\n\n bar', 1],
]); ]);
}); });
@ -170,7 +131,7 @@ import {humanizeDom} from './ast_spec_utils';
expect(parseAndRemoveWS(`<div ${PRESERVE_WS_ATTR_NAME}><img> <img></div>`)).toEqual([ expect(parseAndRemoveWS(`<div ${PRESERVE_WS_ATTR_NAME}><img> <img></div>`)).toEqual([
[html.Element, 'div', 0], [html.Element, 'div', 0],
[html.Element, 'img', 1], [html.Element, 'img', 1],
[html.Text, ' ', 1, [' ']], [html.Text, ' ', 1],
[html.Element, 'img', 1], [html.Element, 'img', 1],
]); ]);
}); });

View File

@ -29,9 +29,9 @@ import {humanizeNodes} from './ast_spec_utils';
[html.Attribute, '[ngPlural]', 'messages.length'], [html.Attribute, '[ngPlural]', 'messages.length'],
[html.Element, 'ng-template', 1], [html.Element, 'ng-template', 1],
[html.Attribute, 'ngPluralCase', '=0'], [html.Attribute, 'ngPluralCase', '=0'],
[html.Text, 'zero', 2, ['zero']], [html.Text, 'zero', 2],
[html.Element, 'b', 2], [html.Element, 'b', 2],
[html.Text, 'bold', 3, ['bold']], [html.Text, 'bold', 3],
]); ]);
}); });
@ -47,8 +47,8 @@ import {humanizeNodes} from './ast_spec_utils';
[html.Attribute, '[ngSwitch]', 'p.gender'], [html.Attribute, '[ngSwitch]', 'p.gender'],
[html.Element, 'ng-template', 3], [html.Element, 'ng-template', 3],
[html.Attribute, 'ngSwitchCase', 'male'], [html.Attribute, 'ngSwitchCase', 'male'],
[html.Text, 'm', 4, ['m']], [html.Text, 'm', 4],
[html.Text, ' ', 2, [' ']], [html.Text, ' ', 2],
]); ]);
}); });
@ -88,10 +88,10 @@ import {humanizeNodes} from './ast_spec_utils';
[html.Attribute, '[ngSwitch]', 'person.gender'], [html.Attribute, '[ngSwitch]', 'person.gender'],
[html.Element, 'ng-template', 1], [html.Element, 'ng-template', 1],
[html.Attribute, 'ngSwitchCase', 'male'], [html.Attribute, 'ngSwitchCase', 'male'],
[html.Text, 'm', 2, ['m']], [html.Text, 'm', 2],
[html.Element, 'ng-template', 1], [html.Element, 'ng-template', 1],
[html.Attribute, 'ngSwitchDefault', ''], [html.Attribute, 'ngSwitchDefault', ''],
[html.Text, 'default', 2, ['default']], [html.Text, 'default', 2],
]); ]);
}); });
@ -105,7 +105,7 @@ import {humanizeNodes} from './ast_spec_utils';
[html.Attribute, '[ngSwitch]', 'a'], [html.Attribute, '[ngSwitch]', 'a'],
[html.Element, 'ng-template', 3], [html.Element, 'ng-template', 3],
[html.Attribute, 'ngSwitchCase', '=4'], [html.Attribute, 'ngSwitchCase', '=4'],
[html.Text, 'c', 4, ['c']], [html.Text, 'c', 4],
]); ]);
}); });