feat(ivy): emit module scope metadata using pure function call (#29598)
Prior to this change, all module metadata would be included in the `defineNgModule` call that is set as the `ngModuleDef` field of module types. Part of the metadata is scope information like declarations, imports and exports that is used for computing the transitive module scope in JIT environments, preventing those references from being tree-shaken for production builds. This change moves the metadata for scope computations to a pure function call that patches the scope references onto the module type. Because the function is marked pure, it may be tree-shaken out during production builds such that references to declarations and exports are dropped, which in turn allows for tree-shaken any declaration that is not otherwise referenced. Fixes #28077, FW-1035 PR Close #29598
This commit is contained in:
@ -80,9 +80,11 @@ export function compileNgModule(meta: R3NgModuleMetadata): R3NgModuleDef {
|
||||
imports,
|
||||
exports,
|
||||
schemas,
|
||||
containsForwardDecls
|
||||
containsForwardDecls,
|
||||
emitInline
|
||||
} = meta;
|
||||
|
||||
const additionalStatements: o.Statement[] = [];
|
||||
const definitionMap = {
|
||||
type: moduleType
|
||||
} as{
|
||||
@ -99,16 +101,29 @@ export function compileNgModule(meta: R3NgModuleMetadata): R3NgModuleDef {
|
||||
definitionMap.bootstrap = refsToArray(bootstrap, containsForwardDecls);
|
||||
}
|
||||
|
||||
if (declarations.length) {
|
||||
definitionMap.declarations = refsToArray(declarations, containsForwardDecls);
|
||||
// If requested to emit scope information inline, pass the declarations, imports and exports to
|
||||
// the `defineNgModule` call. The JIT compilation uses this.
|
||||
if (emitInline) {
|
||||
if (declarations.length) {
|
||||
definitionMap.declarations = refsToArray(declarations, containsForwardDecls);
|
||||
}
|
||||
|
||||
if (imports.length) {
|
||||
definitionMap.imports = refsToArray(imports, containsForwardDecls);
|
||||
}
|
||||
|
||||
if (exports.length) {
|
||||
definitionMap.exports = refsToArray(exports, containsForwardDecls);
|
||||
}
|
||||
}
|
||||
|
||||
if (imports.length) {
|
||||
definitionMap.imports = refsToArray(imports, containsForwardDecls);
|
||||
}
|
||||
|
||||
if (exports.length) {
|
||||
definitionMap.exports = refsToArray(exports, containsForwardDecls);
|
||||
// If not emitting inline, the scope information is not passed into `defineNgModule` as it would
|
||||
// prevent tree-shaking of the declarations, imports and exports references.
|
||||
else {
|
||||
const setNgModuleScopeCall = generateSetNgModuleScopeCall(meta);
|
||||
if (setNgModuleScopeCall !== null) {
|
||||
additionalStatements.push(setNgModuleScopeCall);
|
||||
}
|
||||
}
|
||||
|
||||
if (schemas && schemas.length) {
|
||||
@ -121,10 +136,50 @@ export function compileNgModule(meta: R3NgModuleMetadata): R3NgModuleDef {
|
||||
tupleTypeOf(exports)
|
||||
]));
|
||||
|
||||
const additionalStatements: o.Statement[] = [];
|
||||
|
||||
return {expression, type, additionalStatements};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a function call to `setNgModuleScope` with all necessary information so that the
|
||||
* transitive module scope can be computed during runtime in JIT mode. This call is marked pure
|
||||
* such that the references to declarations, imports and exports may be elided causing these
|
||||
* symbols to become tree-shakeable.
|
||||
*/
|
||||
function generateSetNgModuleScopeCall(meta: R3NgModuleMetadata): o.Statement|null {
|
||||
const {type: moduleType, declarations, imports, exports, containsForwardDecls} = meta;
|
||||
|
||||
const scopeMap = {} as{
|
||||
declarations: o.Expression,
|
||||
imports: o.Expression,
|
||||
exports: o.Expression,
|
||||
};
|
||||
|
||||
if (declarations.length) {
|
||||
scopeMap.declarations = refsToArray(declarations, containsForwardDecls);
|
||||
}
|
||||
|
||||
if (imports.length) {
|
||||
scopeMap.imports = refsToArray(imports, containsForwardDecls);
|
||||
}
|
||||
|
||||
if (exports.length) {
|
||||
scopeMap.exports = refsToArray(exports, containsForwardDecls);
|
||||
}
|
||||
|
||||
if (Object.keys(scopeMap).length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const fnCall = new o.InvokeFunctionExpr(
|
||||
/* fn */ o.importExpr(R3.setNgModuleScope),
|
||||
/* args */[moduleType, mapToMapExpression(scopeMap)],
|
||||
/* type */ undefined,
|
||||
/* sourceSpan */ undefined,
|
||||
/* pure */ true);
|
||||
return fnCall.toStmt();
|
||||
}
|
||||
|
||||
export interface R3InjectorDef {
|
||||
expression: o.Expression;
|
||||
type: o.Type;
|
||||
@ -200,4 +255,4 @@ function tupleTypeOf(exp: R3Reference[]): o.Type {
|
||||
function refsToArray(refs: R3Reference[], shouldForwardDeclare: boolean): o.Expression {
|
||||
const values = o.literalArr(refs.map(ref => ref.value));
|
||||
return shouldForwardDeclare ? o.fn([], [new o.ReturnStatement(values)]) : values;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user