diff --git a/packages/compiler/src/i18n/extractor_merger.ts b/packages/compiler/src/i18n/extractor_merger.ts index 7bb07f9b7b..b0da1c372b 100644 --- a/packages/compiler/src/i18n/extractor_merger.ts +++ b/packages/compiler/src/i18n/extractor_merger.ts @@ -396,12 +396,12 @@ class _Visitor implements html.Visitor { if (nodes.length == 0) { translatedAttributes.push(new html.Attribute( attr.name, '', attr.sourceSpan, undefined /* keySpan */, undefined /* valueSpan */, - undefined /* valueTokens */, undefined /* i18n */)); + undefined /* i18n */)); } else if (nodes[0] instanceof html.Text) { const value = (nodes[0] as html.Text).value; translatedAttributes.push(new html.Attribute( attr.name, value, attr.sourceSpan, undefined /* keySpan */, - undefined /* valueSpan */, undefined /* valueTokens */, undefined /* i18n */)); + undefined /* valueSpan */, undefined /* i18n */)); } else { this._reportError( el, diff --git a/packages/compiler/src/ml_parser/ast.ts b/packages/compiler/src/ml_parser/ast.ts index a8abcc80a6..7b71ffe9af 100644 --- a/packages/compiler/src/ml_parser/ast.ts +++ b/packages/compiler/src/ml_parser/ast.ts @@ -9,7 +9,6 @@ import {AstPath} from '../ast_path'; import {I18nMeta} from '../i18n/i18n_ast'; import {ParseSourceSpan} from '../parse_util'; -import {Token} from './lexer'; interface BaseNode { sourceSpan: ParseSourceSpan; @@ -24,8 +23,7 @@ export abstract class NodeWithI18n implements BaseNode { } export class Text extends NodeWithI18n { - constructor( - public value: string, sourceSpan: ParseSourceSpan, public tokens: Token[], i18n?: I18nMeta) { + constructor(public value: string, sourceSpan: ParseSourceSpan, i18n?: I18nMeta) { super(sourceSpan, i18n); } override visit(visitor: Visitor, context: any): any { @@ -57,8 +55,8 @@ export class ExpansionCase implements BaseNode { export class Attribute extends NodeWithI18n { constructor( public name: string, public value: string, sourceSpan: ParseSourceSpan, - readonly keySpan: ParseSourceSpan|undefined, public valueSpan: ParseSourceSpan|undefined, - public valueTokens: Token[]|undefined, i18n: I18nMeta|undefined) { + readonly keySpan: ParseSourceSpan|undefined, public valueSpan?: ParseSourceSpan, + i18n?: I18nMeta) { super(sourceSpan, i18n); } override visit(visitor: Visitor, context: any): any { diff --git a/packages/compiler/src/ml_parser/html_whitespaces.ts b/packages/compiler/src/ml_parser/html_whitespaces.ts index 57045ee8dc..f416523476 100644 --- a/packages/compiler/src/ml_parser/html_whitespaces.ts +++ b/packages/compiler/src/ml_parser/html_whitespaces.ts @@ -8,7 +8,6 @@ import * as html from './ast'; import {NGSP_UNICODE} from './entities'; -import {Token, TokenType} from './lexer'; import {ParseTreeResult} from './parser'; 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); if (isNotBlank || hasExpansionSibling) { - // Process the whitespace in the tokens of this Text node - const tokens = text.tokens.map( - 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 new html.Text( + replaceNgsp(text.value).replace(WS_REPLACE_REGEXP, ' '), text.sourceSpan, text.i18n); } 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 { return new ParseTreeResult( html.visitAll(new WhitespaceVisitor(), htmlAstWithErrors.rootNodes), diff --git a/packages/compiler/src/ml_parser/icu_ast_expander.ts b/packages/compiler/src/ml_parser/icu_ast_expander.ts index f52affd590..9e08c954dd 100644 --- a/packages/compiler/src/ml_parser/icu_ast_expander.ts +++ b/packages/compiler/src/ml_parser/icu_ast_expander.ts @@ -102,15 +102,14 @@ function _expandPluralForm(ast: html.Expansion, errors: ParseError[]): html.Elem errors.push(...expansionResult.errors); return new html.Element( - `ng-template`, - [new html.Attribute( - 'ngPluralCase', `${c.value}`, c.valueSourceSpan, undefined /* keySpan */, - undefined /* valueSpan */, undefined /* valueTokens */, undefined /* i18n */)], + `ng-template`, [new html.Attribute( + 'ngPluralCase', `${c.value}`, c.valueSourceSpan, undefined /* keySpan */, + undefined /* valueSpan */, undefined /* i18n */)], expansionResult.nodes, c.sourceSpan, c.sourceSpan, c.sourceSpan); }); const switchAttr = new html.Attribute( '[ngPlural]', ast.switchValue, ast.switchValueSourceSpan, undefined /* keySpan */, - undefined /* valueSpan */, undefined /* valueTokens */, undefined /* i18n */); + undefined /* valueSpan */, undefined /* i18n */); return new html.Element( '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') { // other is the default case when no values match return new html.Element( - `ng-template`, - [new html.Attribute( - 'ngSwitchDefault', '', c.valueSourceSpan, undefined /* keySpan */, - undefined /* valueSpan */, undefined /* valueTokens */, undefined /* i18n */)], + `ng-template`, [new html.Attribute( + 'ngSwitchDefault', '', c.valueSourceSpan, undefined /* keySpan */, + undefined /* valueSpan */, undefined /* i18n */)], expansionResult.nodes, c.sourceSpan, c.sourceSpan, c.sourceSpan); } return new html.Element( - `ng-template`, - [new html.Attribute( - 'ngSwitchCase', `${c.value}`, c.valueSourceSpan, undefined /* keySpan */, - undefined /* valueSpan */, undefined /* valueTokens */, undefined /* i18n */)], + `ng-template`, [new html.Attribute( + 'ngSwitchCase', `${c.value}`, c.valueSourceSpan, undefined /* keySpan */, + undefined /* valueSpan */, undefined /* i18n */)], expansionResult.nodes, c.sourceSpan, c.sourceSpan, c.sourceSpan); }); const switchAttr = new html.Attribute( '[ngSwitch]', ast.switchValue, ast.switchValueSourceSpan, undefined /* keySpan */, - undefined /* valueSpan */, undefined /* valueTokens */, undefined /* i18n */); + undefined /* valueSpan */, undefined /* i18n */); return new html.Element( 'ng-container', [switchAttr], children, ast.sourceSpan, ast.sourceSpan, ast.sourceSpan); } diff --git a/packages/compiler/src/ml_parser/parser.ts b/packages/compiler/src/ml_parser/parser.ts index d9f5d17bd3..4d5f18d440 100644 --- a/packages/compiler/src/ml_parser/parser.ts +++ b/packages/compiler/src/ml_parser/parser.ts @@ -216,7 +216,6 @@ class _TreeBuilder { } private _consumeText(token: lex.Token) { - const tokens = [token]; const startSpan = token.sourceSpan; let text = token.parts[0]; if (text.length > 0 && text[0] == '\n') { @@ -224,15 +223,14 @@ class _TreeBuilder { if (parent != null && parent.children.length == 0 && this.getTagDefinition(parent.name).ignoreFirstLf) { 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 || this._peek.type === lex.TokenType.TEXT || this._peek.type === lex.TokenType.ENCODED_ENTITY) { token = this._advance(); - tokens.push(token); if (token.type === lex.TokenType.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 @@ -250,8 +248,8 @@ class _TreeBuilder { const endSpan = token.sourceSpan; this._addToParent(new html.Text( text, - new ParseSourceSpan(startSpan.start, endSpan.end, startSpan.fullStart, startSpan.details), - tokens)); + new ParseSourceSpan( + startSpan.start, endSpan.end, startSpan.fullStart, startSpan.details))); } } @@ -374,17 +372,16 @@ class _TreeBuilder { // Consume the attribute value let value = ''; - const valueTokens: lex.Token[] = []; let valueStartSpan: ParseSourceSpan|undefined = undefined; let valueEnd: ParseLocation|undefined = undefined; if (this._peek.type === lex.TokenType.ATTR_VALUE_TEXT) { valueStartSpan = this._peek.sourceSpan; valueEnd = this._peek.sourceSpan.end; + // For now recombine text, interpolation and entity tokens while (this._peek.type === lex.TokenType.ATTR_VALUE_TEXT || this._peek.type === lex.TokenType.ATTR_VALUE_INTERPOLATION || this._peek.type === lex.TokenType.ENCODED_ENTITY) { - const valueToken = this._advance(); - valueTokens.push(valueToken); + let valueToken = this._advance(); if (valueToken.type === lex.TokenType.ATTR_VALUE_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 @@ -411,8 +408,7 @@ class _TreeBuilder { return new html.Attribute( fullName, value, new ParseSourceSpan(attrName.sourceSpan.start, attrEnd, attrName.sourceSpan.fullStart), - attrName.sourceSpan, valueSpan, valueTokens.length > 0 ? valueTokens : undefined, - undefined); + attrName.sourceSpan, valueSpan); } private _getParentElement(): html.Element|null { diff --git a/packages/compiler/test/ml_parser/ast_spec_utils.ts b/packages/compiler/test/ml_parser/ast_spec_utils.ts index 818ae726ce..a6f7ca65df 100644 --- a/packages/compiler/test/ml_parser/ast_spec_utils.ts +++ b/packages/compiler/test/ml_parser/ast_spec_utils.ts @@ -52,16 +52,12 @@ class _Humanizer implements html.Visitor { } visitAttribute(attribute: html.Attribute, context: any): any { - const valueTokens = attribute.valueTokens ?? []; - const res = this._appendContext(attribute, [ - html.Attribute, attribute.name, attribute.value, ...valueTokens.map(token => token.parts) - ]); + const res = this._appendContext(attribute, [html.Attribute, attribute.name, attribute.value]); this.result.push(res); } visitText(text: html.Text, context: any): any { - const res = this._appendContext( - text, [html.Text, text.value, this.elDepth, ...text.tokens.map(token => token.parts)]); + const res = this._appendContext(text, [html.Text, text.value, this.elDepth]); this.result.push(res); } diff --git a/packages/compiler/test/ml_parser/html_parser_spec.ts b/packages/compiler/test/ml_parser/html_parser_spec.ts index 57171d5f40..4fbfa75692 100644 --- a/packages/compiler/test/ml_parser/html_parser_spec.ts +++ b/packages/compiler/test/ml_parser/html_parser_spec.ts @@ -24,31 +24,31 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} describe('parse', () => { describe('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', () => { expect(humanizeDom(parser.parse('
a
', '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 elements', () => { expect(humanizeDom(parser.parse('a', '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', () => { expect(humanizeDom(parser.parse('', 'TestComp'))).toEqual([ - [html.Text, 'text', 0, ['text']] + [html.Text, 'text', 0] ]); }); it('should normalize line endings within CDATA', () => { const parsed = parser.parse('', 'TestComp'); 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([]); }); @@ -76,8 +76,8 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} expect(humanizeDom(parser.parse('', 'TestComp'))) .toEqual([ [html.Element, 'link', 0], - [html.Attribute, 'rel', 'author license', ['author license']], - [html.Attribute, 'href', '/about', ['/about']], + [html.Attribute, 'rel', 'author license'], + [html.Attribute, 'href', '/about'], ]); }); @@ -106,9 +106,9 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} it('should close void elements on text nodes', () => { expect(humanizeDom(parser.parse('

before
after

', 'TestComp'))).toEqual([ [html.Element, 'p', 0], - [html.Text, 'before', 1, ['before']], + [html.Text, 'before', 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('

1

2

', 'TestComp'))).toEqual([ [html.Element, 'div', 0], [html.Element, 'p', 1], - [html.Text, '1', 2, ['1']], + [html.Text, '1', 2], [html.Element, 'p', 1], - [html.Text, '2', 2, ['2']], + [html.Text, '2', 2], ]); }); @@ -200,12 +200,12 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} 'TestComp'))) .toEqual([ [html.Element, 'p', 0], - [html.Text, '\n', 1, ['\n']], + [html.Text, '\n', 1], [html.Element, 'textarea', 0], [html.Element, 'pre', 0], - [html.Text, '\n', 1, ['\n']], + [html.Text, '\n', 1], [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(' line 1 \r\n line 2 ', 'TestComp'); expect(humanizeDom(parsed)).toEqual([ [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([]); parsed = parser.parse('', 'TestComp'); expect(humanizeDom(parsed)).toEqual([ [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([]); parsed = parser.parse('
line 1 \r\n line 2
', 'TestComp'); expect(humanizeDom(parsed)).toEqual([ [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([]); parsed = parser.parse(' line 1 \r\n line 2 ', 'TestComp'); expect(humanizeDom(parsed)).toEqual([ [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([]); }); @@ -245,22 +245,8 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} it('should parse attributes on regular elements case sensitive', () => { expect(humanizeDom(parser.parse('
', 'TestComp'))).toEqual([ [html.Element, 'div', 0], - [html.Attribute, 'kEy', 'v', ['v']], - [html.Attribute, 'key2', 'v2', ['v2']], - ]); - }); - - it('should parse attributes containing interpolation', () => { - expect(humanizeDom(parser.parse('
', '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('
', 'TestComp'))).toEqual([ - [html.Element, 'div', 0], - [html.Attribute, 'foo', '&', [''], ['&', '&'], ['']], + [html.Attribute, 'kEy', 'v'], + [html.Attribute, 'key2', 'v2'], ]); }); @@ -273,10 +259,7 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} html.Element, 'div', 0, '
', '
', '
' ], - [ - html.Attribute, 'foo', '{{&}}', [''], ['{{', '&', '}}'], [''], - 'foo="{{&}}"' - ] + [html.Attribute, 'foo', '{{&}}', 'foo="{{&}}"'] ]); }); @@ -285,7 +268,7 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} parser.parse('
', 'TestComp'); expect(humanizeDom(result)).toEqual([ [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([]); }); @@ -300,7 +283,7 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} it('should parse attributes on svg elements case sensitive', () => { expect(humanizeDom(parser.parse('', 'TestComp'))).toEqual([ [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('', 'TestComp'))) .toEqual([ [html.Element, 'ng-template', 0], - [html.Attribute, 'k', 'v', ['v']], + [html.Attribute, 'k', 'v'], ]); }); it('should support namespace', () => { expect(humanizeDom(parser.parse('', 'TestComp'))).toEqual([ [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([ [html.Element, 'div', 0], - [html.Text, 'before', 1, ['before']], + [html.Text, 'before', 1], [html.Expansion, 'messages.length', 'plural', 1], [html.ExpansionCase, '=0', 2], [html.ExpansionCase, '=1', 2], - [html.Text, 'after', 1, ['after']], + [html.Text, 'after', 1], ]); const cases = (parsed.rootNodes[0]).children[1].cases; 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.Text, 'no', 1, ['no']], - [html.Text, ' messages', 0, [' messages']], + [html.Text, 'no', 1], + [html.Text, ' messages', 0], ]); 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([ [html.Element, 'div', 0], - [html.Text, '\n ', 1, ['\n ']], + [html.Text, '\n ', 1], [html.Expansion, '\n messages.length', 'plural', 1], [html.ExpansionCase, '=0', 2], [html.ExpansionCase, '=1', 2], - [html.Text, '\n', 1, ['\n']], + [html.Text, '\n', 1], ]); const cases = (parsed.rootNodes[0]).children[1].cases; 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([ - [html.Text, 'One {{message}}', 0, ['One '], ['{{', 'message', '}}'], ['']] + [html.Text, 'One {{message}}', 0] ]); expect(parsed.errors).toEqual([]); @@ -413,20 +396,20 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} expect(humanizeDom(parsed)).toEqual([ [html.Element, 'div', 0], - [html.Text, '\n ', 1, ['\n ']], + [html.Text, '\n ', 1], [html.Expansion, '\r\n messages.length', 'plural', 1], [html.ExpansionCase, '=0', 2], [html.ExpansionCase, '=1', 2], - [html.Text, '\n', 1, ['\n']], + [html.Text, '\n', 1], ]); const cases = (parsed.rootNodes[0]).children[1].cases; 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([ - [html.Text, 'One {{message}}', 0, ['One '], ['{{', 'message', '}}'], ['']] + [html.Text, 'One {{message}}', 0] ]); expect(parsed.errors).toEqual([]); @@ -450,20 +433,20 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} expect(humanizeDom(parsed)).toEqual([ [html.Element, 'div', 0], - [html.Text, '\n ', 1, ['\n ']], + [html.Text, '\n ', 1], [html.Expansion, '\n messages.length', 'plural', 1], [html.ExpansionCase, '=0', 2], [html.ExpansionCase, '=1', 2], - [html.Text, '\n', 1, ['\n']], + [html.Text, '\n', 1], ]); const cases = (parsed.rootNodes[0]).children[1].cases; 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([ - [html.Text, 'One {{message}}', 0, ['One '], ['{{', 'message', '}}'], ['']] + [html.Text, 'One {{message}}', 0] ]); expect(parsed.errors).toEqual([]); @@ -483,20 +466,20 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} expect(humanizeDom(parsed)).toEqual([ [html.Element, 'div', 0], - [html.Text, '\n ', 1, ['\n ']], + [html.Text, '\n ', 1], [html.Expansion, '\r\n messages.length', 'plural', 1], [html.ExpansionCase, '=0', 2], [html.ExpansionCase, '=1', 2], - [html.Text, '\n', 1, ['\n']], + [html.Text, '\n', 1], ]); const cases = (parsed.rootNodes[0]).children[1].cases; 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([ - [html.Text, 'One {{message}}', 0, ['One '], ['{{', 'message', '}}'], ['']] + [html.Text, 'One {{message}}', 0] ]); expect(parsed.errors).toEqual([]); @@ -529,7 +512,7 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} expect(humanizeDom(new ParseTreeResult(firstCase.expression, []))).toEqual([ [html.Expansion, 'p.gender', 'select', 0], [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; 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.ExpansionCase, 'male', 1], - [html.Text, '\n ', 0, ['\n ']], + [html.Text, '\n ', 0], ]); expect(parsed.errors).toEqual([]); @@ -586,10 +569,10 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} const expansion = parsed.rootNodes[0] as html.Expansion; 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.ExpansionCase, 'male', 1], - [html.Text, '\n ', 0, ['\n ']], + [html.Text, '\n ', 0], ]); expect(parsed.errors).toEqual([]); @@ -615,10 +598,10 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} const expansion = parsed.rootNodes[0] as html.Expansion; 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.ExpansionCase, 'male', 1], - [html.Text, '\n ', 0, ['\n ']], + [html.Text, '\n ', 0], ]); @@ -687,11 +670,11 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} '
\na\n
', '
', '
' ], - [html.Attribute, '[prop]', 'v1', ['v1'], '[prop]="v1"'], - [html.Attribute, '(e)', 'do()', ['do()'], '(e)="do()"'], - [html.Attribute, 'attr', 'v2', ['v2'], 'attr="v2"'], + [html.Attribute, '[prop]', 'v1', '[prop]="v1"'], + [html.Attribute, '(e)', 'do()', '(e)="do()"'], + [html.Attribute, 'attr', 'v2', 'attr="v2"'], [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} '{{&}}' + '{{▾}}' + '{{▾}}' + - '{{&unknown;}}' + '{{& (no semi-colon)}}' + - '{{&#xyz; (invalid hex)}}' + '{{BE; (invalid decimal)}}', 'TestComp'))) .toEqual([[ @@ -722,48 +703,17 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} '{{&}}' + '{{\u25BE}}' + '{{\u25BE}}' + - '{{&unknown;}}' + '{{& (no semi-colon)}}' + - '{{&#xyz; (invalid hex)}}' + '{{BE; (invalid decimal)}}', 0, - [''], - ['{{', '&', '}}'], - [''], - ['{{', '▾', '}}'], - [''], - ['{{', '▾', '}}'], - [''], - ['{{', '&unknown;', '}}'], - [''], - ['{{', '& (no semi-colon)', '}}'], - [''], - ['{{', '&#xyz; (invalid hex)', '}}'], - [''], - ['{{', 'BE; (invalid decimal)', '}}'], - [''], '{{&}}' + '{{▾}}' + '{{▾}}' + - '{{&unknown;}}' + '{{& (no semi-colon)}}' + - '{{&#xyz; (invalid hex)}}' + '{{BE; (invalid decimal)}}', ]]); }); - it('should support interpolations in text', () => { - expect( - humanizeDomSourceSpans(parser.parse('
pre {{ value }} post
', 'TestComp'))) - .toEqual([ - [html.Element, 'div', 0, '
pre {{ value }} post
', '
', '
'], - [ - html.Text, ' pre {{ value }} post ', 1, [' pre '], ['{{', ' value ', '}}'], - [' post '], ' pre {{ value }} post ' - ], - ]); - }); - it('should not set the end source span for void elements', () => { expect(humanizeDomSourceSpans(parser.parse('

', 'TestComp'))).toEqual([ [html.Element, 'div', 0, '

', '
', '
'], @@ -808,10 +758,10 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} html.Element, 'input', 0, '', '', '' ], - [html.Attribute, 'type', 'text', ['text'], 'type="text"'], - [html.Text, '\n\n\n ', 0, ['\n\n\n '], ''], + [html.Attribute, 'type', 'text', 'type="text"'], + [html.Text, '\n\n\n ', 0, ''], [html.Element, 'span', 0, '\n', '', ''], - [html.Text, '\n', 1, ['\n'], ''], + [html.Text, '\n', 1, ''], ]); }); @@ -824,9 +774,9 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} .toEqual([ [html.Element, 'div', 0, '
  • A
  • B
  • ', '
    ', '
    '], [html.Element, 'li', 1, '
  • ', '
  • ', null], - [html.Text, 'A', 2, ['A'], 'A'], + [html.Text, 'A', 2, 'A'], [html.Element, 'li', 1, '
  • ', '
  • ', null], - [html.Text, 'B', 2, ['B'], 'B'], + [html.Text, 'B', 2, 'B'], ]); }); @@ -864,7 +814,7 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} describe('visitor', () => { it('should visit text nodes', () => { 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', () => { @@ -874,7 +824,7 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} it('should visit attribute nodes', () => { const result = humanizeDom(parser.parse('
    ', 'TestComp')); - expect(result).toContain([html.Attribute, 'id', 'foo', ['foo']]); + expect(result).toContain([html.Attribute, 'id', 'foo']); }); it('should visit all nodes', () => { @@ -982,7 +932,7 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} expect(humanizeNodes(rootNodes, true)).toEqual([ [html.Element, 'div', 0, '
    ', '', ''], ]); @@ -997,7 +947,7 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes} expect(humanizeNodes(rootNodes, true)).toEqual([ [html.Element, 'div', 0, '
    ', '', ''], ]); diff --git a/packages/compiler/test/ml_parser/html_whitespaces_spec.ts b/packages/compiler/test/ml_parser/html_whitespaces_spec.ts index 4d1062a9c8..117be970d1 100644 --- a/packages/compiler/test/ml_parser/html_whitespaces_spec.ts +++ b/packages/compiler/test/ml_parser/html_whitespaces_spec.ts @@ -7,7 +7,6 @@ */ 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 {PRESERVE_WS_ATTR_NAME, removeWhitespaces} from '../../src/ml_parser/html_whitespaces'; import {TokenizeOptions} from '../../src/ml_parser/lexer'; @@ -53,86 +52,48 @@ import {humanizeDom} from './ast_spec_utils'; expect(parseAndRemoveWS('
    foo&ngsp;bar
    ')).toEqual([ [html.Element, 'div', 0], [html.Element, 'span', 1], - [html.Text, 'foo', 2, ['foo']], - [html.Text, ' ', 1, [''], [NGSP_UNICODE, '&ngsp;'], ['']], + [html.Text, 'foo', 2], + [html.Text, ' ', 1], [html.Element, 'span', 1], - [html.Text, 'bar', 2, ['bar']], + [html.Text, 'bar', 2], ]); }); 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 foo \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]]); }); it('should not replace  ', () => { - expect(parseAndRemoveWS(' ')).toEqual([ - [html.Text, '\u00a0', 0, [''], ['\u00a0', ' '], ['']] - ]); + expect(parseAndRemoveWS(' ')).toEqual([[html.Text, '\u00a0', 0]]); }); it('should not replace sequences of  ', () => { - expect(parseAndRemoveWS('  foo  ')).toEqual([[ - html.Text, - '\u00a0\u00a0foo\u00a0\u00a0', - 0, - [''], - ['\u00a0', ' '], - [''], - ['\u00a0', ' '], - ['foo'], - ['\u00a0', ' '], - [''], - ['\u00a0', ' '], - [''], - ]]); + expect(parseAndRemoveWS('  foo  ')).toEqual([ + [html.Text, '\u00a0\u00a0foo\u00a0\u00a0', 0] + ]); }); it('should not replace single tab and newline with spaces', () => { - expect(parseAndRemoveWS('\nfoo')).toEqual([[html.Text, '\nfoo', 0, ['\nfoo']]]); - expect(parseAndRemoveWS('\tfoo')).toEqual([[html.Text, '\tfoo', 0, ['\tfoo']]]); + expect(parseAndRemoveWS('\nfoo')).toEqual([[html.Text, '\nfoo', 0]]); + expect(parseAndRemoveWS('\tfoo')).toEqual([[html.Text, '\tfoo', 0]]); }); it('should preserve single whitespaces between interpolations', () => { - expect(parseAndRemoveWS(`{{fooExp}} {{barExp}}`)).toEqual([[ - html.Text, - '{{fooExp}} {{barExp}}', - 0, - [''], - ['{{', 'fooExp', '}}'], - [' '], - ['{{', 'barExp', '}}'], - [''], - ]]); + expect(parseAndRemoveWS(`{{fooExp}} {{barExp}}`)).toEqual([ + [html.Text, '{{fooExp}} {{barExp}}', 0], + ]); expect(parseAndRemoveWS(`{{fooExp}}\t{{barExp}}`)).toEqual([ - [ - html.Text, - '{{fooExp}}\t{{barExp}}', - 0, - [''], - ['{{', 'fooExp', '}}'], - ['\t'], - ['{{', 'barExp', '}}'], - [''], - ], + [html.Text, '{{fooExp}}\t{{barExp}}', 0], ]); expect(parseAndRemoveWS(`{{fooExp}}\n{{barExp}}`)).toEqual([ - [ - html.Text, - '{{fooExp}}\n{{barExp}}', - 0, - [''], - ['{{', 'fooExp', '}}'], - ['\n'], - ['{{', 'barExp', '}}'], - [''], - ], + [html.Text, '{{fooExp}}\n{{barExp}}', 0], ]); }); it('should preserve whitespaces around interpolations', () => { 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(` {a, b, =4 {c}} `, {tokenizeExpansionForms: true})) .toEqual([ [html.Element, 'span', 0], - [html.Text, ' ', 1, [' ']], + [html.Text, ' ', 1], [html.Expansion, 'a', 'b', 1], [html.ExpansionCase, '=4', 2], - [html.Text, ' ', 1, [' ']], + [html.Text, ' ', 1], ]); }); @@ -151,17 +112,17 @@ import {humanizeDom} from './ast_spec_utils'; expect(parseAndRemoveWS(`
    foo\nbar
    `)).toEqual([ [html.Element, 'pre', 0], [html.Element, 'strong', 1], - [html.Text, 'foo', 2, ['foo']], - [html.Text, '\n', 1, ['\n']], + [html.Text, 'foo', 2], + [html.Text, '\n', 1], [html.Element, 'strong', 1], - [html.Text, 'bar', 2, ['bar']], + [html.Text, 'bar', 2], ]); }); it('should skip whitespace trimming in `)).toEqual([ [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(`
    `)).toEqual([ [html.Element, 'div', 0], [html.Element, 'img', 1], - [html.Text, ' ', 1, [' ']], + [html.Text, ' ', 1], [html.Element, 'img', 1], ]); }); diff --git a/packages/compiler/test/ml_parser/icu_ast_expander_spec.ts b/packages/compiler/test/ml_parser/icu_ast_expander_spec.ts index cbd51c1c6c..d9e8ed2407 100644 --- a/packages/compiler/test/ml_parser/icu_ast_expander_spec.ts +++ b/packages/compiler/test/ml_parser/icu_ast_expander_spec.ts @@ -29,9 +29,9 @@ import {humanizeNodes} from './ast_spec_utils'; [html.Attribute, '[ngPlural]', 'messages.length'], [html.Element, 'ng-template', 1], [html.Attribute, 'ngPluralCase', '=0'], - [html.Text, 'zero', 2, ['zero']], + [html.Text, 'zero', 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.Element, 'ng-template', 3], [html.Attribute, 'ngSwitchCase', 'male'], - [html.Text, 'm', 4, ['m']], - [html.Text, ' ', 2, [' ']], + [html.Text, 'm', 4], + [html.Text, ' ', 2], ]); }); @@ -88,10 +88,10 @@ import {humanizeNodes} from './ast_spec_utils'; [html.Attribute, '[ngSwitch]', 'person.gender'], [html.Element, 'ng-template', 1], [html.Attribute, 'ngSwitchCase', 'male'], - [html.Text, 'm', 2, ['m']], + [html.Text, 'm', 2], [html.Element, 'ng-template', 1], [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.Element, 'ng-template', 3], [html.Attribute, 'ngSwitchCase', '=4'], - [html.Text, 'c', 4, ['c']], + [html.Text, 'c', 4], ]); });