pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/angular/angular/pull/68680

4bb.css" /> refactor(compiler): add support for compiling NgModules under isolatedDeclarations by alxhub · Pull Request #68680 · angular/angular · GitHub
Skip to content

refactor(compiler): add support for compiling NgModules under isolatedDeclarations#68680

Open
alxhub wants to merge 1 commit into
angular:mainfrom
alxhub:iso-decl
Open

refactor(compiler): add support for compiling NgModules under isolatedDeclarations#68680
alxhub wants to merge 1 commit into
angular:mainfrom
alxhub:iso-decl

Conversation

@alxhub
Copy link
Copy Markdown
Member

@alxhub alxhub commented May 11, 2026

Note: only really relevant for g3

attempt to statically evaluate @NgModule imports and exports in isolated declarations mode. if resolution succeeds, emit the resolved values as concrete references. if resolution fails, fall back to purely syntactic transforms, such as transforming calls to ReturnType<typeof ...>.

also add validation to ensure expressions falling back to typeof are valid entity names, producing a LOCAL_COMPILATION_UNSUPPORTED_EXPRESSION diagnostic otherwise.

this allows emitting correct type references in .d.ts files without needing full resolution of external references, while supporting common patterns like forRoot() and local evaluation where possible.

TAG=agy
CONV=51a2b6d6-5679-49cf-8fa6-61fbc69628be

@angular-robot angular-robot Bot added detected: feature PR contains a feature commit area: compiler Issues related to `ngc`, Angular's template compiler labels May 11, 2026
@ngbot ngbot Bot added this to the Backlog milestone May 11, 2026
@alxhub alxhub force-pushed the iso-decl branch 3 times, most recently from 179ab9b to 38c8d71 Compare May 12, 2026 21:40
@alxhub alxhub changed the title feat(compiler-cli): support partial evaluation and ReturnType in isolated declarations refactor(compiler): add support for compiling NgModules under isolatedDeclarations May 12, 2026
…dDeclarations

This commit adds support for compiling NgModules in isolated declarations mode.

Key changes:
- Removed the unnecessary @NgModule({id}) warning diagnostic in isolated declarations mode.
- Set declarations to never in the generated .d.ts file for isolated NgModules.
- Extracted closures to standalone functions in NgModuleDecoratorHandler to avoid capturing state.
- Removed bootstrap and declarations from R3NgModuleMetadataIsolated as they are not used or always never.
- Extended the foreign function mechanism in PartialEvaluator to handle foreign types like ModuleWithProviders.
- Converted declaration-only compliance tests to use explicit goldens (*_isolated.golden.d.ts) and updated behavioral tests.

These changes are internal refactorings to support isolated declarations and are not relevant to 3rd party Angular users.
@alxhub alxhub marked this pull request as ready for review May 13, 2026 20:34
@alxhub alxhub added area: testing Issues related to Angular testing features, such as TestBed target: patch This PR is targeted for the next patch release labels May 14, 2026
@alxhub
Copy link
Copy Markdown
Member Author

alxhub commented May 14, 2026

TGP is "green"

@alxhub alxhub added target: rc This PR is targeted for the next release-candidate and removed target: patch This PR is targeted for the next patch release labels May 14, 2026
const rawDeclarations = ngModule.get('declarations') ?? null;
const rawImports = ngModule.get('imports') ?? null;
const rawExports = ngModule.get('exports') ?? null;
const rawBootstrap = ngModule.get('bootstrap') ?? null;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is unused

@@ -116,6 +142,30 @@ export class DtsMetadataReader implements MetadataReader {
};
}

