Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lexical context #13

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,9 @@ export class PartialComponentLinkerVersion1<TStatement, TExpression>
metaObj: AstObject<R3PartialDeclaration, TExpression>,
version: string,
): LinkedDefinition {
const meta = this.toR3ComponentMeta(metaObj, version);
return compileComponentFromMetadata(meta, constantPool, makeBindingParser());
// TODO
const meta = this.toR3ComponentMeta(metaObj as any, version);
return compileComponentFromMetadata(meta, constantPool, makeBindingParser(), []);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@ export class ComponentDecoratorHandler
analyze(
node: ClassDeclaration,
decorator: Readonly<Decorator>,
symbols: string[],
): AnalysisOutput<ComponentAnalysisData> {
this.perf.eventCount(PerfEvent.AnalyzeComponent);
const containingFile = node.getSourceFile().fileName;
Expand Down Expand Up @@ -904,6 +905,7 @@ export class ComponentDecoratorHandler
explicitlyDeferredTypes,
schemas,
decorator: (decorator?.node as ts.Decorator | null) ?? null,
symbols,
},
diagnostics,
};
Expand Down Expand Up @@ -958,6 +960,7 @@ export class ComponentDecoratorHandler
ngContentSelectors: analysis.template.ngContentSelectors,
preserveWhitespaces: analysis.template.preserveWhitespaces ?? false,
isExplicitlyDeferred: false,
symbols: analysis.symbols,
});

this.resourceRegistry.registerResources(analysis.resources, node);
Expand Down Expand Up @@ -1602,6 +1605,7 @@ export class ComponentDecoratorHandler
analysis: Readonly<ComponentAnalysisData>,
resolution: Readonly<ComponentResolutionData>,
pool: ConstantPool,
symbols: string[],
): CompileResult[] {
if (analysis.template.errors !== null && analysis.template.errors.length > 0) {
return [];
Expand All @@ -1621,7 +1625,7 @@ export class ComponentDecoratorHandler
removeDeferrableTypesFromComponentDecorator(analysis, perComponentDeferredDeps);
}

const def = compileComponentFromMetadata(meta, pool, makeBindingParser());
const def = compileComponentFromMetadata(meta, pool, makeBindingParser(), symbols);
const inputTransformFields = compileInputTransformFields(analysis.inputs);
const classMetadata =
analysis.classMetadata !== null
Expand Down Expand Up @@ -1728,6 +1732,7 @@ export class ComponentDecoratorHandler
analysis: Readonly<ComponentAnalysisData>,
resolution: Readonly<Partial<ComponentResolutionData>>,
pool: ConstantPool,
symbols: string[],
): CompileResult[] {
// In the local compilation mode we can only rely on the information available
// within the `@Component.deferredImports` array, because in this mode compiler
Expand All @@ -1745,7 +1750,7 @@ export class ComponentDecoratorHandler
}

const fac = compileNgFactoryDefField(toFactoryMetadata(meta, FactoryTarget.Component));
const def = compileComponentFromMetadata(meta, pool, makeBindingParser());
const def = compileComponentFromMetadata(meta, pool, makeBindingParser(), symbols);
const inputTransformFields = compileInputTransformFields(analysis.inputs);
const classMetadata =
analysis.classMetadata !== null
Expand Down Expand Up @@ -1787,6 +1792,7 @@ export class ComponentDecoratorHandler
node: ClassDeclaration,
analysis: Readonly<ComponentAnalysisData>,
resolution: Readonly<ComponentResolutionData>,
symbols: string[],
): ts.FunctionDeclaration | null {
if (analysis.template.errors !== null && analysis.template.errors.length > 0) {
return null;
Expand All @@ -1800,7 +1806,7 @@ export class ComponentDecoratorHandler
defer: this.compileDeferBlocks(resolution),
};
const fac = compileNgFactoryDefField(toFactoryMetadata(meta, FactoryTarget.Component));
const def = compileComponentFromMetadata(meta, pool, makeBindingParser());
const def = compileComponentFromMetadata(meta, pool, makeBindingParser(), symbols);
const classMetadata =
analysis.classMetadata !== null
? compileComponentClassMetadata(analysis.classMetadata, null).toStmt()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ export interface ComponentAnalysisData {
rawDeferredImports: ts.Expression | null;
resolvedDeferredImports: Reference<ClassDeclaration>[] | null;

symbols: string[] | null;

/**
* Map of symbol name -> import path for types from `@Component.deferredImports` field.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ export class DirectiveDecoratorHandler
// Instead, we statically analyze their imports to make a direct determination.
assumedToExportProviders: false,
isExplicitlyDeferred: false,
symbols: null,
});

this.injectableRegistry.registerInjectable(node, {
Expand Down
5 changes: 5 additions & 0 deletions packages/compiler-cli/src/ngtsc/metadata/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,11 @@ export interface DirectiveMeta extends T2DirectiveMeta, DirectiveTypeCheckMeta {
*/
deferredImports: Reference<ClassDeclaration>[] | null;

/**
* Imported symbols in the module where the directive is declared
*/
symbols: string[] | null;

/**
* For standalone components, the list of schemas declared.
*/
Expand Down
1 change: 1 addition & 0 deletions packages/compiler-cli/src/ngtsc/metadata/src/dts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ export class DtsMetadataReader implements MetadataReader {
// used to increase the accuracy of a diagnostic.
preserveWhitespaces: false,
isExplicitlyDeferred: false,
symbols: null,
};
}

Expand Down
8 changes: 6 additions & 2 deletions packages/compiler-cli/src/ngtsc/transform/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export interface DecoratorHandler<D, A, S extends SemanticSymbol | null, R> {
* builds. Any side effects required for compilation (e.g. registration of metadata) should happen
* in the `register` phase, which is guaranteed to run even for incremental builds.
*/
analyze(node: ClassDeclaration, metadata: Readonly<D>): AnalysisOutput<A>;
analyze(node: ClassDeclaration, metadata: Readonly<D>, symbols: string[]): AnalysisOutput<A>;

/**
* React to a change in a resource file by updating the `analysis` or `resolution`, under the
Expand Down Expand Up @@ -141,7 +141,7 @@ export interface DecoratorHandler<D, A, S extends SemanticSymbol | null, R> {
* Registration always occurs for a given decorator/class, regardless of whether analysis was
* performed directly or whether the analysis results were reused from the previous program.
*/
register?(node: ClassDeclaration, analysis: A): void;
register?(node: ClassDeclaration, analysis: A, symbols: string[]): void;

/**
* Registers information about the decorator for the indexing phase in a
Expand Down Expand Up @@ -199,6 +199,7 @@ export interface DecoratorHandler<D, A, S extends SemanticSymbol | null, R> {
analysis: Readonly<A>,
resolution: Readonly<R>,
constantPool: ConstantPool,
symbols: string[],
): CompileResult | CompileResult[];

/**
Expand All @@ -213,6 +214,7 @@ export interface DecoratorHandler<D, A, S extends SemanticSymbol | null, R> {
node: ClassDeclaration,
analysis: Readonly<A>,
resolution: Readonly<R>,
symbols: string[],
): CompileResult | CompileResult[];

/**
Expand All @@ -222,6 +224,7 @@ export interface DecoratorHandler<D, A, S extends SemanticSymbol | null, R> {
node: ClassDeclaration,
analysis: Readonly<A>,
resolution: Readonly<R>,
symbols: string[],
): ts.FunctionDeclaration | null;

/**
Expand All @@ -233,6 +236,7 @@ export interface DecoratorHandler<D, A, S extends SemanticSymbol | null, R> {
analysis: Readonly<A>,
resolution: Readonly<Partial<R>>,
constantPool: ConstantPool,
symbols: string[],
): CompileResult | CompileResult[];
}

Expand Down
70 changes: 61 additions & 9 deletions packages/compiler-cli/src/ngtsc/transform/src/compilation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,11 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {
return;
}

const symbols = getImportedSymbols(sf);

const visit = (node: ts.Node): void => {
if (this.reflector.isClass(node)) {
this.analyzeClass(node, preanalyze ? promises : null);
this.analyzeClass(node, preanalyze ? promises : null, symbols);
}
ts.forEachChild(node, visit);
};
Expand Down Expand Up @@ -245,7 +247,7 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {
const symbol = this.makeSymbolForTrait(handler, record.node, priorTrait.analysis);
trait = trait.toAnalyzed(priorTrait.analysis, priorTrait.analysisDiagnostics, symbol);
if (trait.analysis !== null && trait.handler.register !== undefined) {
trait.handler.register(record.node, trait.analysis);
trait.handler.register(record.node, trait.analysis, []);
}
} else if (priorTrait.state === TraitState.Skipped) {
trait = trait.toSkipped();
Expand Down Expand Up @@ -411,7 +413,11 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {
return symbol;
}

private analyzeClass(clazz: ClassDeclaration, preanalyzeQueue: Promise<void>[] | null): void {
private analyzeClass(
clazz: ClassDeclaration,
preanalyzeQueue: Promise<void>[] | null,
symbols: string[],
): void {
const traits = this.scanClassForTraits(clazz);

if (traits === null) {
Expand All @@ -420,7 +426,7 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {
}

for (const trait of traits) {
const analyze = () => this.analyzeTrait(clazz, trait);
const analyze = () => this.analyzeTrait(clazz, trait, symbols);

let preanalysis: Promise<void> | null = null;
if (preanalyzeQueue !== null && trait.handler.preanalyze !== undefined) {
Expand Down Expand Up @@ -448,6 +454,7 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {
private analyzeTrait(
clazz: ClassDeclaration,
trait: Trait<unknown, unknown, SemanticSymbol | null, unknown>,
symbols: string[],
): void {
if (trait.state !== TraitState.Pending) {
throw new Error(
Expand All @@ -462,7 +469,7 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {
// Attempt analysis. This could fail with a `FatalDiagnosticError`; catch it if it does.
let result: AnalysisOutput<unknown>;
try {
result = trait.handler.analyze(clazz, trait.detected.metadata);
result = trait.handler.analyze(clazz, trait.detected.metadata, symbols);
} catch (err) {
if (err instanceof FatalDiagnosticError) {
trait.toAnalyzed(null, [err.toDiagnostic()], null);
Expand All @@ -474,7 +481,7 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {

const symbol = this.makeSymbolForTrait(trait.handler, clazz, result.analysis ?? null);
if (result.analysis !== undefined && trait.handler.register !== undefined) {
trait.handler.register(clazz, result.analysis);
trait.handler.register(clazz, result.analysis, symbols);
}
trait = trait.toAnalyzed(result.analysis ?? null, result.diagnostics ?? null, symbol);
}
Expand Down Expand Up @@ -647,7 +654,11 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {
}
}

compile(clazz: DeclarationNode, constantPool: ConstantPool): CompileResult[] | null {
compile(
clazz: DeclarationNode,
constantPool: ConstantPool,
symbols: string[],
): CompileResult[] | null {
const original = ts.getOriginalNode(clazz) as typeof clazz;
if (
!this.reflector.isClass(clazz) ||
Expand Down Expand Up @@ -682,6 +693,7 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {
trait.analysis!,
trait.resolution!,
constantPool,
symbols,
);
} else {
// `trait.resolution` is non-null asserted below because TypeScript does not recognize that
Expand All @@ -691,13 +703,14 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {
this.compilationMode === CompilationMode.PARTIAL &&
trait.handler.compilePartial !== undefined
) {
compileRes = trait.handler.compilePartial(clazz, trait.analysis, trait.resolution!);
compileRes = trait.handler.compilePartial(clazz, trait.analysis, trait.resolution!, []);
} else {
compileRes = trait.handler.compileFull(
clazz,
trait.analysis,
trait.resolution!,
constantPool,
symbols,
);
}
}
Expand Down Expand Up @@ -745,7 +758,13 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {
!containsErrors(trait.analysisDiagnostics) &&
!containsErrors(trait.resolveDiagnostics)
) {
return trait.handler.compileHmrUpdateDeclaration(clazz, trait.analysis, trait.resolution!);
// TODO
return trait.handler.compileHmrUpdateDeclaration(
clazz,
trait.analysis,
trait.resolution!,
[],
);
}
}

Expand Down Expand Up @@ -808,3 +827,36 @@ function containsErrors(diagnostics: ts.Diagnostic[] | null): boolean {
diagnostics.some((diag) => diag.category === ts.DiagnosticCategory.Error)
);
}

function getImportedSymbols(sourceFile: ts.SourceFile): string[] {
const importedSymbols: string[] = [];

// Traverse the AST
const visit = (node: ts.Node) => {
if (ts.isImportDeclaration(node) && node.importClause) {
const importClause = node.importClause;

// Default import (e.g., `import foo from 'module';`)
if (importClause.name) {
importedSymbols.push(importClause.name.text);
}

// Named imports (e.g., `import { bar, baz } from 'module';`)
if (importClause.namedBindings && ts.isNamedImports(importClause.namedBindings)) {
for (const element of importClause.namedBindings.elements) {
importedSymbols.push(element.name.text);
}
}

// Namespace import (e.g., `import * as ns from 'module';`)
if (importClause.namedBindings && ts.isNamespaceImport(importClause.namedBindings)) {
importedSymbols.push(importClause.namedBindings.name.text);
}
}

ts.forEachChild(node, visit); // Recursively process child nodes
};

visit(sourceFile);
return importedSymbols;
}
Loading