-
Notifications
You must be signed in to change notification settings - Fork 24.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(compiler): bypass expansion form tokenizing in ngNonBindable blocks #12178
Conversation
@@ -88,6 +89,8 @@ class _Tokenizer { | |||
private _currentTokenType: TokenType; | |||
private _expansionCaseStack: TokenType[] = []; | |||
private _inInterpolation: boolean = false; | |||
private _inNonBindableBlock: boolean = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be a getter (depth gt 0)
@@ -60,6 +60,7 @@ export function tokenize( | |||
} | |||
|
|||
const _CR_OR_CRLF_REGEXP = /\r\n?/g; | |||
const NG_NON_BINDABLE_ATTR = 'ngNonBindable'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this be exported from the template parser ?
Reviewed comments and ammended, @vicb |
try { | ||
if (!chars.isAsciiLetter(this._peek)) { | ||
throw this._createError(_unexpectedCharacterErrorMsg(this._peek), this._getSpan()); | ||
} | ||
const nameStart = this._index; | ||
this._consumeTagOpenStart(start); | ||
if (this._inNonBindableBlock()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- merge this test with the code below,
- push all
AttrName
tokens to an array,
if (this._inNonBindableBlock()) {
depth++;
} else if (attrNames.some(isNonBindableAttribute)) {
depth = 1;
}
@@ -640,6 +655,8 @@ class _Tokenizer { | |||
} | |||
} | |||
|
|||
private _inNonBindableBlock(): boolean { return this._nonBindableBlockDepth > 0; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Block -> Element
@@ -523,6 +535,9 @@ class _Tokenizer { | |||
this._attemptCharCodeUntilFn(isNotWhitespace); | |||
this._requireCharCode(chars.$GT); | |||
this._endToken(prefixAndName); | |||
if (this._inNonBindableBlock()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some tag are implicitly closed
<ul><li ngNonBindable>blabla<li>{{ 'hello' }}</ul>
Please fix and add a test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably not easy as the logic is in the parser...
May be we should move ICU logic into the parser ??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I was just dwelling on that - makes the explicit close-open tags mush more appealing! 😄
It's definitely doable, specially since the lexer does get a _getTagDefinition getter we can use. Moving all code to a checker function that runs after a tag open and a tag close, instead of sprinkling it around the class might do the trick.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A first draft for clarity. Let me know if you prefer to move the logic/approach this in another way.
private _checkNonBindableElementDepth () {
let currentToken: Token = this.tokens[this.tokens.length - 1];
if (currentToken.type === TokenType.TAG_OPEN_END ||
currentToken.type === TokenType.TAG_OPEN_END_VOID ||
currentToken.type === TokenType.TAG_CLOSE) {
// check if this closes any previous, react
}
if (currentToken.type === TokenType.TAG_OPEN_END) {
if (this._inNonBindableElement()) {
this._nonBindableElementDepth++;
}
else if (/* tag contains ngNonBindable attr */){
this._nonBindableElementDepth = 1;
}
}
else if (currentToken.type === TokenType.TAG_CLOSE) {
if (this._inNonBindableElement()) {
this._nonBindableElementDepth--;
}
}
};
Could you please make sure that we have integration test for
Thanks ! |
@vicb changed the approach to keep (and cull) an tag token array, which ended up cleaner (code all in one place). None of the original code was kept, so please re-review as fresh. |
@@ -423,6 +426,7 @@ class _Tokenizer { | |||
let savedPos = this._savePosition(); | |||
let tagName: string; | |||
let lowercaseTagName: string; | |||
let attributeStart: number; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unused
private _inNonBindableElement(): boolean { return this._nonBindableElementArr.length > 0; } | ||
|
||
private _checkNonBindableElementDepth() { | ||
let currentToken: Token = this.tokens[this.tokens.length - 1]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_check...
makes me expect a boolean or a throw.
- find a better name
- add some inline docs
// ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const currentToken
private _checkNonBindableElementDepth() { | ||
let currentToken: Token = this.tokens[this.tokens.length - 1]; | ||
|
||
if (this._inNonBindableElement() && (currentToken.type === TokenType.TAG_OPEN_END || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
=== || === || ===
should probably be extracted in a fn
this.tokens, t => t.type === TokenType.TAG_OPEN_START || t.type === TokenType.TAG_CLOSE); | ||
} | ||
|
||
private _NonBindableAttrInCurrentElement(): boolean { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_non
@@ -711,3 +779,15 @@ function mergeTextTokens(srcTokens: Token[]): Token[] { | |||
|
|||
return dstTokens; | |||
} | |||
|
|||
function findPreviousToken( | |||
array: Token[], filter: (token: Token) => boolean, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
filter -> predicate ?
|
||
function findPreviousToken( | ||
array: Token[], filter: (token: Token) => boolean, | ||
stopWhen: (token: Token) => boolean = t => false): Token { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lastPredicate
array: Token[], filter: (token: Token) => boolean, | ||
stopWhen: (token: Token) => boolean = t => false): Token { | ||
let index = array.length - 1; | ||
while (index >= 0 && !stopWhen(array[index])) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for ?
Added some comments but I'm not sure if this logic belongs here or in the parser. Have you tried / thought about detecting ICUs in the parser ? |
Fixed the code according to your comments. Re: refactor: |
Also - The failed check seems unrelated to this work - is this correct? |
@vicb, can you verify this PR? I disagree with the need for moving responsibilities, and assume the test failure was incidental and not related. |
We are closing this PR because it is unclear whether this is the right fix and we need to finalize the design before committing to a solution. |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Please check if the PR fulfills these requirements
What kind of change does this PR introduce? (check one with "x")
What is the current behavior? (You can also link to an open issue here)
See #11859
Does this PR introduce a breaking change? (check one with "x")
If this PR contains a breaking change, please describe the impact and migration path for existing applications: ...
Other information:
fixes #11859