private extractReferencesFromResolvedValue(
value: ResolvedValue,
bestGuessOwningModule: OwningModule | null,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unused

const rawImports = ngModule.get('imports') ?? null;
const rawExports = ngModule.get('exports') ?? null;
const rawBootstrap = ngModule.get('bootstrap') ?? null;
const rawProviders = ngModule.has('providers') ? ngModule.get('providers')! : null;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const rawProviders = ngModule.has('providers') ? ngModule.get('providers')! : null;
const rawProviders = ngModule.get('providers') ?? null;

const idExpr = ngModule.get('id')!;
if (!isModuleIdExpression(idExpr)) {
id = new WrappedNodeExpr(idExpr);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have an else case with WARN_NGMODULE_ID_UNNECESSARY?

Comment on lines +1396 to +1399
const forwardRefUnwrapped = tryUnwrapForwardRef(el, reflector);
if (forwardRefUnwrapped !== null) {
return transformToTypeTupleElement(forwardRefUnwrapped, reflector, diagnostics);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe use a loop instead of recursion

}
if (ts.isPropertyAccessExpression(expr) && ts.isIdentifier(expr.name)) {
const left = expressionToEntityName(expr.expression);
return left === null ? null : ts.factory.createQualifiedName(left, expr.name.text);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return left === null ? null : ts.factory.createQualifiedName(left, expr.name.text);
return left === null ? null : ts.factory.createQualifiedName(left, expr.name);

nit

Also this looks quite similar to the existing getEntityTypeFromExpression?

Comment on lines +87 to +117
const declarations = this.extractReferencesFromResolvedValue(
declarationMetadata.kind === ts.SyntaxKind.NeverKeyword
? []
: this.evaluator.evaluateType(
declarationMetadata,
ref.bestGuessOwningModule,
undefined,
foreignTypeResolver,
),
ref.bestGuessOwningModule,
);
const exports = extractReferencesFromType(
this.checker,
exportMetadata,
const exports = this.extractReferencesFromResolvedValue(
exportMetadata.kind === ts.SyntaxKind.NeverKeyword
? []
: this.evaluator.evaluateType(
exportMetadata,
ref.bestGuessOwningModule,
undefined,
foreignTypeResolver,
),
ref.bestGuessOwningModule,
);
const imports = extractReferencesFromType(
this.checker,
importMetadata,
const imports = this.extractReferencesFromResolvedValue(
importMetadata.kind === ts.SyntaxKind.NeverKeyword
? []
: this.evaluator.evaluateType(
importMetadata,
ref.bestGuessOwningModule,
undefined,
foreignTypeResolver,
),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const declarations = this.extractReferencesFromResolvedValue(
declarationMetadata.kind === ts.SyntaxKind.NeverKeyword
? []
: this.evaluator.evaluateType(
declarationMetadata,
ref.bestGuessOwningModule,
undefined,
foreignTypeResolver,
),
ref.bestGuessOwningModule,
);
const exports = extractReferencesFromType(
this.checker,
exportMetadata,
const exports = this.extractReferencesFromResolvedValue(
exportMetadata.kind === ts.SyntaxKind.NeverKeyword
? []
: this.evaluator.evaluateType(
exportMetadata,
ref.bestGuessOwningModule,
undefined,
foreignTypeResolver,
),
ref.bestGuessOwningModule,
);
const imports = extractReferencesFromType(
this.checker,
importMetadata,
const imports = this.extractReferencesFromResolvedValue(
importMetadata.kind === ts.SyntaxKind.NeverKeyword
? []
: this.evaluator.evaluateType(
importMetadata,
ref.bestGuessOwningModule,
undefined,
foreignTypeResolver,
),
const evaluateMetadata = (metadataNode: ts.TypeNode) => {
return metadataNode.kind === ts.SyntaxKind.NeverKeyword
? []
: this.evaluator.evaluateType(
metadataNode,
ref.bestGuessOwningModule,
undefined,
foreignTypeResolver,
);
};
const declarations = this.extractReferencesFromResolvedValue(
evaluateMetadata(declarationMetadata),
ref.bestGuessOwningModule,
);
const exports = this.extractReferencesFromResolvedValue(
evaluateMetadata(exportMetadata),
ref.bestGuessOwningModule,
);
const imports = this.extractReferencesFromResolvedValue(
evaluateMetadata(importMetadata),
ref.bestGuessOwningModule,
);

return null;
}
const name = ts.isQualifiedName(type.typeName) ? type.typeName.right : type.typeName;
if (!ts.isIdentifier(name) || name.text !== 'ModuleWithProviders') {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!ts.isIdentifier(name) || name.text !== 'ModuleWithProviders') {
if (name.text !== 'ModuleWithProviders') {

Types guarantee this is an identifier

Comment on lines +310 to +316
const typeArguments = node.typeArguments
? ts.factory.createNodeArray(
node.typeArguments.map(
(arg) => ts.visitNode(arg, visit, ts.isTypeNode) as ts.TypeNode,
),
)
: undefined;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const typeArguments = node.typeArguments
? ts.factory.createNodeArray(
node.typeArguments.map(
(arg) => ts.visitNode(arg, visit, ts.isTypeNode) as ts.TypeNode,
),
)
: undefined;
const typeArguments = node.typeArguments
? ts.visitNodes(node.typeArguments, visit, ts.isTypeNode)
: undefined;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: compiler Issues related to `ngc`, Angular's template compiler area: testing Issues related to Angular testing features, such as TestBed detected: feature PR contains a feature commit target: rc This PR is targeted for the next release-candidate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy