diff --git a/packages/compiler/src/ml_parser/lexer.ts b/packages/compiler/src/ml_parser/lexer.ts
index d62a54f576..f0fb361232 100644
--- a/packages/compiler/src/ml_parser/lexer.ts
+++ b/packages/compiler/src/ml_parser/lexer.ts
@@ -22,7 +22,6 @@ export enum TokenType {
TEXT,
ESCAPABLE_RAW_TEXT,
RAW_TEXT,
- INTERPOLATION,
COMMENT_START,
COMMENT_END,
CDATA_START,
@@ -286,7 +285,7 @@ class _Tokenizer {
}
const token = new Token(
this._currentTokenType, parts,
- (end ?? this._cursor).getSpan(this._currentTokenStart, this._leadingTriviaCodePoints));
+ this._cursor.getSpan(this._currentTokenStart, this._leadingTriviaCodePoints));
this.tokens.push(token);
this._currentTokenStart = null;
this._currentTokenType = null;
@@ -697,16 +696,19 @@ class _Tokenizer {
}
private _consumeText() {
- this._beginToken(TokenType.TEXT);
+ const start = this._cursor.clone();
+ this._beginToken(TokenType.TEXT, start);
const parts: string[] = [];
do {
- const current = this._cursor.clone();
if (this._interpolationConfig && this._attemptStr(this._interpolationConfig.start)) {
- this._endToken([this._processCarriageReturns(parts.join(''))], current);
- this._consumeInterpolation(current);
- parts.length = 0;
- this._beginToken(TokenType.TEXT);
+ parts.push(this._interpolationConfig.start);
+ this._inInterpolation = true;
+ } else if (
+ this._interpolationConfig && this._inInterpolation &&
+ this._attemptStr(this._interpolationConfig.end)) {
+ parts.push(this._interpolationConfig.end);
+ this._inInterpolation = false;
} else {
parts.push(this._readChar(true));
}
@@ -719,61 +721,6 @@ class _Tokenizer {
this._endToken([this._processCarriageReturns(parts.join(''))]);
}
- private _consumeInterpolation(interpolationStart: CharacterCursor) {
- const parts: string[] = [];
- this._beginToken(TokenType.INTERPOLATION, interpolationStart);
- parts.push(this._interpolationConfig.start);
-
- // Find the end of the interpolation, ignoring content inside quotes.
- const expressionStart = this._cursor.clone();
- let inQuote: string|null = null;
- let inComment = false;
- while (this._cursor.peek() !== chars.$EOF) {
- const current = this._cursor.clone();
-
- if (this._isTagStart()) {
- // We are starting what looks like an HTML element in the middle of this interpolation.
- // Reset the cursor to before the `<` character and end the interpolation token.
- // (This is actually wrong but here for backward compatibility).
- this._cursor = current;
- parts.push(this._getProcessedChars(expressionStart, current));
- return this._endToken(parts);
- }
-
- if (inQuote === null) {
- if (this._attemptStr(this._interpolationConfig.end)) {
- // We are not in a string, and we hit the end interpolation marker
- parts.push(this._getProcessedChars(expressionStart, current));
- parts.push(this._interpolationConfig.end);
- return this._endToken(parts);
- } else if (this._attemptStr('//')) {
- // Once we are in a comment we ignore any quotes
- inComment = true;
- }
- }
-
- const char = this._readChar(true);
- if (char === '\\') {
- // Skip the next character because it was escaped.
- this._readChar(true);
- } else if (char === inQuote) {
- // Exiting the current quoted string
- inQuote = null;
- } else if (!inComment && /['"`]/.test(char)) {
- // Entering a new quoted string
- inQuote = char;
- }
- }
-
- // We hit EOF without finding a closing interpolation marker
- parts.push(this._getProcessedChars(expressionStart, this._cursor));
- return this._endToken(parts);
- }
-
- private _getProcessedChars(start: CharacterCursor, end: CharacterCursor): string {
- return this._processCarriageReturns(end.getChars(start))
- }
-
private _isTextEnd(): boolean {
if (this._isTagStart() || this._cursor.peek() === chars.$EOF) {
return true;
diff --git a/packages/compiler/src/ml_parser/parser.ts b/packages/compiler/src/ml_parser/parser.ts
index fd01357d43..24465f8e97 100644
--- a/packages/compiler/src/ml_parser/parser.ts
+++ b/packages/compiler/src/ml_parser/parser.ts
@@ -9,7 +9,6 @@
import {ParseError, ParseSourceSpan} from '../parse_util';
import * as html from './ast';
-import {NAMED_ENTITIES} from './entities';
import * as lex from './lexer';
import {getNsPrefix, mergeNsAndName, splitNsName, TagDefinition} from './tags';
@@ -216,7 +215,6 @@ class _TreeBuilder {
}
private _consumeText(token: lex.Token) {
- const startSpan = token.sourceSpan;
let text = token.parts[0];
if (text.length > 0 && text[0] == '\n') {
const parent = this._getParentElement();
@@ -226,29 +224,8 @@ class _TreeBuilder {
}
}
- // For now recombine text and interpolation tokens
- if (this._peek.type === lex.TokenType.INTERPOLATION) {
- while (this._peek.type === lex.TokenType.INTERPOLATION ||
- this._peek.type === lex.TokenType.TEXT) {
- token = this._advance();
- 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
- // fix it. It should be addressed in a larger project to refactor the entire parser/lexer
- // chain after View Engine has been removed.
- text += token.parts.join('').replace(/&([^;]+);/g, decodeEntity);
- } else {
- text += token.parts.join('');
- }
- }
- }
-
if (text.length > 0) {
- const endSpan = token.sourceSpan;
- this._addToParent(new html.Text(
- text,
- new ParseSourceSpan(
- startSpan.start, endSpan.end, startSpan.fullStart, startSpan.details)));
+ this._addToParent(new html.Text(text, token.sourceSpan));
}
}
@@ -418,21 +395,3 @@ class _TreeBuilder {
function lastOnStack(stack: any[], element: any): boolean {
return stack.length > 0 && stack[stack.length - 1] === element;
}
-
-/**
- * Decode the `entity` string, which we believe is the contents of an HTML entity.
- *
- * If the string is not actually a valid/known entity then just return the original `match` string.
- */
-function decodeEntity(match: string, entity: string): string {
- if (NAMED_ENTITIES[entity] !== undefined) {
- return NAMED_ENTITIES[entity] || match;
- }
- if (/^#x[a-f0-9]+$/i.test(entity)) {
- return String.fromCodePoint(parseInt(entity.slice(2), 16));
- }
- if (/^#\d+$/.test(entity)) {
- return String.fromCodePoint(parseInt(entity.slice(1), 10));
- }
- return match;
-}
diff --git a/packages/compiler/test/ml_parser/html_parser_spec.ts b/packages/compiler/test/ml_parser/html_parser_spec.ts
index 279bca60d3..b971d9187a 100644
--- a/packages/compiler/test/ml_parser/html_parser_spec.ts
+++ b/packages/compiler/test/ml_parser/html_parser_spec.ts
@@ -675,32 +675,6 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn, humanizeNodes}
expect(node.endSourceSpan!.end.offset).toEqual(12);
});
- // This checks backward compatibility with a previous version of the lexer, which would
- // treat interpolation expressions as regular HTML escapable text.
- it('should decode HTML entities in interpolations', () => {
- expect(humanizeDomSourceSpans(parser.parse(
- '{{&}}' +
- '{{▾}}' +
- '{{▾}}' +
- '{{& (no semi-colon)}}' +
- '{{BE; (invalid decimal)}}',
- 'TestComp')))
- .toEqual([[
- html.Text,
- '{{&}}' +
- '{{\u25BE}}' +
- '{{\u25BE}}' +
- '{{& (no semi-colon)}}' +
- '{{BE; (invalid decimal)}}',
- 0,
- '{{&}}' +
- '{{▾}}' +
- '{{▾}}' +
- '{{& (no semi-colon)}}' +
- '{{BE; (invalid decimal)}}',
- ]]);
- });
-
it('should not set the end source span for void elements', () => {
expect(humanizeDomSourceSpans(parser.parse('
', 'TestComp'))).toEqual([
[html.Element, 'div', 0, '
', '', '
'],
diff --git a/packages/compiler/test/ml_parser/lexer_spec.ts b/packages/compiler/test/ml_parser/lexer_spec.ts
index 54005b28ba..5c795ed959 100644
--- a/packages/compiler/test/ml_parser/lexer_spec.ts
+++ b/packages/compiler/test/ml_parser/lexer_spec.ts
@@ -549,66 +549,25 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u
});
it('should parse interpolation', () => {
- expect(tokenizeAndHumanizeParts('{{ a }}b{{ c // comment }}d{{ e "}}" f }}g{{ h // " i }}'))
- .toEqual([
- [lex.TokenType.TEXT, ''],
- [lex.TokenType.INTERPOLATION, '{{', ' a ', '}}'],
- [lex.TokenType.TEXT, 'b'],
- [lex.TokenType.INTERPOLATION, '{{', ' c // comment ', '}}'],
- [lex.TokenType.TEXT, 'd'],
- [lex.TokenType.INTERPOLATION, '{{', ' e "}}" f ', '}}'],
- [lex.TokenType.TEXT, 'g'],
- [lex.TokenType.INTERPOLATION, '{{', ' h // " i ', '}}'],
- [lex.TokenType.TEXT, ''],
- [lex.TokenType.EOF],
- ]);
-
- expect(tokenizeAndHumanizeSourceSpans('{{ a }}b{{ c // comment }}')).toEqual([
- [lex.TokenType.TEXT, ''],
- [lex.TokenType.INTERPOLATION, '{{ a }}'],
- [lex.TokenType.TEXT, 'b'],
- [lex.TokenType.INTERPOLATION, '{{ c // comment }}'],
- [lex.TokenType.TEXT, ''],
- [lex.TokenType.EOF, ''],
+ expect(tokenizeAndHumanizeParts('{{ a }}b{{ c // comment }}')).toEqual([
+ [lex.TokenType.TEXT, '{{ a }}b{{ c // comment }}'],
+ [lex.TokenType.EOF],
]);
});
it('should parse interpolation with custom markers', () => {
expect(tokenizeAndHumanizeParts('{% a %}', {interpolationConfig: {start: '{%', end: '%}'}}))
.toEqual([
- [lex.TokenType.TEXT, ''],
- [lex.TokenType.INTERPOLATION, '{%', ' a ', '%}'],
- [lex.TokenType.TEXT, ''],
+ [lex.TokenType.TEXT, '{% a %}'],
[lex.TokenType.EOF],
]);
});
- it('should handle CR & LF in text', () => {
+ it('should handle CR & LF', () => {
expect(tokenizeAndHumanizeParts('t\ne\rs\r\nt')).toEqual([
[lex.TokenType.TEXT, 't\ne\ns\nt'],
[lex.TokenType.EOF],
]);
-
- expect(tokenizeAndHumanizeSourceSpans('t\ne\rs\r\nt')).toEqual([
- [lex.TokenType.TEXT, 't\ne\rs\r\nt'],
- [lex.TokenType.EOF, ''],
- ]);
- });
-
- it('should handle CR & LF in interpolation', () => {
- expect(tokenizeAndHumanizeParts('{{t\ne\rs\r\nt}}')).toEqual([
- [lex.TokenType.TEXT, ''],
- [lex.TokenType.INTERPOLATION, '{{', 't\ne\ns\nt', '}}'],
- [lex.TokenType.TEXT, ''],
- [lex.TokenType.EOF],
- ]);
-
- expect(tokenizeAndHumanizeSourceSpans('{{t\ne\rs\r\nt}}')).toEqual([
- [lex.TokenType.TEXT, ''],
- [lex.TokenType.INTERPOLATION, '{{t\ne\rs\r\nt}}'],
- [lex.TokenType.TEXT, ''],
- [lex.TokenType.EOF, ''],
- ]);
});
it('should parse entities', () => {
@@ -616,11 +575,6 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u
[lex.TokenType.TEXT, 'a&b'],
[lex.TokenType.EOF],
]);
-
- expect(tokenizeAndHumanizeSourceSpans('a&b')).toEqual([
- [lex.TokenType.TEXT, 'a&b'],
- [lex.TokenType.EOF, ''],
- ]);
});
it('should parse text starting with "&"', () => {
@@ -639,9 +593,7 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u
it('should allow "<" in text nodes', () => {
expect(tokenizeAndHumanizeParts('{{ a < b ? c : d }}')).toEqual([
- [lex.TokenType.TEXT, ''],
- [lex.TokenType.INTERPOLATION, '{{', ' a < b ? c : d ', '}}'],
- [lex.TokenType.TEXT, ''],
+ [lex.TokenType.TEXT, '{{ a < b ? c : d }}'],
[lex.TokenType.EOF],
]);
@@ -662,9 +614,7 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u
it('should break out of interpolation in text token on valid start tag', () => {
expect(tokenizeAndHumanizeParts('{{ a d }}')).toEqual([
- [lex.TokenType.TEXT, ''],
- [lex.TokenType.INTERPOLATION, '{{', ' a '],
- [lex.TokenType.TEXT, ''],
+ [lex.TokenType.TEXT, '{{ a '],
[lex.TokenType.TAG_OPEN_START, '', 'b'],
[lex.TokenType.ATTR_NAME, '', '&&'],
[lex.TokenType.ATTR_NAME, '', 'c'],
@@ -676,9 +626,7 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u
it('should break out of interpolation in text token on valid comment', () => {
expect(tokenizeAndHumanizeParts('{{ a }}')).toEqual([
- [lex.TokenType.TEXT, ''],
- [lex.TokenType.INTERPOLATION, '{{', ' a }'],
- [lex.TokenType.TEXT, ''],
+ [lex.TokenType.TEXT, '{{ a }'],
[lex.TokenType.COMMENT_START],
[lex.TokenType.RAW_TEXT, ''],
[lex.TokenType.COMMENT_END],
@@ -689,9 +637,7 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u
it('should break out of interpolation in text token on valid CDATA', () => {
expect(tokenizeAndHumanizeParts('{{ a }}')).toEqual([
- [lex.TokenType.TEXT, ''],
- [lex.TokenType.INTERPOLATION, '{{', ' a }'],
- [lex.TokenType.TEXT, ''],
+ [lex.TokenType.TEXT, '{{ a }'],
[lex.TokenType.CDATA_START],
[lex.TokenType.RAW_TEXT, ''],
[lex.TokenType.CDATA_END],
@@ -707,14 +653,13 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u
.toEqual([
[lex.TokenType.TAG_OPEN_START, '', 'code'],
[lex.TokenType.TAG_OPEN_END],
- [lex.TokenType.TEXT, ''],
- [lex.TokenType.INTERPOLATION, '{{', '\'<={\'', '}}'],
- [lex.TokenType.TEXT, ''],
+ [lex.TokenType.TEXT, '{{\'<={\'}}'],
[lex.TokenType.TAG_CLOSE, '', 'code'],
[lex.TokenType.EOF],
]);
});
+
it('should parse start tags quotes in place of an attribute name as text', () => {
expect(tokenizeAndHumanizeParts('')).toEqual([
[lex.TokenType.INCOMPLETE_TAG_OPEN, '', 't'],
@@ -758,32 +703,18 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u
it('should be able to escape {', () => {
expect(tokenizeAndHumanizeParts('{{ "{" }}')).toEqual([
- [lex.TokenType.TEXT, ''],
- [lex.TokenType.INTERPOLATION, '{{', ' "{" ', '}}'],
- [lex.TokenType.TEXT, ''],
+ [lex.TokenType.TEXT, '{{ "{" }}'],
[lex.TokenType.EOF],
]);
});
it('should be able to escape {{', () => {
expect(tokenizeAndHumanizeParts('{{ "{{" }}')).toEqual([
- [lex.TokenType.TEXT, ''],
- [lex.TokenType.INTERPOLATION, '{{', ' "{{" ', '}}'],
- [lex.TokenType.TEXT, ''],
+ [lex.TokenType.TEXT, '{{ "{{" }}'],
[lex.TokenType.EOF],
]);
});
- it('should capture everything up to the end of file in the interpolation expression part if there are mismatched quotes',
- () => {
- expect(tokenizeAndHumanizeParts('{{ "{{a}}\' }}')).toEqual([
- [lex.TokenType.TEXT, ''],
- [lex.TokenType.INTERPOLATION, '{{', ' "{{a}}\' }}'],
- [lex.TokenType.TEXT, ''],
- [lex.TokenType.EOF],
- ]);
- });
-
it('should treat expansion form as text when they are not parsed', () => {
expect(tokenizeAndHumanizeParts(
'{a, b, =4 {c}}', {tokenizeExpansionForms: false}))
@@ -1045,9 +976,7 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u
[lex.TokenType.RAW_TEXT, 'three'],
[lex.TokenType.EXPANSION_CASE_VALUE, '=4'],
[lex.TokenType.EXPANSION_CASE_EXP_START],
- [lex.TokenType.TEXT, 'four '],
- [lex.TokenType.INTERPOLATION, '{{', 'a', '}}'],
- [lex.TokenType.TEXT, ''],
+ [lex.TokenType.TEXT, 'four {{a}}'],
[lex.TokenType.EXPANSION_CASE_EXP_END],
[lex.TokenType.EXPANSION_FORM_END],
[lex.TokenType.EOF],
@@ -1104,9 +1033,7 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u
[lex.TokenType.EXPANSION_CASE_EXP_END],
[lex.TokenType.EXPANSION_CASE_VALUE, '=1'],
[lex.TokenType.EXPANSION_CASE_EXP_START],
- [lex.TokenType.TEXT, 'One '],
- [lex.TokenType.INTERPOLATION, '{{', 'message', '}}'],
- [lex.TokenType.TEXT, ''],
+ [lex.TokenType.TEXT, 'One {{message}}'],
[lex.TokenType.EXPANSION_CASE_EXP_END],
[lex.TokenType.EXPANSION_FORM_END],
[lex.TokenType.TEXT, '\n'],
@@ -1136,9 +1063,7 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u
[lex.TokenType.EXPANSION_CASE_EXP_END],
[lex.TokenType.EXPANSION_CASE_VALUE, '=1'],
[lex.TokenType.EXPANSION_CASE_EXP_START],
- [lex.TokenType.TEXT, 'One '],
- [lex.TokenType.INTERPOLATION, '{{', 'message', '}}'],
- [lex.TokenType.TEXT, ''],
+ [lex.TokenType.TEXT, 'One {{message}}'],
[lex.TokenType.EXPANSION_CASE_EXP_END],
[lex.TokenType.EXPANSION_FORM_END],
[lex.TokenType.TEXT, '\n'],
@@ -1219,9 +1144,7 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u
[lex.TokenType.EXPANSION_CASE_EXP_END],
[lex.TokenType.EXPANSION_CASE_VALUE, '=1'],
[lex.TokenType.EXPANSION_CASE_EXP_START],
- [lex.TokenType.TEXT, 'One '],
- [lex.TokenType.INTERPOLATION, '{{', 'message', '}}'],
- [lex.TokenType.TEXT, ''],
+ [lex.TokenType.TEXT, 'One {{message}}'],
[lex.TokenType.EXPANSION_CASE_EXP_END],
[lex.TokenType.EXPANSION_FORM_END],
[lex.TokenType.TEXT, '\n'],
@@ -1251,9 +1174,7 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u
[lex.TokenType.EXPANSION_CASE_EXP_END],
[lex.TokenType.EXPANSION_CASE_VALUE, '=1'],
[lex.TokenType.EXPANSION_CASE_EXP_START],
- [lex.TokenType.TEXT, 'One '],
- [lex.TokenType.INTERPOLATION, '{{', 'message', '}}'],
- [lex.TokenType.TEXT, ''],
+ [lex.TokenType.TEXT, 'One {{message}}'],
[lex.TokenType.EXPANSION_CASE_EXP_END],
[lex.TokenType.EXPANSION_FORM_END],
[lex.TokenType.TEXT, '\n'],
@@ -1380,11 +1301,8 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u
[lex.TokenType.TEXT, '\n \n \n'],
[lex.TokenType.EOF],
]);
- expect(tokenizeAndHumanizeParts('\\r{{\\r}}\\r', {escapedString: true})).toEqual([
- // post processing converts `\r` to `\n`
- [lex.TokenType.TEXT, '\n'],
- [lex.TokenType.INTERPOLATION, '{{', '\n', '}}'],
- [lex.TokenType.TEXT, '\n'],
+ expect(tokenizeAndHumanizeParts('\\r \\r \\r', {escapedString: true})).toEqual([
+ [lex.TokenType.TEXT, '\n \n \n'], // post processing converts `\r` to `\n`
[lex.TokenType.EOF],
]);
expect(tokenizeAndHumanizeParts('\\v \\v \\v', {escapedString: true})).toEqual([