mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
refactor(compiler-cli): introduce infrastructure for running additional checks against TypeScript files (#54993)
Adds the new `SourceFileValidator` that will be used to check for file-level issues that may prevent Angular from working, like invoking the `input()` function outside of an initializer. Currently only one check is planned, but this setup will allow us to easily add more in the future. PR Close #54993
This commit is contained in:
committed by
Dylan Hunn
parent
3d9f01c6ec
commit
8226be6abf
@@ -42,6 +42,7 @@ ts_library(
|
||||
"//packages/compiler-cli/src/ngtsc/typecheck/template_semantics",
|
||||
"//packages/compiler-cli/src/ngtsc/typecheck/template_semantics/api",
|
||||
"//packages/compiler-cli/src/ngtsc/util",
|
||||
"//packages/compiler-cli/src/ngtsc/validation",
|
||||
"//packages/compiler-cli/src/ngtsc/xi18n",
|
||||
"@npm//@types/semver",
|
||||
"@npm//semver",
|
||||
|
||||
@@ -37,6 +37,7 @@ import {ExtendedTemplateChecker} from '../../typecheck/extended/api';
|
||||
import {TemplateSemanticsChecker} from '../../typecheck/template_semantics/api/api';
|
||||
import {TemplateSemanticsCheckerImpl} from '../../typecheck/template_semantics/src/template_semantics_checker';
|
||||
import {getSourceFileOrNull, isDtsPath, toUnredirectedSourceFile} from '../../util/src/typescript';
|
||||
import {SourceFileValidator} from '../../validation';
|
||||
import {Xi18nContext} from '../../xi18n';
|
||||
import {DiagnosticCategoryLabel, NgCompilerAdapter, NgCompilerOptions} from '../api';
|
||||
|
||||
@@ -62,6 +63,7 @@ interface LazyCompilationState {
|
||||
resourceRegistry: ResourceRegistry;
|
||||
extendedTemplateChecker: ExtendedTemplateChecker|null;
|
||||
templateSemanticsChecker: TemplateSemanticsChecker|null;
|
||||
sourceFileValidator: SourceFileValidator|null;
|
||||
|
||||
/**
|
||||
* Only available in local compilation mode when option `generateExtraImportsInLocalMode` is set.
|
||||
@@ -978,10 +980,17 @@ export class NgCompiler {
|
||||
private runAdditionalChecks(sf?: ts.SourceFile): ts.Diagnostic[] {
|
||||
const diagnostics: ts.Diagnostic[] = [];
|
||||
const compilation = this.ensureAnalyzed();
|
||||
const {extendedTemplateChecker, templateSemanticsChecker} = compilation;
|
||||
const {extendedTemplateChecker, templateSemanticsChecker, sourceFileValidator} = compilation;
|
||||
const files = sf ? [sf] : this.inputProgram.getSourceFiles();
|
||||
|
||||
for (const sf of files) {
|
||||
if (sourceFileValidator !== null) {
|
||||
const sourceFileDiagnostics = sourceFileValidator.getDiagnosticsForFile(sf);
|
||||
if (sourceFileDiagnostics !== null) {
|
||||
diagnostics.push(...sourceFileDiagnostics);
|
||||
}
|
||||
}
|
||||
|
||||
if (templateSemanticsChecker !== null) {
|
||||
diagnostics.push(...compilation.traitCompiler.runAdditionalChecks(sf, (clazz, handler) => {
|
||||
return handler.templateSemanticsCheck?.(clazz, templateSemanticsChecker) || null;
|
||||
@@ -1239,6 +1248,10 @@ export class NgCompiler {
|
||||
new TemplateSemanticsCheckerImpl(templateTypeChecker) :
|
||||
null;
|
||||
|
||||
const sourceFileValidator = this.constructionDiagnostics.length === 0 ?
|
||||
new SourceFileValidator(reflector, importTracker) :
|
||||
null;
|
||||
|
||||
return {
|
||||
isCore,
|
||||
traitCompiler,
|
||||
@@ -1255,6 +1268,7 @@ export class NgCompiler {
|
||||
extendedTemplateChecker,
|
||||
localCompilationExtraImportsTracker,
|
||||
templateSemanticsChecker,
|
||||
sourceFileValidator,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
load("//tools:defaults.bzl", "ts_library")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
ts_library(
|
||||
name = "validation",
|
||||
srcs = glob(
|
||||
["**/*.ts"],
|
||||
),
|
||||
deps = [
|
||||
"//packages/compiler-cli/src/ngtsc/annotations",
|
||||
"//packages/compiler-cli/src/ngtsc/diagnostics",
|
||||
"//packages/compiler-cli/src/ngtsc/imports",
|
||||
"//packages/compiler-cli/src/ngtsc/reflection",
|
||||
"@npm//@types/node",
|
||||
"@npm//typescript",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* @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.io/license
|
||||
*/
|
||||
|
||||
export {SourceFileValidator} from './src/source_file_validator';
|
||||
@@ -0,0 +1,27 @@
|
||||
/*!
|
||||
* @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.io/license
|
||||
*/
|
||||
|
||||
import ts from 'typescript';
|
||||
|
||||
/**
|
||||
* Rule that checks a source file a specific issue.
|
||||
*/
|
||||
export interface SourceFileValidatorRule {
|
||||
/**
|
||||
* Whether the file should be checked. Used to stop the traversal of the file early.
|
||||
* @param sourceFile File to be checked.
|
||||
*/
|
||||
shouldCheck(sourceFile: ts.SourceFile): boolean;
|
||||
|
||||
/**
|
||||
* Produces diagnostics for a specific node that may
|
||||
* contain the issue that the rule is enforcing.
|
||||
* @param node Node to be checked.
|
||||
*/
|
||||
checkNode(node: ts.Node): ts.Diagnostic[]|null;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*!
|
||||
* @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.io/license
|
||||
*/
|
||||
|
||||
import ts from 'typescript';
|
||||
|
||||
import {ImportedSymbolsTracker} from '../../imports';
|
||||
import {ReflectionHost} from '../../reflection';
|
||||
|
||||
import {SourceFileValidatorRule} from './rules/api';
|
||||
|
||||
/**
|
||||
* Validates that TypeScript files match a specific set of rules set by the Angular compiler.
|
||||
*/
|
||||
export class SourceFileValidator {
|
||||
private rules: SourceFileValidatorRule[];
|
||||
|
||||
constructor(reflector: ReflectionHost, importedSymbolsTracker: ImportedSymbolsTracker) {
|
||||
this.rules = []; // TODO: implement the rules.
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the diagnostics for a specific file, or null if the file is valid.
|
||||
* @param sourceFile File to be checked.
|
||||
*/
|
||||
getDiagnosticsForFile(sourceFile: ts.SourceFile): ts.Diagnostic[]|null {
|
||||
if (sourceFile.isDeclarationFile || sourceFile.fileName.endsWith('.ngtypecheck.ts')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let rulesToRun: SourceFileValidatorRule[]|null = null;
|
||||
for (const rule of this.rules) {
|
||||
if (rule.shouldCheck(sourceFile)) {
|
||||
rulesToRun ??= [];
|
||||
rulesToRun.push(rule);
|
||||
}
|
||||
}
|
||||
|
||||
if (rulesToRun === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let fileDiagnostics: ts.Diagnostic[]|null = null;
|
||||
sourceFile.forEachChild(function walk(node) {
|
||||
// Note: non-null assertion is here because of g3.
|
||||
for (const rule of rulesToRun!) {
|
||||
const nodeDiagnostics = rule.checkNode(node);
|
||||
if (nodeDiagnostics !== null) {
|
||||
fileDiagnostics ??= [];
|
||||
fileDiagnostics.push(...nodeDiagnostics);
|
||||
}
|
||||
}
|
||||
node.forEachChild(walk);
|
||||
});
|
||||
|
||||
return fileDiagnostics;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user