refactor(compiler): element.sourceSpan should span the outerHTML (#38581)

Previously, the `sourceSpan` and `startSourceSpan` were the same
object, which meant that you had the following situation:

```
element = <div>some content</div>
sourceSpan = <div>
startSourceSpan = <div>
endSourceSpan = </div>
```

This made `sourceSpan` redundant and meant that if you
wanted a span for the whole element including its content
and closing tag, it had to be computed.

Now `sourceSpan` is separated from `startSourceSpan`
resulting in:

```
element = <div>some content</div>
sourceSpan = <div>some content</div>
startSourceSpan = <div>
endSourceSpan = </div>
```

PR Close #38581
This commit is contained in:
Pete Bacon Darwin
2020-08-26 11:56:38 +01:00
committed by Andrew Scott
parent a68f1a78a7
commit 1d8c5d88cd
11 changed files with 59 additions and 43 deletions

View File

@ -322,19 +322,28 @@ import {serializeNodes as serializeHtmlNodes} from '../ml_parser/util/util';
describe('elements', () => {
it('should report nested translatable elements', () => {
expect(extractErrors(`<p i18n><b i18n></b></p>`)).toEqual([
['Could not mark an element as translatable inside a translatable section', '<b i18n>'],
[
'Could not mark an element as translatable inside a translatable section',
'<b i18n></b>'
],
]);
});
it('should report translatable elements in implicit elements', () => {
expect(extractErrors(`<p><b i18n></b></p>`, ['p'])).toEqual([
['Could not mark an element as translatable inside a translatable section', '<b i18n>'],
[
'Could not mark an element as translatable inside a translatable section',
'<b i18n></b>'
],
]);
});
it('should report translatable elements in translatable blocks', () => {
expect(extractErrors(`<!-- i18n --><b i18n></b><!-- /i18n -->`)).toEqual([
['Could not mark an element as translatable inside a translatable section', '<b i18n>'],
[
'Could not mark an element as translatable inside a translatable section',
'<b i18n></b>'
],
]);
});
});
@ -370,7 +379,7 @@ import {serializeNodes as serializeHtmlNodes} from '../ml_parser/util/util';
it('should report when start and end of a block are not at the same level', () => {
expect(extractErrors(`<!-- i18n --><p><!-- /i18n --></p>`)).toEqual([
['I18N blocks should not cross element boundaries', '<!--'],
['Unclosed block', '<p>'],
['Unclosed block', '<p><!-- /i18n --></p>'],
]);
expect(extractErrors(`<p><!-- i18n --></p><!-- /i18n -->`)).toEqual([

View File

@ -653,7 +653,8 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe
'<div [prop]="v1" (e)="do()" attr="v2" noValue>\na\n</div>', 'TestComp')))
.toEqual([
[
html.Element, 'div', 0, '<div [prop]="v1" (e)="do()" attr="v2" noValue>',
html.Element, 'div', 0,
'<div [prop]="v1" (e)="do()" attr="v2" noValue>\na\n</div>',
'<div [prop]="v1" (e)="do()" attr="v2" noValue>', '</div>'
],
[html.Attribute, '[prop]', 'v1', '[prop]="v1"'],
@ -676,14 +677,14 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe
it('should not set the end source span for void elements', () => {
expect(humanizeDomSourceSpans(parser.parse('<div><br></div>', 'TestComp'))).toEqual([
[html.Element, 'div', 0, '<div>', '<div>', '</div>'],
[html.Element, 'div', 0, '<div><br></div>', '<div>', '</div>'],
[html.Element, 'br', 1, '<br>', '<br>', null],
]);
});
it('should not set the end source span for multiple void elements', () => {
expect(humanizeDomSourceSpans(parser.parse('<div><br><hr></div>', 'TestComp'))).toEqual([
[html.Element, 'div', 0, '<div>', '<div>', '</div>'],
[html.Element, 'div', 0, '<div><br><hr></div>', '<div>', '</div>'],
[html.Element, 'br', 1, '<br>', '<br>', null],
[html.Element, 'hr', 1, '<hr>', '<hr>', null],
]);
@ -703,19 +704,19 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe
it('should set the end source span for self-closing elements', () => {
expect(humanizeDomSourceSpans(parser.parse('<div><br/></div>', 'TestComp'))).toEqual([
[html.Element, 'div', 0, '<div>', '<div>', '</div>'],
[html.Element, 'div', 0, '<div><br/></div>', '<div>', '</div>'],
[html.Element, 'br', 1, '<br/>', '<br/>', '<br/>'],
]);
});
it('should not set the end source span for elements that are implicitly closed', () => {
expect(humanizeDomSourceSpans(parser.parse('<div><p></div>', 'TestComp'))).toEqual([
[html.Element, 'div', 0, '<div>', '<div>', '</div>'],
[html.Element, 'div', 0, '<div><p></div>', '<div>', '</div>'],
[html.Element, 'p', 1, '<p>', '<p>', null],
]);
expect(humanizeDomSourceSpans(parser.parse('<div><li>A<li>B</div>', 'TestComp')))
.toEqual([
[html.Element, 'div', 0, '<div>', '<div>', '</div>'],
[html.Element, 'div', 0, '<div><li>A<li>B</div>', '<div>', '</div>'],
[html.Element, 'li', 1, '<li>', '<li>', null],
[html.Text, 'A', 2, 'A'],
[html.Element, 'li', 1, '<li>', '<li>', null],
@ -728,7 +729,7 @@ import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './ast_spe
'<div>{count, plural, =0 {msg}}</div>', 'TestComp',
{tokenizeExpansionForms: true})))
.toEqual([
[html.Element, 'div', 0, '<div>', '<div>', '</div>'],
[html.Element, 'div', 0, '<div>{count, plural, =0 {msg}}</div>', '<div>', '</div>'],
[html.Expansion, 'count', 'plural', 1, '{count, plural, =0 {msg}}'],
[html.ExpansionCase, '=0', 2, '=0 {msg}'],
]);

View File

@ -173,7 +173,7 @@ describe('R3 AST source spans', () => {
describe('templates', () => {
it('is correct for * directives', () => {
expectFromHtml('<div *ngIf></div>').toEqual([
['Template', '0:11', '0:11', '11:17'],
['Template', '0:17', '0:11', '11:17'],
['TextAttribute', '5:10', '<empty>'],
['Element', '0:17', '0:11', '11:17'],
]);
@ -181,48 +181,48 @@ describe('R3 AST source spans', () => {
it('is correct for <ng-template>', () => {
expectFromHtml('<ng-template></ng-template>').toEqual([
['Template', '0:13', '0:13', '13:27'],
['Template', '0:27', '0:13', '13:27'],
]);
});
it('is correct for reference via #...', () => {
expectFromHtml('<ng-template #a></ng-template>').toEqual([
['Template', '0:16', '0:16', '16:30'],
['Template', '0:30', '0:16', '16:30'],
['Reference', '13:15', '<empty>'],
]);
});
it('is correct for reference with name', () => {
expectFromHtml('<ng-template #a="b"></ng-template>').toEqual([
['Template', '0:20', '0:20', '20:34'],
['Template', '0:34', '0:20', '20:34'],
['Reference', '13:19', '17:18'],
]);
});
it('is correct for reference via ref-...', () => {
expectFromHtml('<ng-template ref-a></ng-template>').toEqual([
['Template', '0:19', '0:19', '19:33'],
['Template', '0:33', '0:19', '19:33'],
['Reference', '13:18', '<empty>'],
]);
});
it('is correct for variables via let-...', () => {
expectFromHtml('<ng-template let-a="b"></ng-template>').toEqual([
['Template', '0:23', '0:23', '23:37'],
['Template', '0:37', '0:23', '23:37'],
['Variable', '13:22', '20:21'],
]);
});
it('is correct for attributes', () => {
expectFromHtml('<ng-template k1="v1"></ng-template>').toEqual([
['Template', '0:21', '0:21', '21:35'],
['Template', '0:35', '0:21', '21:35'],
['TextAttribute', '13:20', '17:19'],
]);
});
it('is correct for bound attributes', () => {
expectFromHtml('<ng-template [k1]="v1"></ng-template>').toEqual([
['Template', '0:23', '0:23', '23:37'],
['Template', '0:37', '0:23', '23:37'],
['BoundAttribute', '13:22', '19:21'],
]);
});
@ -236,7 +236,7 @@ describe('R3 AST source spans', () => {
// <div></div>
// </ng-template>
expectFromHtml('<div *ngFor="let item of items"></div>').toEqual([
['Template', '0:32', '0:32', '32:38'],
['Template', '0:38', '0:32', '32:38'],
['TextAttribute', '5:31', '<empty>'],
['BoundAttribute', '5:31', '25:30'], // *ngFor="let item of items" -> items
['Variable', '13:22', '<empty>'], // let item
@ -250,7 +250,7 @@ describe('R3 AST source spans', () => {
// <div></div>
// </ng-template>
expectFromHtml('<div *ngFor="item of items"></div>').toEqual([
['Template', '0:28', '0:28', '28:34'],
['Template', '0:34', '0:28', '28:34'],
['BoundAttribute', '5:27', '13:17'], // ngFor="item of items" -> item
['BoundAttribute', '5:27', '21:26'], // ngFor="item of items" -> items
['Element', '0:34', '0:28', '28:34'],
@ -259,7 +259,7 @@ describe('R3 AST source spans', () => {
it('is correct for variables via let ...', () => {
expectFromHtml('<div *ngIf="let a=b"></div>').toEqual([
['Template', '0:21', '0:21', '21:27'],
['Template', '0:27', '0:21', '21:27'],
['TextAttribute', '5:20', '<empty>'],
['Variable', '12:19', '18:19'], // let a=b -> b
['Element', '0:27', '0:21', '21:27'],
@ -268,7 +268,7 @@ describe('R3 AST source spans', () => {
it('is correct for variables via as ...', () => {
expectFromHtml('<div *ngIf="expr as local"></div>').toEqual([
['Template', '0:27', '0:27', '27:33'],
['Template', '0:33', '0:27', '27:33'],
['BoundAttribute', '5:26', '12:16'], // ngIf="expr as local" -> expr
['Variable', '6:25', '6:10'], // ngIf="expr as local -> ngIf
['Element', '0:33', '0:27', '27:33'],

View File

@ -2046,7 +2046,7 @@ Property binding a not used by any directive on an embedded template. Make sure
it('should support embedded template', () => {
expect(humanizeTplAstSourceSpans(parse('<ng-template></ng-template>', []))).toEqual([
[EmbeddedTemplateAst, '<ng-template>']
[EmbeddedTemplateAst, '<ng-template></ng-template>']
]);
});
@ -2058,14 +2058,14 @@ Property binding a not used by any directive on an embedded template. Make sure
it('should support references', () => {
expect(humanizeTplAstSourceSpans(parse('<div #a></div>', []))).toEqual([
[ElementAst, 'div', '<div #a>'], [ReferenceAst, 'a', null, '#a']
[ElementAst, 'div', '<div #a></div>'], [ReferenceAst, 'a', null, '#a']
]);
});
it('should support variables', () => {
expect(humanizeTplAstSourceSpans(parse('<ng-template let-a="b"></ng-template>', [])))
.toEqual([
[EmbeddedTemplateAst, '<ng-template let-a="b">'],
[EmbeddedTemplateAst, '<ng-template let-a="b"></ng-template>'],
[VariableAst, 'a', 'b', 'let-a="b"'],
]);
});
@ -2128,7 +2128,7 @@ Property binding a not used by any directive on an embedded template. Make sure
expect(humanizeTplAstSourceSpans(
parse('<svg><circle /><use xlink:href="Port" /></svg>', [tagSel, attrSel])))
.toEqual([
[ElementAst, ':svg:svg', '<svg>'],
[ElementAst, ':svg:svg', '<svg><circle /><use xlink:href="Port" /></svg>'],
[ElementAst, ':svg:circle', '<circle />'],
[DirectiveAst, tagSel, '<circle />'],
[ElementAst, ':svg:use', '<use xlink:href="Port" />'],
@ -2144,7 +2144,8 @@ Property binding a not used by any directive on an embedded template. Make sure
inputs: ['aProp']
}).toSummary();
expect(humanizeTplAstSourceSpans(parse('<div [aProp]="foo"></div>', [dirA]))).toEqual([
[ElementAst, 'div', '<div [aProp]="foo">'], [DirectiveAst, dirA, '<div [aProp]="foo">'],
[ElementAst, 'div', '<div [aProp]="foo"></div>'],
[DirectiveAst, dirA, '<div [aProp]="foo"></div>'],
[BoundDirectivePropertyAst, 'aProp', 'foo', '[aProp]="foo"']
]);
});