Skip to content

Commit 4605df8

Browse files
pkozlowski-opensourcematsko
authored andcommittedMar 19, 2019
fix(core): parse incorrect ML open tag as text (#29328)
This PR alligns markup language lexer with the previous behaviour in version 7.x: https://stackblitz.com/edit/angular-iancj2 While this behaviour is not perfect (we should be giving users an error message here about invalid HTML instead of assuming text node) this is probably best we can do without more substential re-write of lexing / parsing infrastructure. This PR just fixes #29231 and restores VE behaviour - a more elaborate fix will be done in a separate PR as it requries non-trivial rewrites. PR Close #29328
1 parent c0ad9e1 commit 4605df8

File tree

2 files changed

+45
-3
lines changed

2 files changed

+45
-3
lines changed
 

‎packages/compiler/src/ml_parser/lexer.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -461,12 +461,14 @@ class _Tokenizer {
461461
let tagName: string;
462462
let prefix: string;
463463
let openTagToken: Token|undefined;
464+
let tokensBeforeTagOpen = this.tokens.length;
464465
const innerStart = this._cursor.clone();
465466
try {
466467
if (!chars.isAsciiLetter(this._cursor.peek())) {
467468
throw this._createError(
468469
_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));
469470
}
471+
470472
openTagToken = this._consumeTagOpenStart(start);
471473
prefix = openTagToken.parts[0];
472474
tagName = openTagToken.parts[1];
@@ -483,10 +485,10 @@ class _Tokenizer {
483485
this._consumeTagOpenEnd();
484486
} catch (e) {
485487
if (e instanceof _ControlFlowError) {
486-
// When the start tag is invalid, assume we want a "<"
488+
// When the start tag is invalid (including invalid "attributes"), assume we want a "<"
487489
this._cursor = innerStart;
488490
if (openTagToken) {
489-
this.tokens.pop();
491+
this.tokens.length = tokensBeforeTagOpen;
490492
}
491493
// Back to back text tokens are merged at the end
492494
this._beginToken(TokenType.TEXT, start);
@@ -528,6 +530,10 @@ class _Tokenizer {
528530
}
529531

530532
private _consumeAttributeName() {
533+
const attrNameStart = this._cursor.peek();
534+
if (attrNameStart === chars.$SQ || attrNameStart === chars.$DQ) {
535+
throw this._createError(_unexpectedCharacterErrorMsg(attrNameStart), this._cursor.getSpan());
536+
}
531537
this._beginToken(TokenType.ATTR_NAME);
532538
const prefixAndName = this._consumePrefixAndName();
533539
this._endToken(prefixAndName);

‎packages/compiler/test/ml_parser/lexer_spec.ts

+37-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
*/
88

99
import {getHtmlTagDefinition} from '../../src/ml_parser/html_tags';
10-
import {InterpolationConfig} from '../../src/ml_parser/interpolation_config';
1110
import * as lex from '../../src/ml_parser/lexer';
1211
import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_util';
1312

@@ -378,6 +377,18 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u
378377
]);
379378
});
380379

380+
it('should report missing closing single quote', () => {
381+
expect(tokenizeAndHumanizeErrors('<t a=\'b>')).toEqual([
382+
[lex.TokenType.ATTR_VALUE, 'Unexpected character "EOF"', '0:8'],
383+
]);
384+
});
385+
386+
it('should report missing closing double quote', () => {
387+
expect(tokenizeAndHumanizeErrors('<t a="b>')).toEqual([
388+
[lex.TokenType.ATTR_VALUE, 'Unexpected character "EOF"', '0:8'],
389+
]);
390+
});
391+
381392
});
382393

383394
describe('closing tags', () => {
@@ -552,6 +563,31 @@ import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_u
552563
]);
553564
});
554565

566+
it('should parse start tags quotes in place of an attribute name as text', () => {
567+
expect(tokenizeAndHumanizeParts('<t ">')).toEqual([
568+
[lex.TokenType.TEXT, '<t ">'],
569+
[lex.TokenType.EOF],
570+
]);
571+
572+
expect(tokenizeAndHumanizeParts('<t \'>')).toEqual([
573+
[lex.TokenType.TEXT, '<t \'>'],
574+
[lex.TokenType.EOF],
575+
]);
576+
});
577+
578+
it('should parse start tags quotes in place of an attribute name (after a valid attribute) as text',
579+
() => {
580+
expect(tokenizeAndHumanizeParts('<t a="b" ">')).toEqual([
581+
[lex.TokenType.TEXT, '<t a="b" ">'],
582+
[lex.TokenType.EOF],
583+
]);
584+
585+
expect(tokenizeAndHumanizeParts('<t a=\'b\' \'>')).toEqual([
586+
[lex.TokenType.TEXT, '<t a=\'b\' \'>'],
587+
[lex.TokenType.EOF],
588+
]);
589+
});
590+
555591
it('should be able to escape {', () => {
556592
expect(tokenizeAndHumanizeParts('{{ "{" }}')).toEqual([
557593
[lex.TokenType.TEXT, '{{ "{" }}'],

0 commit comments

Comments
 (0)