Skip to content

Commit

Permalink
refactor(compiler): enable register and resolve phases for local …
Browse files Browse the repository at this point in the history
…compilation (#53901)

This commit update the logic to enable `register` and `resolve` phases for local compilation. Those phases will be useful for local compilation in certain cases (will be used in followup PRs).

PR Close #53901
  • Loading branch information
AndrewKushnir authored and dylhunn committed Jan 17, 2024
1 parent 6f6ad02 commit b07c549
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,10 @@ export class ComponentDecoratorHandler implements
}

register(node: ClassDeclaration, analysis: ComponentAnalysisData): void {
if (this.compilationMode === CompilationMode.LOCAL) {
return;
}

// Register this component's information with the `MetadataRegistry`. This ensures that
// the information about the component is available during the compile() phase.
const ref = new Reference(node);
Expand Down Expand Up @@ -652,6 +656,10 @@ export class ComponentDecoratorHandler implements
resolve(
node: ClassDeclaration, analysis: Readonly<ComponentAnalysisData>,
symbol: ComponentSymbol): ResolveResult<ComponentResolutionData> {
if (this.compilationMode === CompilationMode.LOCAL) {
return {};
}

if (this.semanticDepGraphUpdater !== null && analysis.baseClass instanceof Reference) {
symbol.baseClass = this.semanticDepGraphUpdater.getSymbol(analysis.baseClass.node);
}
Expand Down Expand Up @@ -1100,7 +1108,7 @@ export class ComponentDecoratorHandler implements

compileLocal(
node: ClassDeclaration, analysis: Readonly<ComponentAnalysisData>,
pool: ConstantPool): CompileResult[] {
resolution: Readonly<Partial<ComponentResolutionData>>, pool: ConstantPool): CompileResult[] {
if (analysis.template.errors !== null && analysis.template.errors.length > 0) {
return [];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ export class DirectiveDecoratorHandler implements
}

register(node: ClassDeclaration, analysis: Readonly<DirectiveHandlerData>): void {
if (this.compilationMode === CompilationMode.LOCAL) {
return;
}

// Register this directive's information with the `MetadataRegistry`. This ensures that
// the information about the directive is available during the compile() phase.
const ref = new Reference(node);
Expand Down Expand Up @@ -186,6 +190,10 @@ export class DirectiveDecoratorHandler implements

resolve(node: ClassDeclaration, analysis: DirectiveHandlerData, symbol: DirectiveSymbol):
ResolveResult<unknown> {
if (this.compilationMode === CompilationMode.LOCAL) {
return {};
}

if (this.semanticDepGraphUpdater !== null && analysis.baseClass instanceof Reference) {
symbol.baseClass = this.semanticDepGraphUpdater.getSymbol(analysis.baseClass.node);
}
Expand Down Expand Up @@ -246,7 +254,7 @@ export class DirectiveDecoratorHandler implements

compileLocal(
node: ClassDeclaration, analysis: Readonly<DirectiveHandlerData>,
pool: ConstantPool): CompileResult[] {
resolution: Readonly<unknown>, pool: ConstantPool): CompileResult[] {
const fac = compileNgFactoryDefField(toFactoryMetadata(analysis.meta, FactoryTarget.Directive));
const def = compileDirectiveFromMetadata(analysis.meta, pool, makeBindingParser());
const inputTransformFields = compileInputTransformFields(analysis.inputs);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,10 @@ export class NgModuleDecoratorHandler implements
}

register(node: ClassDeclaration, analysis: NgModuleAnalysis): void {
if (this.compilationMode === CompilationMode.LOCAL) {
return;
}

// Register this module's information with the LocalModuleScopeRegistry. This ensures that
// during the compile() phase, the module's metadata is available for selector scope
// computation.
Expand All @@ -555,6 +559,10 @@ export class NgModuleDecoratorHandler implements

resolve(node: ClassDeclaration, analysis: Readonly<NgModuleAnalysis>):
ResolveResult<NgModuleResolution> {
if (this.compilationMode === CompilationMode.LOCAL) {
return {};
}

const scope = this.scopeRegistry.getScopeOfModule(node);
const diagnostics: ts.Diagnostic[] = [];

Expand Down
10 changes: 9 additions & 1 deletion packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,21 @@ export class InjectableDecoratorHandler implements
}

register(node: ClassDeclaration, analysis: InjectableHandlerData): void {
if (this.compilationMode === CompilationMode.LOCAL) {
return;
}

this.injectableRegistry.registerInjectable(node, {
ctorDeps: analysis.ctorDeps,
});
}

resolve(node: ClassDeclaration, analysis: Readonly<InjectableHandlerData>, symbol: null):
resolve(node: ClassDeclaration, analysis: Readonly<InjectableHandlerData>):
ResolveResult<unknown> {
if (this.compilationMode === CompilationMode.LOCAL) {
return {};
}

if (requiresValidCtor(analysis.meta)) {
const diagnostic = checkInheritanceOfInjectable(
node, this.injectableRegistry, this.reflector, this.evaluator, this.strictCtorDeps,
Expand Down
8 changes: 8 additions & 0 deletions packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ export class PipeDecoratorHandler implements
}

register(node: ClassDeclaration, analysis: Readonly<PipeHandlerData>): void {
if (this.compilationMode === CompilationMode.LOCAL) {
return;
}

const ref = new Reference(node);
this.metaRegistry.registerPipeMetadata({
kind: MetaKind.Pipe,
Expand All @@ -170,6 +174,10 @@ export class PipeDecoratorHandler implements
}

resolve(node: ClassDeclaration): ResolveResult<unknown> {
if (this.compilationMode === CompilationMode.LOCAL) {
return {};
}

const duplicateDeclData = this.scopeRegistry.getDuplicateDeclarations(node);
if (duplicateDeclData !== null) {
// This pipe was declared twice (or more).
Expand Down
5 changes: 3 additions & 2 deletions packages/compiler-cli/src/ngtsc/transform/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,9 @@ export interface DecoratorHandler<D, A, S extends SemanticSymbol|null, R> {
* Generates code based on each individual source file without using its
* dependencies (suitable for local dev edit/refresh workflow)
*/
compileLocal(node: ClassDeclaration, analysis: Readonly<A>, constantPool: ConstantPool):
CompileResult|CompileResult[];
compileLocal(
node: ClassDeclaration, analysis: Readonly<A>, resolution: Readonly<Partial<R>>,
constantPool: ConstantPool): CompileResult|CompileResult[];
}

/**
Expand Down
33 changes: 11 additions & 22 deletions packages/compiler-cli/src/ngtsc/transform/src/compilation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -411,20 +411,13 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {
}

const symbol = this.makeSymbolForTrait(trait.handler, clazz, result.analysis ?? null);
if (this.compilationMode !== CompilationMode.LOCAL && result.analysis !== undefined &&
trait.handler.register !== undefined) {
if (result.analysis !== undefined && trait.handler.register !== undefined) {
trait.handler.register(clazz, result.analysis);
}
trait = trait.toAnalyzed(result.analysis ?? null, result.diagnostics ?? null, symbol);
}

resolve(): void {
// No resolving needed for local compilation (only analysis and compile will be done in this
// mode)
if (this.compilationMode === CompilationMode.LOCAL) {
return;
}

const classes = this.classes.keys();
for (const clazz of classes) {
const record = this.classes.get(clazz)!;
Expand Down Expand Up @@ -484,7 +477,7 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {
* `ts.SourceFile`.
*/
typeCheck(sf: ts.SourceFile, ctx: TypeCheckContext): void {
if (!this.fileToClasses.has(sf)) {
if (!this.fileToClasses.has(sf) || this.compilationMode === CompilationMode.LOCAL) {
return;
}

Expand Down Expand Up @@ -596,24 +589,20 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {

for (const trait of record.traits) {
let compileRes: CompileResult|CompileResult[];

if (trait.state !== TraitState.Resolved || containsErrors(trait.analysisDiagnostics) ||
containsErrors(trait.resolveDiagnostics)) {
// Cannot compile a trait that is not resolved, or had any errors in its declaration.
continue;
}

if (this.compilationMode === CompilationMode.LOCAL) {
if (trait.state !== TraitState.Analyzed || trait.analysis === null ||
containsErrors(trait.analysisDiagnostics)) {
// Cannot compile a trait in local mode that is not analyzed, or had any errors in its
// declaration.
continue;
}
// `trait.analysis` is non-null asserted here because TypeScript does not recognize that
// `Readonly<unknown>` is nullable (as `unknown` itself is nullable) due to the way that
// `Readonly` works.
compileRes = trait.handler.compileLocal(clazz, trait.analysis!, constantPool);
compileRes =
trait.handler.compileLocal(clazz, trait.analysis!, trait.resolution!, constantPool);
} else {
if (trait.state !== TraitState.Resolved || containsErrors(trait.analysisDiagnostics) ||
containsErrors(trait.resolveDiagnostics)) {
// Cannot compile a trait in global mode that is not resolved, or had any errors in its
// declaration.
continue;
}
// `trait.resolution` is non-null asserted below because TypeScript does not recognize that
// `Readonly<unknown>` is nullable (as `unknown` itself is nullable) due to the way that
// `Readonly` works.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,21 +340,21 @@ runInEachFileSystem(() => {
}
}

it('should not run resolve phase', () => {
it('should invoke `resolve` phase', () => {
const contents = `
export class Test {}
`;
const handler = new TestDecoratorHandler();
spyOn(handler, 'resolve');
spyOn(handler, 'resolve').and.callThrough();
const {compiler, sourceFile} = setup(contents, [handler], CompilationMode.LOCAL);

compiler.analyzeSync(sourceFile);
compiler.resolve();

expect(handler.resolve).not.toHaveBeenCalled();
expect(handler.resolve).toHaveBeenCalled();
});

it('should not register', () => {
it('should invoke `register` phase', () => {
const contents = `
export class Test {}
`;
Expand All @@ -365,7 +365,7 @@ runInEachFileSystem(() => {
compiler.analyzeSync(sourceFile);
compiler.resolve();

expect(handler.register).not.toHaveBeenCalled();
expect(handler.register).toHaveBeenCalled();
});

it('should not call extendedTemplateCheck', () => {
Expand Down

0 comments on commit b07c549

Please sign in to comment.