fix(compiler-cli): return directives for an element on a microsyntax template (#42640)
When the template type checker try to get a symbol of a template node, it will not return the directives intended for an element on a microsyntax template, for example, `<div *ngFor="let user of users;" dir>`, the `dir` will be skipped, but it's needed in language service. Fixes https://github.com/angular/vscode-ng-language-service/issues/1420 PR Close #42640
This commit is contained in:
parent
bfa1b5d9eb
commit
74350a5cf1
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AST, ASTWithSource, BindingPipe, MethodCall, PropertyRead, PropertyWrite, SafeMethodCall, SafePropertyRead, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstElement, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstTextAttribute, TmplAstVariable} from '@angular/compiler';
|
||||
import {AST, ASTWithSource, BindingPipe, MethodCall, ParseSourceSpan, PropertyRead, PropertyWrite, SafeMethodCall, SafePropertyRead, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstElement, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstTextAttribute, TmplAstVariable} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath} from '../../file_system';
|
||||
|
@ -150,7 +150,24 @@ export class SymbolBuilder {
|
|||
private getDirectiveMeta(
|
||||
host: TmplAstTemplate|TmplAstElement,
|
||||
directiveDeclaration: ts.Declaration): TypeCheckableDirectiveMeta|null {
|
||||
const directives = this.templateData.boundTarget.getDirectivesOfNode(host);
|
||||
let directives = this.templateData.boundTarget.getDirectivesOfNode(host);
|
||||
|
||||
// `getDirectivesOfNode` will not return the directives intended for an element
|
||||
// on a microsyntax template, for example `<div *ngFor="let user of users;" dir>`,
|
||||
// the `dir` will be skipped, but it's needed in language service.
|
||||
const firstChild = host.children[0];
|
||||
if (firstChild instanceof TmplAstElement) {
|
||||
const isMicrosyntaxTemplate = host instanceof TmplAstTemplate &&
|
||||
sourceSpanEqual(firstChild.sourceSpan, host.sourceSpan);
|
||||
if (isMicrosyntaxTemplate) {
|
||||
const firstChildDirectives = this.templateData.boundTarget.getDirectivesOfNode(firstChild);
|
||||
if (firstChildDirectives !== null && directives !== null) {
|
||||
directives = directives.concat(firstChildDirectives);
|
||||
} else {
|
||||
directives = directives ?? firstChildDirectives;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (directives === null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -577,3 +594,7 @@ export class SymbolBuilder {
|
|||
function anyNodeFilter(n: ts.Node): n is ts.Node {
|
||||
return true;
|
||||
}
|
||||
|
||||
function sourceSpanEqual(a: ParseSourceSpan, b: ParseSourceSpan) {
|
||||
return a.start.offset === b.start.offset && a.end.offset === b.end.offset;
|
||||
}
|
||||
|
|
|
@ -223,8 +223,9 @@ runInEachFileSystem(() => {
|
|||
|
||||
beforeEach(() => {
|
||||
const fileName = absoluteFrom('/main.ts');
|
||||
const dirFile = absoluteFrom('/dir.ts');
|
||||
const templateString = `
|
||||
<div *ngFor="let user of users; let i = index;">
|
||||
<div *ngFor="let user of users; let i = index;" dir>
|
||||
{{user.name}} {{user.streetNumber}}
|
||||
<div [tabIndex]="i"></div>
|
||||
</div>`;
|
||||
|
@ -239,9 +240,23 @@ runInEachFileSystem(() => {
|
|||
}
|
||||
export class Cmp { users: User[]; }
|
||||
`,
|
||||
declarations: [ngForDeclaration()],
|
||||
declarations: [
|
||||
ngForDeclaration(),
|
||||
{
|
||||
name: 'TestDir',
|
||||
selector: '[dir]',
|
||||
file: dirFile,
|
||||
type: 'directive',
|
||||
inputs: {name: 'name'}
|
||||
},
|
||||
],
|
||||
},
|
||||
ngForTypeCheckTarget(),
|
||||
{
|
||||
fileName: dirFile,
|
||||
source: `export class TestDir {name:string}`,
|
||||
templates: {},
|
||||
},
|
||||
]);
|
||||
templateTypeChecker = testValues.templateTypeChecker;
|
||||
program = testValues.program;
|
||||
|
@ -250,6 +265,13 @@ runInEachFileSystem(() => {
|
|||
templateNode = getAstTemplates(templateTypeChecker, cmp)[0];
|
||||
});
|
||||
|
||||
it('should retrieve a symbol for a directive on a microsyntax template', () => {
|
||||
const symbol = templateTypeChecker.getSymbolOfNode(templateNode, cmp);
|
||||
const testDir = symbol?.directives.find(dir => dir.selector === '[dir]');
|
||||
expect(testDir).toBeDefined();
|
||||
expect(program.getTypeChecker().symbolToString(testDir!.tsSymbol)).toEqual('TestDir');
|
||||
});
|
||||
|
||||
it('should retrieve a symbol for an expression inside structural binding', () => {
|
||||
const ngForOfBinding =
|
||||
templateNode.templateAttrs.find(a => a.name === 'ngForOf')! as TmplAstBoundAttribute;
|
||||
|
|
Loading…
Reference in New Issue