| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 46fcfe0 commit f5aab2b
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -244,10 +244,10 @@ export class Parser { | |||
| 244 | 244 | ||
| 245 | 245 | atInterpolation = true; | |
| 246 | 246 | } else { | |
| 247 | - // parse from starting {{ to ending }} | ||
| 247 | + // parse from starting {{ to ending }} while ignoring content inside quotes. | ||
| 248 | 248 | const fullStart = i; | |
| 249 | 249 | const exprStart = fullStart + interpStart.length; | |
| 250 | - const exprEnd = input.indexOf(interpEnd, exprStart); | ||
| 250 | + const exprEnd = this._getExpressiondEndIndex(input, interpEnd, exprStart); | ||
| 251 | 251 | if (exprEnd === -1) { | |
| 252 | 252 | // Could not find the end of the interpolation; do not parse an expression. | |
| 253 | 253 | // Instead we should extend the content on the last raw string. | |
@@ -340,10 +340,39 @@ export class Parser { | |||
| 340 | 340 | ||
| 341 | 341 | return errLocation.length; | |
| 342 | 342 | } | |
| 343 | + | ||
| 344 | + /** | ||
| 345 | + * Finds the index of the end of an interpolation expression | ||
| 346 | + * while ignoring comments and quoted content. | ||
| 347 | + */ | ||
| 348 | + private _getExpressiondEndIndex(input: string, expressionEnd: string, start: number): number { | ||
| 349 | + let currentQuote: string|null = null; | ||
| 350 | + let escapeCount = 0; | ||
| 351 | + for (let i = start; i < input.length; i++) { | ||
| 352 | + const char = input[i]; | ||
| 353 | + // Skip the characters inside quotes. Note that we only care about the | ||
| 354 | + // outer-most quotes matching up and we need to account for escape characters. | ||
| 355 | + if (isQuote(input.charCodeAt(i)) && (currentQuote === null || currentQuote === char) && | ||
| 356 | + escapeCount % 2 === 0) { | ||
| 357 | + currentQuote = currentQuote === null ? char : null; | ||
| 358 | + } else if (currentQuote === null) { | ||
| 359 | + if (input.startsWith(expressionEnd, i)) { | ||
| 360 | + return i; | ||
| 361 | + } | ||
| 362 | + // Nothing else in the expression matters after we've | ||
| 363 | + // hit a comment so look directly for the end token. | ||
| 364 | + if (input.startsWith('//', i)) { | ||
| 365 | + return input.indexOf(expressionEnd, i); | ||
| 366 | + } | ||
| 367 | + } | ||
| 368 | + escapeCount = char === '\\' ? escapeCount + 1 : 0; | ||
| 369 | + } | ||
| 370 | + return -1; | ||
| 371 | + } | ||
| 343 | 372 | } | |
| 344 | 373 | ||
| 345 | 374 | export class IvyParser extends Parser { | |
| 346 | - simpleExpressionChecker = IvySimpleExpressionChecker; // | ||
| 375 | + simpleExpressionChecker = IvySimpleExpressionChecker; | ||
| 347 | 376 | } | |
| 348 | 377 | ||
| 349 | 378 | /** Describes a stateful context an expression parser is in. */ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -762,6 +762,37 @@ describe('parser', () => { | |||
| 762 | 762 | expect(ast.expressions[0].name).toEqual('a'); | |
| 763 | 763 | }); | |
| 764 | 764 | ||
| 765 | + it('should parse interpolation inside quotes', () => { | ||
| 766 | + const ast = parseInterpolation('"{{a}}"')!.ast as Interpolation; | ||
| 767 | + expect(ast.strings).toEqual(['"', '"']); | ||
| 768 | + expect(ast.expressions.length).toEqual(1); | ||
| 769 | + expect(ast.expressions[0].name).toEqual('a'); | ||
| 770 | + }); | ||
| 771 | + | ||
| 772 | + it('should parse interpolation with interpolation characters inside quotes', () => { | ||
| 773 | + checkInterpolation('{{"{{a}}"}}', '{{ "{{a}}" }}'); | ||
| 774 | + checkInterpolation('{{"{{"}}', '{{ "{{" }}'); | ||
| 775 | + checkInterpolation('{{"}}"}}', '{{ "}}" }}'); | ||
| 776 | + checkInterpolation('{{"{"}}', '{{ "{" }}'); | ||
| 777 | + checkInterpolation('{{"}"}}', '{{ "}" }}'); | ||
| 778 | + }); | ||
| 779 | + | ||
| 780 | + it('should parse interpolation with escaped quotes', () => { | ||
| 781 | + checkInterpolation(`{{'It\\'s just Angular'}}`, `{{ "It's just Angular" }}`); | ||
| 782 | + checkInterpolation(`{{'It\\'s {{ just Angular'}}`, `{{ "It's {{ just Angular" }}`); | ||
| 783 | + checkInterpolation(`{{'It\\'s }} just Angular'}}`, `{{ "It's }} just Angular" }}`); | ||
| 784 | + }); | ||
| 785 | + | ||
| 786 | + it('should parse interpolation with escaped backslashes', () => { | ||
| 787 | + checkInterpolation(`{{foo.split('\\\\')}}`, `{{ foo.split("\\") }}`); | ||
| 788 | + checkInterpolation(`{{foo.split('\\\\\\\\')}}`, `{{ foo.split("\\\\") }}`); | ||
| 789 | + checkInterpolation(`{{foo.split('\\\\\\\\\\\\')}}`, `{{ foo.split("\\\\\\") }}`); | ||
| 790 | + }); | ||
| 791 | + | ||
| 792 | + it('should not parse interpolation with mismatching quotes', () => { | ||
| 793 | + expect(parseInterpolation(`{{ "{{a}}' }}`)).toBeNull(); | ||
| 794 | + }); | ||
| 795 | + | ||
| 765 | 796 | it('should parse prefix/suffix with multiple interpolation', () => { | |
| 766 | 797 | const originalExp = 'before {{ a }} middle {{ b }} after'; | |
| 767 | 798 | const ast = parseInterpolation(originalExp)!.ast; | |
@@ -819,6 +850,10 @@ describe('parser', () => { | |||
| 819 | 850 | it('should retain // in nested, unterminated strings', () => { | |
| 820 | 851 | checkInterpolation(`{{ "a\'b\`" //comment}}`, `{{ "a\'b\`" }}`); | |
| 821 | 852 | }); | |
| 853 | + | ||
| 854 | + it('should ignore quotes inside a comment', () => { | ||
| 855 | + checkInterpolation(`"{{name // " }}"`, `"{{ name }}"`); | ||
| 856 | + }); | ||
| 822 | 857 | }); | |
| 823 | 858 | }); | |
| 824 | 859 | ||
@@ -999,8 +1034,11 @@ function parseSimpleBindingIvy( | |||
| 999 | 1034 | } | |
| 1000 | 1035 | ||
| 1001 | 1036 | function checkInterpolation(exp: string, expected?: string) { | |
| 1002 | - const ast = parseInterpolation(exp)!; | ||
| 1037 | + const ast = parseInterpolation(exp); | ||
| 1003 | 1038 | if (expected == null) expected = exp; | |
| 1039 | + if (ast === null) { | ||
| 1040 | + throw Error(`Failed to parse expression "${exp}"`); | ||
| 1041 | + } | ||
| 1004 | 1042 | expect(unparse(ast)).toEqual(expected); | |
| 1005 | 1043 | validate(ast); | |
| 1006 | 1044 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -540,6 +540,54 @@ describe('TemplateParser', () => { | |||
| 540 | 540 | expect(humanizeTplAst(parse('{{a}}', []))).toEqual([[BoundTextAst, '{{ a }}']]); | |
| 541 | 541 | }); | |
| 542 | 542 | ||
| 543 | + it('should parse bound text nodes inside quotes', () => { | ||
| 544 | + expect(humanizeTplAst(parse('"{{a}}"', []))).toEqual([[BoundTextAst, '"{{ a }}"']]); | ||
| 545 | + }); | ||
| 546 | + | ||
| 547 | + it('should parse bound text nodes with interpolations inside quotes', () => { | ||
| 548 | + expect(humanizeTplAst(parse('{{ "{{a}}" }}', []))).toEqual([[BoundTextAst, '{{ "{{a}}" }}']]); | ||
| 549 | + expect(humanizeTplAst(parse('{{"{{"}}', []))).toEqual([[BoundTextAst, '{{ "{{" }}']]); | ||
| 550 | + expect(humanizeTplAst(parse('{{"}}"}}', []))).toEqual([[BoundTextAst, '{{ "}}" }}']]); | ||
| 551 | + expect(humanizeTplAst(parse('{{"{"}}', []))).toEqual([[BoundTextAst, '{{ "{" }}']]); | ||
| 552 | + expect(humanizeTplAst(parse('{{"}"}}', []))).toEqual([[BoundTextAst, '{{ "}" }}']]); | ||
| 553 | + }); | ||
| 554 | + | ||
| 555 | + it('should parse bound text nodes with escaped quotes', () => { | ||
| 556 | + expect(humanizeTplAst(parse(`{{'It\\'s just Angular'}}`, []))).toEqual([ | ||
| 557 | + [BoundTextAst, `{{ "It's just Angular" }}`] | ||
| 558 | + ]); | ||
| 559 | + | ||
| 560 | + expect(humanizeTplAst(parse(`{{'It\\'s {{ just Angular'}}`, []))).toEqual([ | ||
| 561 | + [BoundTextAst, `{{ "It's {{ just Angular" }}`] | ||
| 562 | + ]); | ||
| 563 | + | ||
| 564 | + expect(humanizeTplAst(parse(`{{'It\\'s }} just Angular'}}`, []))).toEqual([ | ||
| 565 | + [BoundTextAst, `{{ "It's }} just Angular" }}`] | ||
| 566 | + ]); | ||
| 567 | + }); | ||
| 568 | + | ||
| 569 | + it('should not parse bound text nodes with mismatching quotes', () => { | ||
| 570 | + expect(humanizeTplAst(parse(`{{ "{{a}}' }}`, []))).toEqual([[TextAst, `{{ "{{a}}' }}`]]); | ||
| 571 | + }); | ||
| 572 | + | ||
| 573 | + it('should parse interpolation with escaped backslashes', () => { | ||
| 574 | + expect(humanizeTplAst(parse(`{{foo.split('\\\\')}}`, []))).toEqual([ | ||
| 575 | + [BoundTextAst, `{{ foo.split("\\") }}`] | ||
| 576 | + ]); | ||
| 577 | + expect(humanizeTplAst(parse(`{{foo.split('\\\\\\\\')}}`, []))).toEqual([ | ||
| 578 | + [BoundTextAst, `{{ foo.split("\\\\") }}`] | ||
| 579 | + ]); | ||
| 580 | + expect(humanizeTplAst(parse(`{{foo.split('\\\\\\\\\\\\')}}`, []))).toEqual([ | ||
| 581 | + [BoundTextAst, `{{ foo.split("\\\\\\") }}`] | ||
| 582 | + ]); | ||
| 583 | + }); | ||
| 584 | + | ||
| 585 | + it('should ignore quotes inside a comment', () => { | ||
| 586 | + expect(humanizeTplAst(parse(`"{{name // " }}"`, []))).toEqual([ | ||
| 587 | + [BoundTextAst, `"{{ name }}"`] | ||
| 588 | + ]); | ||
| 589 | + }); | ||
| 590 | + | ||
| 543 | 591 | it('should parse with custom interpolation config', | |
| 544 | 592 | inject([TemplateParser], (parser: TemplateParser) => { | |
| 545 | 593 | const component = CompileDirectiveMetadata.create({ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -171,4 +171,18 @@ describe('text instructions', () => { | |||
| 171 | 171 | // `Symbol(hello)_p.sc8s398cplk`, whereas the native one is `Symbol(hello)`. | |
| 172 | 172 | expect(fixture.nativeElement.textContent).toContain('Symbol(hello)'); | |
| 173 | 173 | }); | |
| 174 | + | ||
| 175 | + it('should handle binding syntax used inside quoted text', () => { | ||
| 176 | + @Component({ | ||
| 177 | + template: `{{'Interpolations look like {{this}}'}}`, | ||
| 178 | + }) | ||
| 179 | + class App { | ||
| 180 | + } | ||
| 181 | + | ||
| 182 | + TestBed.configureTestingModule({declarations: [App]}); | ||
| 183 | + const fixture = TestBed.createComponent(App); | ||
| 184 | + fixture.detectChanges(); | ||
| 185 | + | ||
| 186 | + expect(fixture.nativeElement.textContent).toBe('Interpolations look like {{this}}'); | ||
| 187 | + }); | ||
| 174 | 188 | }); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments