Skip to content

Commit

Permalink
refactor(compiler-cli): update DeferredSymbolTracker to use explicit …
Browse files Browse the repository at this point in the history
…deps config (#53591)

This commit updates the `DeferredSymbolTracker` class to take info account the `onlyExplicitDeferDependencyImports` flag. The `DeferredSymbolTracker` class also exposes a new API to register import declarations as explicitly deferred, which will be used in followup commits.

PR Close #53591
  • Loading branch information
AndrewKushnir authored and atscott committed Jan 10, 2024
1 parent 9cae9b4 commit c6df89c
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ function setup(
hostDirectivesResolver,
true,
compilationMode,
new DeferredSymbolTracker(checker),
new DeferredSymbolTracker(checker, /* onlyExplicitDeferDependencyImports */ false),
/* forbidOrphanRenderering */ false,
/* enableBlockSyntax */ true,
);
Expand Down
4 changes: 3 additions & 1 deletion packages/compiler-cli/src/ngtsc/core/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1072,7 +1072,9 @@ export class NgCompiler {

const resourceRegistry = new ResourceRegistry();

const deferredSymbolsTracker = new DeferredSymbolTracker(this.inputProgram.getTypeChecker());
const deferredSymbolsTracker = new DeferredSymbolTracker(
this.inputProgram.getTypeChecker(),
this.options.onlyExplicitDeferDependencyImports ?? false);

// Cycles are handled in full compilation mode by "remote scoping".
// "Remote scoping" does not work well with tree shaking for libraries.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@ import {getContainingImportDeclaration} from '../../reflection/src/typescript';
const AssumeEager = 'AssumeEager';
type AssumeEager = typeof AssumeEager;

/**
* A marker indicating that a symbol from an import declaration
* was referenced in a `@Component.deferredImports` list.
*/
const ExplicitlyDeferred = 'ExplicitlyDeferred';
type ExplicitlyDeferred = typeof ExplicitlyDeferred;

/**
* Maps imported symbol name to a set of locations where the symbols is used
* in a source file.
*/
type SymbolMap = Map<string, Set<ts.Identifier>|AssumeEager>;

/**
* Allows to register a symbol as deferrable and keep track of its usage.
*
Expand All @@ -21,10 +34,11 @@ type AssumeEager = typeof AssumeEager;
* in favor of using a dynamic import for cases when defer blocks are used.
*/
export class DeferredSymbolTracker {
private readonly imports =
new Map<ts.ImportDeclaration, Map<string, Set<ts.Identifier>|AssumeEager>>();
private readonly imports = new Map<ts.ImportDeclaration, ExplicitlyDeferred|SymbolMap>();

constructor(private readonly typeChecker: ts.TypeChecker) {}
constructor(
private readonly typeChecker: ts.TypeChecker,
private onlyExplicitDeferDependencyImports: boolean) {}

/**
* Given an import declaration node, extract the names of all imported symbols
Expand Down Expand Up @@ -71,19 +85,39 @@ export class DeferredSymbolTracker {
return symbolMap;
}

/**
* Marks a given import declaration as explicitly deferred, since it's
* used in the `@Component.deferredImports` field.
*/
markAsExplicitlyDeferred(importDecl: ts.ImportDeclaration): void {
this.imports.set(importDecl, ExplicitlyDeferred);
}

/**
* Marks a given identifier and an associated import declaration as a candidate
* for defer loading.
*/
markAsDeferrableCandidate(identifier: ts.Identifier, importDecl: ts.ImportDeclaration): void {
if (this.onlyExplicitDeferDependencyImports) {
// Ignore deferrable candidates when only explicit deferred imports mode is enabled.
// In that mode only dependencies from the `@Component.deferredImports` field are
// defer-loadable.
return;
}

let symbolMap = this.imports.get(importDecl);

// Do we come across this import as a part of `@Component.deferredImports` already?
if (symbolMap === ExplicitlyDeferred) {
return;
}

// Do we come across this import for the first time?
if (!this.imports.has(importDecl)) {
const symbolMap = this.extractImportedSymbols(importDecl);
if (!symbolMap) {
symbolMap = this.extractImportedSymbols(importDecl);
this.imports.set(importDecl, symbolMap);
}

const symbolMap = this.imports.get(importDecl)!;

if (!symbolMap.has(identifier.text)) {
throw new Error(
`The '${identifier.text}' identifier doesn't belong ` +
Expand Down Expand Up @@ -113,6 +147,10 @@ export class DeferredSymbolTracker {
}

const symbolsMap = this.imports.get(importDecl)!;
if (symbolsMap === ExplicitlyDeferred) {
return true;
}

for (const [symbol, refs] of symbolsMap) {
if (refs === AssumeEager || refs.size > 0) {
// There may be still eager references to this symbol.
Expand Down

0 comments on commit c6df89c

Please sign in to comment.