@angular/core
Version:
Angular - the core framework
153 lines (148 loc) • 6.96 kB
JavaScript
;
/**
* @license Angular v22.0.0
* (c) 2010-2026 Google LLC. https://angular.dev/
* License: MIT
*/
;
require('@angular-devkit/core');
require('node:path/posix');
var project_paths = require('./project_paths-D2V-Uh2L.cjs');
var migrations = require('@angular/compiler-cli/private/migrations');
var ts = require('typescript');
var ng_decorators = require('./ng_decorators-IVztR9rk.cjs');
require('@angular/compiler-cli');
require('node:path');
var apply_import_manager = require('./apply_import_manager-CxA_YYgB.cjs');
var leading_space = require('./leading_space-BTPRV0wu.cjs');
require('@angular-devkit/schematics');
require('./project_tsconfig_paths-DkkMibv-.cjs');
require('./imports-CKV-ITqD.cjs');
class ChangeDetectionEagerMigration extends project_paths.TsurgeFunnelMigration {
config;
constructor(config = {}) {
super();
this.config = config;
}
async analyze(info) {
const { sourceFiles, program } = info;
const typeChecker = program.getTypeChecker();
const replacements = [];
const importManager = new migrations.ImportManager();
const printer = ts.createPrinter();
for (const sf of sourceFiles) {
const file = project_paths.projectFile(sf, info);
if (this.config.shouldMigrate && !this.config.shouldMigrate(file)) {
continue;
}
ts.forEachChild(sf, (node) => {
if (!ts.isClassDeclaration(node)) {
return;
}
const decorators = ng_decorators.getAngularDecorators(typeChecker, ts.getDecorators(node) || []);
const componentDecorator = decorators.find((d) => d.name === 'Component' && d.moduleName === '@angular/core');
if (!componentDecorator) {
return;
}
// The helper `getAngularDecorators` guarantees that `node` is `CallExpressionDecorator`.
// So `componentDecorator.node.expression` is `ts.CallExpression`.
const callExpression = componentDecorator.node.expression;
if (callExpression.arguments.length !== 1 ||
!ts.isObjectLiteralExpression(callExpression.arguments[0])) {
return;
}
const metadata = callExpression.arguments[0];
const changeDetectionProp = metadata.properties.find((p) => ts.isPropertyAssignment(p) &&
(ts.isIdentifier(p.name) || ts.isStringLiteral(p.name)) &&
p.name.text === 'changeDetection');
if (!changeDetectionProp) {
// Property missing. Add it.
const changeDetectionStrategyExpr = importManager.addImport({
exportModuleSpecifier: '@angular/core',
exportSymbolName: 'ChangeDetectionStrategy',
requestedFile: sf,
});
// Print the identifier
const exprText = printer.printNode(ts.EmitHint.Unspecified, changeDetectionStrategyExpr, sf);
const properties = metadata.properties;
let insertPos;
let toInsert;
if (properties.length > 0) {
const lastProp = properties[properties.length - 1];
insertPos = lastProp.getStart();
toInsert = `changeDetection: ${exprText}.Eager,\n${leading_space.getLeadingLineWhitespaceOfNode(lastProp)}`;
}
else {
insertPos = metadata.getStart() + 1;
toInsert = `\n changeDetection: ${exprText}.Eager\n`;
}
replacements.push(new project_paths.Replacement(project_paths.projectFile(sf, info), new project_paths.TextUpdate({
position: insertPos,
end: insertPos,
toInsert: toInsert,
})));
return;
}
// Check if explicitly set to Default.
if (!ts.isPropertyAccessExpression(changeDetectionProp.initializer)) {
return;
}
const initializer = changeDetectionProp.initializer;
// Best effort check for ChangeDetectionStrategy.Default
if (!ts.isIdentifier(initializer.expression) || initializer.name.text !== 'Default') {
return;
}
// Verify it is indeed ChangeDetectionStrategy.
// We can check if the symbol of the expression is imported from @angular/core and named ChangeDetectionStrategy.
const symbol = typeChecker.getSymbolAtLocation(initializer.expression);
if (!symbol || !symbol.declarations || symbol.declarations.length === 0) {
return;
}
const declaration = symbol.declarations[0];
if (!ts.isImportSpecifier(declaration)) {
return;
}
const propertyName = declaration.propertyName?.text ?? declaration.name.text;
const importDecl = declaration.parent.parent.parent;
if (!ts.isImportDeclaration(importDecl) ||
!ts.isStringLiteral(importDecl.moduleSpecifier) ||
importDecl.moduleSpecifier.text !== '@angular/core' ||
propertyName !== 'ChangeDetectionStrategy') {
return;
}
replacements.push(new project_paths.Replacement(project_paths.projectFile(sf, info), new project_paths.TextUpdate({
position: initializer.name.getStart(),
end: initializer.name.getEnd(),
toInsert: 'Eager',
})));
});
}
apply_import_manager.applyImportManagerChanges(importManager, replacements, sourceFiles, info);
return project_paths.confirmAsSerializable({
replacements,
});
}
async combine(unitA, unitB) {
return project_paths.confirmAsSerializable({
replacements: [...unitA.replacements, ...unitB.replacements],
});
}
async globalMeta(combinedData) {
return project_paths.confirmAsSerializable(combinedData);
}
async stats(globalMetadata) {
return project_paths.confirmAsSerializable({});
}
async migrate(globalData) {
return { replacements: globalData.replacements };
}
}
function migrate(options) {
return async (tree, context) => {
await project_paths.runMigrationInDevkit({
tree,
getMigration: (fs) => new ChangeDetectionEagerMigration(),
});
};
}
exports.migrate = migrate;