Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions goldens/public-api/compiler-cli/compiler_options.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// @public
export interface BazelAndG3Options {
annotateForClosureCompiler?: boolean;
enableTemplateSourceLocations?: boolean;
_experimentalAllowEmitDeclarationOnly?: boolean;
generateDeepReexports?: boolean;
generateExtraImportsInLocalMode?: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,11 @@ export interface BazelAndG3Options {
* Defaults to `false`.
*/
legacyOptionalChaining?: boolean;

/**
* Internal option for enabling template source location metadata.
*/
enableTemplateSourceLocations?: boolean;
}

/**
Expand Down
9 changes: 8 additions & 1 deletion packages/compiler-cli/src/ngtsc/core/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
* found in the LICENSE file at https://angular.dev/license
*/

import {LEGACY_OPTIONAL_CHAINING_DEFAULT, TypeCheckingConfig} from '@angular/compiler';
import {
LEGACY_OPTIONAL_CHAINING_DEFAULT,
setEnableTemplateSourceLocations,
TypeCheckingConfig,
} from '@angular/compiler';
import ts from 'typescript';

import {
Expand Down Expand Up @@ -485,6 +489,9 @@ export class NgCompiler {
...verifyCompatibleTypeCheckOptions(this.options),
...verifyEmitDeclarationOnly(this.options),
);
if (this.options.enableTemplateSourceLocations) {
setEnableTemplateSourceLocations(true);
}

this.currentProgram = inputProgram;
this.closureCompilerEnabled = !!this.options.annotateForClosureCompiler;
Expand Down
16 changes: 8 additions & 8 deletions packages/compiler-cli/test/ngtsc/attach_source_location_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ runInEachFileSystem(() => {
);
env.driveMain();
const content = env.getContents('test.js');
expect(content).toContain('ɵɵdomElementStart(0, "div")(1, "span")(2, "strong");');
expect(content).toContain('ɵɵdomElementStart(0, "div", 0)(1, "span", 1)(2, "strong", 2);');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did these additional arguments show up?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it's because there are additional attributes on the elements now, because of the new data-sourcecode-loc attributes being added when the flag that controls this feature is enabled

expect(content).toContain(
'ɵɵattachSourceLocations("test.ts", [[0, 114, 5, 14], [1, 119, 5, 19], [2, 142, 6, 16]]);',
);
Expand All @@ -72,7 +72,7 @@ runInEachFileSystem(() => {
);
env.driveMain();
const content = env.getContents('test.js');
expect(content).toContain('ɵɵdomElementStart(0, "div")(1, "span")(2, "strong");');
expect(content).toContain('ɵɵdomElementStart(0, "div", 0)(1, "span", 1)(2, "strong", 2);');
expect(content).toContain(
'ɵɵattachSourceLocations("test.html", [[0, 9, 1, 8], [1, 14, 1, 13], [2, 31, 2, 10]]);',
);
Expand All @@ -99,8 +99,8 @@ runInEachFileSystem(() => {
env.driveMain();
const content = env.getContents('test.js');
expect(content).toContain('ɵɵtemplate(0,');
expect(content).toContain('ɵɵelementStart(0, "div");');
expect(content).toContain('ɵɵelement(1, "span");');
expect(content).toContain('ɵɵelementStart(0, "div", 1);');
expect(content).toContain('ɵɵelement(1, "span", 2);');
expect(content).toContain(
'ɵɵattachSourceLocations("test.ts", [[0, 207, 7, 14], [1, 242, 8, 16]]);',
);
Expand Down Expand Up @@ -128,10 +128,10 @@ runInEachFileSystem(() => {
);
env.driveMain();
const content = env.getContents('test.js');
expect(content).toContain('ɵɵdomElementContainerStart(0);');
expect(content).toContain('ɵɵdomElementStart(1, "div");');
expect(content).toContain('ɵɵdomElementContainerStart(2);');
expect(content).toContain('ɵɵdomElement(3, "span");');
expect(content).toContain('ɵɵdomElementContainerStart(0, 0);');
expect(content).toContain('ɵɵdomElementStart(1, "div", 1);');
expect(content).toContain('ɵɵdomElementContainerStart(2, 2);');
expect(content).toContain('ɵɵdomElement(3, "span", 3);');
expect(content).toContain(
'ɵɵattachSourceLocations("test.ts", [[1, 145, 6, 16], [3, 204, 8, 20]]);',
);
Expand Down
51 changes: 51 additions & 0 deletions packages/compiler-cli/test/ngtsc/attach_sourcecode_loc_spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/

import {setEnableTemplateSourceLocations} from '@angular/compiler';
import {runInEachFileSystem} from '../../src/ngtsc/file_system/testing';
import {loadStandardTestFiles} from '../../src/ngtsc/testing';
import {NgtscTestEnvironment} from './env';

const testFiles = loadStandardTestFiles({fakeCommon: true});

runInEachFileSystem(() => {
describe('attachSourcecodeLoc phase', () => {
let env!: NgtscTestEnvironment;

beforeEach(() => {
setEnableTemplateSourceLocations(true);
env = NgtscTestEnvironment.setup(testFiles);
env.tsconfig();
});

afterEach(() => {
setEnableTemplateSourceLocations(false);
});

it('should attach the sourcecode location as a data attribute', () => {
env.write(
`test.ts`,
`
import {Component} from '@angular/core';

@Component({
template: \`<div><span>Hello</span></div>\`,
})
class Comp {}
`,
);
env.driveMain();
const content = env.getContents('test.js');

// The div
expect(content).toContain(`["data-sourcecode-loc", "test.ts;l=5-5;c=24-53"]`);
// The span
expect(content).toContain(`["data-sourcecode-loc", "test.ts;l=5-5;c=29-47"]`);
});
});
});
4 changes: 4 additions & 0 deletions packages/compiler/src/template/pipeline/src/emit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ import {transformTwoWayBindingSet} from './phases/transform_two_way_binding_set'
import {countVariables} from './phases/var_counting';
import {optimizeVariables} from './phases/variable_optimization';
import {wrapI18nIcus} from './phases/wrap_icus';
import {attachSourcecodeLoc} from './phases/attach_sourcecode_loc';

type Phase =
| {
Expand All @@ -106,6 +107,9 @@ type Phase =
};

const phases: Phase[] = [
// attachSourcecodeLoc should run before any other phases that modify the
// template.
{kind: Kind.Tmpl, fn: attachSourcecodeLoc},
{kind: Kind.Tmpl, fn: removeContentSelectors},
{kind: Kind.Both, fn: optimizeRegularExpressions},
{kind: Kind.Host, fn: parseHostStyleProperties},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/

import {SecurityContext} from '../../../../core';
import * as o from '../../../../output/output_ast';
import * as ir from '../../ir';
import type {ComponentCompilationJob} from '../compilation';

/**

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't we reuse the existing attachSourceLocations here? As far as I can tell the main differences here are:

  1. We attach more information here (e.g. both start and end positions).
  2. We can call the attribute something different.
  3. The formatting is different.

We only have one user of this internally so we can work with them to account for any changes we make to the formatting.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh cool, I wasn't sure that there weren't more users. I'm coordinating with that user for them to suppor data-sourcecode-loc. Follow along at https://b.corp.google.com/510157286

* Inserts the data-sourcecode-loc attribute onto elements in templates.
*
* This is similar to the ./attach_source_locations phase, but this phase
* attaches more precise source locations, and it emits them directly into
* the HTML in a data attribute.
*
* This protocol is more useful for framework-agnostic tooling.
*/
export function attachSourcecodeLoc(job: ComponentCompilationJob): void {
if (!job.enableDebugLocations || job.relativeTemplatePath === null) {
return;
}

const fileName = job.relativeTemplatePath;
for (const unit of job.units) {
for (const op of unit.create) {
if (op.kind === ir.OpKind.ElementStart || op.kind === ir.OpKind.Element) {
const startLine = op.wholeSourceSpan.start.line + 1;
const startCol = op.wholeSourceSpan.start.col + 1;
const endLine = op.wholeSourceSpan.end.line + 1;
const endCol = op.wholeSourceSpan.end.col + 1;

const locString = `${fileName};l=${startLine}-${endLine};c=${startCol}-${endCol}`;

const extractedAttributeOp = ir.createExtractedAttributeOp(
op.xref,
ir.BindingKind.Attribute,
null,
'data-sourcecode-loc',
o.literal(locString),
null,
null,
SecurityContext.NONE,
);
unit.create.push(extractedAttributeOp);
}
}
}
}
Loading