ng-upgrade-orchestrator
Version:
Enterprise-grade Angular Multi-Version Upgrade Orchestrator with automatic npm installation, comprehensive dependency management, and seamless integration of all 9 official Angular migrations. Safely migrate Angular applications across multiple major vers
947 lines (915 loc) • 37.9 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.Angular16Handler = void 0;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const BaseVersionHandler_1 = require("./BaseVersionHandler");
const SSRDetector_1 = require("../utils/SSRDetector");
/**
* Angular 16 Handler - Required inputs, signals, and new control flow
*
* Handles the migration to Angular 16 with comprehensive support for all new features
* including required inputs, signals introduction, new control flow syntax, and enhanced
* developer experience improvements.
*
* Key Features in Angular 16:
* - Required inputs (@Input({ required: true }))
* - Router data as input
* - New control flow syntax (@if, @for, @switch) - developer preview
* - Signals introduction (developer preview)
* - Self-closing tags support
* - Standalone ng new collection
* - Non-destructive hydration (developer preview)
* - esbuild and Vite support for dev server
*
* @example
* ```typescript
* const handler = new Angular16Handler();
* await handler.applyVersionSpecificChanges('/path/to/project', options);
* ```
*
* @since 1.0.0
* @author Angular Multi-Version Upgrade Orchestrator
*/
class Angular16Handler extends BaseVersionHandler_1.BaseVersionHandler {
/** The Angular version this handler manages */
version = '16';
/**
* Gets the minimum required Node.js version for Angular 16
* @returns The minimum Node.js version requirement
*/
getRequiredNodeVersion() {
return '>=16.14.0';
}
/**
* Gets the required TypeScript version range for Angular 16
* @returns The TypeScript version requirement
*/
getRequiredTypeScriptVersion() {
return '>=4.9.3 <5.1.0';
}
/**
* Gets all dependency updates required for Angular 16 migration
*
* Includes core Angular packages, CLI tools, TypeScript, and third-party
* libraries like Angular Material that need version alignment.
*
* @returns Array of dependency updates with package names, versions, and types
* @example
* ```typescript
* const updates = handler.getDependencyUpdates();
* // Returns: [{ name: '@angular/core', version: '^16.0.0', type: 'dependencies' }, ...]
* ```
*/
getDependencyUpdates() {
return [
// Core Angular packages
{ name: '@angular/animations', version: '^16.0.0', type: 'dependencies' },
{ name: '@angular/common', version: '^16.0.0', type: 'dependencies' },
{ name: '@angular/compiler', version: '^16.0.0', type: 'dependencies' },
{ name: '@angular/core', version: '^16.0.0', type: 'dependencies' },
{ name: '@angular/forms', version: '^16.0.0', type: 'dependencies' },
{ name: '@angular/platform-browser', version: '^16.0.0', type: 'dependencies' },
{ name: '@angular/platform-browser-dynamic', version: '^16.0.0', type: 'dependencies' },
{ name: '@angular/router', version: '^16.0.0', type: 'dependencies' },
// Angular CLI and dev dependencies
{ name: '@angular/cli', version: '^16.0.0', type: 'devDependencies' },
{ name: '@angular/compiler-cli', version: '^16.0.0', type: 'devDependencies' },
{ name: '@angular-devkit/build-angular', version: '^16.0.0', type: 'devDependencies' },
// TypeScript and supporting packages
{ name: 'typescript', version: '~4.9.3', type: 'devDependencies' },
{ name: 'zone.js', version: '~0.13.0', type: 'dependencies' },
{ name: 'rxjs', version: '~7.8.0', type: 'dependencies' },
// Angular Material (if present)
{ name: '@angular/material', version: '^16.0.0', type: 'dependencies' },
{ name: '@angular/cdk', version: '^16.0.0', type: 'dependencies' }
];
}
/**
* Applies all Angular 16 specific transformations to the project
*
* This method orchestrates the complete migration process including:
* - Required inputs implementation
* - Router data as input setup
* - New control flow syntax introduction
* - Signals foundation implementation
* - Self-closing tags support
* - Build configuration updates
* - Third-party compatibility validation
*
* @param projectPath - The absolute path to the Angular project root
* @param options - Upgrade configuration options including strategy and validation level
* @throws {Error} When critical transformations fail
*
* @example
* ```typescript
* await handler.applyVersionSpecificChanges('/path/to/project', {
* strategy: 'balanced',
* validationLevel: 'comprehensive'
* });
* ```
*/
async applyVersionSpecificChanges(projectPath, options) {
this.progressReporter?.updateMessage('Applying Angular 16 transformations...');
// 1. Implement required inputs support
await this.implementRequiredInputs(projectPath);
// 2. Setup router data as input feature
await this.setupRouterDataAsInput(projectPath);
// 3. Introduce new control flow syntax (developer preview)
await this.introduceNewControlFlowSyntax(projectPath);
// 4. Implement signals foundation
await this.implementSignalsFoundation(projectPath);
// 5. Enable self-closing tags support
await this.enableSelfClosingTagsSupport(projectPath);
// 6. Setup standalone ng new collection
await this.setupStandaloneCollection(projectPath);
// 7. Configure non-destructive hydration
await this.configureNonDestructiveHydration(projectPath);
// 8. Setup esbuild and Vite support
await this.setupESBuildAndViteSupport(projectPath);
// 9. Update build configurations for Angular 16
await this.updateBuildConfigurations(projectPath);
// 10. Validate third-party compatibility
await this.validateThirdPartyCompatibility(projectPath);
this.progressReporter?.success('✓ Angular 16 transformations completed');
}
/**
* Gets all breaking changes introduced in Angular 16
*
* Returns a comprehensive list of breaking changes with severity levels,
* descriptions, and migration guidance. Most Angular 16 changes are opt-in
* features with low impact on existing applications.
*
* @returns Array of breaking change objects with migration guidance
*
* @example
* ```typescript
* const changes = handler.getBreakingChanges();
* changes.forEach(change => {
* console.log(`${change.id}: ${change.description}`);
* });
* ```
*/
getBreakingChanges() {
return [
// Required inputs introduction
this.createBreakingChange('ng16-required-inputs', 'api', 'low', 'Required inputs introduced', 'New @Input({ required: true }) syntax for mandatory component inputs', 'Optional feature - existing @Input() decorators continue to work unchanged'),
// TypeScript version requirement
this.createBreakingChange('ng16-typescript-version', 'dependency', 'medium', 'TypeScript 4.9+ required', 'Angular 16 requires TypeScript 4.9.3 or higher', 'Update TypeScript to version 4.9.3 or higher'),
// Node.js version requirement
this.createBreakingChange('ng16-nodejs-version', 'dependency', 'medium', 'Node.js 16.14+ required', 'Angular 16 requires Node.js 16.14.0 or higher', 'Update Node.js to version 16.14.0 or higher'),
// Router data as input
this.createBreakingChange('ng16-router-data-input', 'api', 'low', 'Router data as input feature', 'Route data can now be bound directly to component inputs', 'New feature - existing route data handling continues to work'),
// New control flow syntax (developer preview)
this.createBreakingChange('ng16-new-control-flow', 'template', 'low', 'New control flow syntax (developer preview)', '@if, @for, @switch syntax introduced as developer preview', 'Developer preview only - existing *ngIf, *ngFor continue to work'),
// Signals introduction
this.createBreakingChange('ng16-signals-introduction', 'api', 'low', 'Signals introduced (developer preview)', 'New reactive primitive for state management', 'Developer preview - existing reactive patterns continue to work'),
// Self-closing tags support
this.createBreakingChange('ng16-self-closing-tags', 'template', 'low', 'Self-closing tags support', 'Angular templates now support self-closing tag syntax', 'Optional feature - existing tag syntax continues to work'),
// Non-destructive hydration
this.createBreakingChange('ng16-non-destructive-hydration', 'api', 'low', 'Non-destructive hydration (developer preview)', 'Improved SSR hydration that preserves existing DOM', 'Developer preview - existing hydration continues to work')
];
}
// Private implementation methods
/**
* Implements required inputs support with comprehensive examples
*
* Creates example components demonstrating the new @Input({ required: true })
* syntax and input transforms. Generates practical examples that developers
* can use as reference for migrating their own components.
*
* @param projectPath - The absolute path to the Angular project root
* @private
*
* @example
* Generated example includes:
* ```typescript
* @Input({ required: true }) name!: string;
* @Input({ transform: (value: string) => value.toUpperCase() }) displayName?: string;
* ```
*/
async implementRequiredInputs(projectPath) {
try {
const exampleDir = path.join(projectPath, 'src/app/examples');
await fs.ensureDir(exampleDir);
// Create required inputs example
const requiredInputsExample = `import { Component, Input } from '@angular/core';
export class RequiredInputsExampleComponent {
// Required inputs - must be provided by parent component
name!: string;
email!: string;
// Optional inputs with default values
age: number = 0;
bio?: string;
// Transform input (Angular 16 feature)
displayName?: string;
}
// Example parent component usage:
/*
<app-required-inputs-example
name="John Doe"
email="john.doe@example.com"
[age]="30"
bio="Software Developer"
displayName="John Doe">
</app-required-inputs-example>
*/
`;
const requiredInputsPath = path.join(exampleDir, 'required-inputs-example.component.ts');
if (!await fs.pathExists(requiredInputsPath)) {
await fs.writeFile(requiredInputsPath, requiredInputsExample);
this.progressReporter?.info('✓ Created required inputs example');
}
}
catch (error) {
this.progressReporter?.warn(`Could not implement required inputs: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Setup router data as input feature
*/
async setupRouterDataAsInput(projectPath) {
try {
const exampleDir = path.join(projectPath, 'src/app/examples');
await fs.ensureDir(exampleDir);
// Create router data as input example
const routerDataExample = `import { Component, Input } from '@angular/core';
export class RouterDataExampleComponent {
// These inputs will be automatically populated from route data
title!: string;
section!: string;
userId!: string;
metadata?: any;
}
// Example routing configuration:
/*
const routes: Routes = [
{
path: 'user/:userId',
component: RouterDataExampleComponent,
data: {
title: 'User Profile',
section: 'Users',
metadata: { source: 'admin-panel' }
}
}
];
// In the module configuration, enable router data as input:
@NgModule({
imports: [RouterModule.forRoot(routes, {
bindToComponentInputs: true // Enable router data as input
})],
exports: [RouterModule]
})
export class AppRoutingModule { }
*/
`;
const routerDataPath = path.join(exampleDir, 'router-data-example.component.ts');
if (!await fs.pathExists(routerDataPath)) {
await fs.writeFile(routerDataPath, routerDataExample);
this.progressReporter?.info('✓ Created router data as input example');
}
}
catch (error) {
this.progressReporter?.warn(`Could not setup router data as input: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Introduce new control flow syntax (developer preview)
*/
async introduceNewControlFlowSyntax(projectPath) {
try {
const exampleDir = path.join(projectPath, 'src/app/examples');
await fs.ensureDir(exampleDir);
// Create new control flow syntax example
const controlFlowExample = `import { Component } from '@angular/core';
interface User {
id: number;
name: string;
role: 'admin' | 'user' | 'guest';
}
export class ControlFlowExampleComponent {
currentUser: User | null = {
id: 1,
name: 'John Doe',
role: 'admin'
};
users: User[] = [
{ id: 1, name: 'Alice', role: 'admin' },
{ id: 2, name: 'Bob', role: 'user' },
{ id: 3, name: 'Charlie', role: 'guest' }
];
selectedRole: string = 'admin';
}
/*
NOTE: The new control flow syntax (@if, @for, @switch) is in developer preview.
To enable it, you need to configure it in your Angular application.
The traditional syntax (*ngIf, *ngFor, *ngSwitch) continues to work and is recommended
for production applications until the new syntax is stable.
*/
`;
const controlFlowPath = path.join(exampleDir, 'control-flow-example.component.ts');
if (!await fs.pathExists(controlFlowPath)) {
await fs.writeFile(controlFlowPath, controlFlowExample);
this.progressReporter?.info('✓ Created new control flow syntax example (developer preview)');
}
}
catch (error) {
this.progressReporter?.warn(`Could not create control flow example: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Implements signals foundation with comprehensive examples and patterns
*
* Creates example components demonstrating Angular 16's new signals API including
* basic signals, computed signals, effects, and practical usage patterns. Signals
* are in developer preview and provide a new reactive primitive for state management.
*
* @param projectPath - The absolute path to the Angular project root
* @private
*
* @example
* Generated examples include:
* ```typescript
* count = signal(0);
* doubleCount = computed(() => this.count() * 2);
* effect(() => console.log(`Count: ${this.count()}`));
* ```
*/
async implementSignalsFoundation(projectPath) {
try {
const exampleDir = path.join(projectPath, 'src/app/examples');
await fs.ensureDir(exampleDir);
// Create signals example
const signalsExample = `import { Component, signal, computed, effect } from '@angular/core';
export class SignalsExampleComponent {
// Basic signal
count = signal(0);
// Computed signals (derived state)
doubleCount = computed(() => this.count() * 2);
isEven = computed(() => this.count() % 2 === 0);
// Signals for form data
firstName = signal('');
lastName = signal('');
fullName = computed(() => \`\${this.firstName()} \${this.lastName()}\`.trim());
constructor() {
// Effect runs when signals change
effect(() => {
console.log(\`Count changed to: \${this.count()}\`);
});
effect(() => {
console.log(\`Full name: \${this.fullName()}\`);
});
}
increment() {
this.count.update(value => value + 1);
}
decrement() {
this.count.update(value => value - 1);
}
reset() {
this.count.set(0);
}
}
/*
NOTE: Signals are in developer preview in Angular 16.
They provide a new reactive primitive for managing state and derived values.
Key benefits:
- Automatic dependency tracking
- Efficient change detection
- Composable reactive patterns
- Better performance than Observables for simple state
Use signals for:
- Component state
- Derived values
- Simple reactive patterns
Continue using Observables for:
- Async operations
- Complex reactive streams
- HTTP requests
*/
`;
const signalsPath = path.join(exampleDir, 'signals-example.component.ts');
if (!await fs.pathExists(signalsPath)) {
await fs.writeFile(signalsPath, signalsExample);
this.progressReporter?.info('✓ Created signals example (developer preview)');
}
}
catch (error) {
this.progressReporter?.warn(`Could not implement signals foundation: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Enable self-closing tags support
*/
async enableSelfClosingTagsSupport(projectPath) {
const angularJsonPath = path.join(projectPath, 'angular.json');
if (await fs.pathExists(angularJsonPath)) {
try {
const angularJson = await fs.readJson(angularJsonPath);
// Update build configurations to support self-closing tags
for (const projectName in angularJson.projects) {
const project = angularJson.projects[projectName];
if (project.architect?.build) {
// Enable self-closing tags in build options
project.architect.build.options = {
...project.architect.build.options,
allowSelfClosingTags: true
};
}
}
await fs.writeJson(angularJsonPath, angularJson, { spaces: 2 });
this.progressReporter?.info('✓ Enabled self-closing tags support');
}
catch (error) {
this.progressReporter?.warn(`Could not enable self-closing tags: ${error instanceof Error ? error.message : String(error)}`);
}
}
}
/**
* Setup standalone ng new collection
*/
async setupStandaloneCollection(projectPath) {
try {
// Create standalone collection configuration
const collectionDir = path.join(projectPath, '.angular/schematics');
await fs.ensureDir(collectionDir);
const standaloneConfig = {
defaultCollection: '@angular/core',
standaloneComponents: {
enabled: true,
defaultImports: [
'CommonModule',
'FormsModule',
'RouterModule'
]
}
};
const configPath = path.join(collectionDir, 'standalone.json');
await fs.writeJson(configPath, standaloneConfig, { spaces: 2 });
this.progressReporter?.info('✓ Setup standalone ng new collection');
}
catch (error) {
this.progressReporter?.warn(`Could not setup standalone collection: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Configure non-destructive hydration - only for SSR applications
*/
async configureNonDestructiveHydration(projectPath) {
// Check if this is an SSR application first
const isSSRApp = await SSRDetector_1.SSRDetector.isSSRApplication(projectPath);
if (!isSSRApp) {
this.progressReporter?.info('✓ Skipping hydration configuration (CSR application detected)');
return;
}
const mainTsPath = path.join(projectPath, 'src/main.ts');
if (await fs.pathExists(mainTsPath)) {
try {
let content = await fs.readFile(mainTsPath, 'utf-8');
// Add non-destructive hydration import if SSR is detected and not already present
if (content.includes('bootstrapApplication') && !content.includes('provideClientHydration')) {
content = content.replace(/import { bootstrapApplication } from '@angular\/platform-browser';/, `import { bootstrapApplication } from '@angular/platform-browser';
import { provideClientHydration } from '@angular/platform-browser';`);
// Add hydration provider
content = content.replace(/providers: \[([\s\S]*?)\]/, `providers: [
provideClientHydration(),
$1
]`);
await fs.writeFile(mainTsPath, content);
this.progressReporter?.info('✓ Configured non-destructive hydration for SSR application (developer preview)');
}
}
catch (error) {
this.progressReporter?.warn(`Could not configure hydration: ${error instanceof Error ? error.message : String(error)}`);
}
}
}
/**
* Setup esbuild and Vite support
*/
async setupESBuildAndViteSupport(projectPath) {
const angularJsonPath = path.join(projectPath, 'angular.json');
if (await fs.pathExists(angularJsonPath)) {
try {
const angularJson = await fs.readJson(angularJsonPath);
// Update serve configurations to use esbuild
for (const projectName in angularJson.projects) {
const project = angularJson.projects[projectName];
if (project.architect?.serve) {
// Enable esbuild for development server
project.architect.serve.options = {
...project.architect.serve.options,
buildTarget: `${projectName}:build:development`,
hmr: true
};
// Add development configuration if not exists
if (!project.architect.serve.configurations) {
project.architect.serve.configurations = {};
}
project.architect.serve.configurations.development = {
buildTarget: `${projectName}:build:development`,
hmr: true
};
}
// Update build configurations for esbuild
if (project.architect?.build) {
if (!project.architect.build.configurations) {
project.architect.build.configurations = {};
}
// Add development configuration with esbuild
project.architect.build.configurations.development = {
buildOptimizer: false,
optimization: false,
vendorChunk: true,
extractLicenses: false,
sourceMap: true,
namedChunks: true
};
}
}
await fs.writeJson(angularJsonPath, angularJson, { spaces: 2 });
this.progressReporter?.info('✓ Configured esbuild and Vite support');
}
catch (error) {
this.progressReporter?.warn(`Could not setup esbuild/Vite: ${error instanceof Error ? error.message : String(error)}`);
}
}
}
/**
* Update build configurations for Angular 16
*/
async updateBuildConfigurations(projectPath) {
const angularJsonPath = path.join(projectPath, 'angular.json');
if (await fs.pathExists(angularJsonPath)) {
try {
const angularJson = await fs.readJson(angularJsonPath);
for (const projectName in angularJson.projects) {
const project = angularJson.projects[projectName];
if (project.architect?.build) {
// Update production configuration for Angular 16
if (!project.architect.build.configurations) {
project.architect.build.configurations = {};
}
project.architect.build.configurations.production = {
...project.architect.build.configurations.production,
budgets: [
{
type: 'initial',
maximumWarning: '500kb',
maximumError: '1mb'
},
{
type: 'anyComponentStyle',
maximumWarning: '2kb',
maximumError: '4kb'
}
],
outputHashing: 'all',
optimization: {
scripts: true,
styles: true,
fonts: true
},
sourceMap: false,
namedChunks: false,
aot: true,
extractLicenses: true,
vendorChunk: false,
buildOptimizer: true
};
}
}
await fs.writeJson(angularJsonPath, angularJson, { spaces: 2 });
this.progressReporter?.info('✓ Updated build configurations for Angular 16');
}
catch (error) {
this.progressReporter?.warn(`Could not update build configurations: ${error instanceof Error ? error.message : String(error)}`);
}
}
}
/**
* Validate and update third-party compatibility for Angular 16
*/
async validateThirdPartyCompatibility(projectPath) {
// First, update third-party dependencies to compatible versions
await this.updateThirdPartyDependencies(projectPath);
const packageJsonPath = path.join(projectPath, 'package.json');
if (await fs.pathExists(packageJsonPath)) {
try {
const packageJson = await fs.readJson(packageJsonPath);
const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies };
const warnings = [];
const recommendations = [];
for (const [libName, version] of Object.entries(dependencies)) {
if (typeof version === 'string') {
// Check Angular Material compatibility
if (libName === '@angular/material' && !version.includes('16')) {
warnings.push(`${libName}@${version} should be updated to v16 for full compatibility`);
}
// Check for signals compatibility
if (this.canBenefitFromSignals(libName)) {
recommendations.push(`${libName} can benefit from signals in Angular 16+`);
}
// Check for standalone components compatibility
if (this.canBenefitFromStandalone(libName)) {
recommendations.push(`${libName} supports standalone components in Angular 16+`);
}
}
}
if (warnings.length > 0) {
this.progressReporter?.warn(`Library compatibility warnings: ${warnings.join(', ')}`);
}
if (recommendations.length > 0) {
this.progressReporter?.info(`Enhancement opportunities: ${recommendations.join(', ')}`);
}
this.progressReporter?.info('✓ Third-party library compatibility validation completed');
}
catch (error) {
this.progressReporter?.warn(`Could not validate third-party compatibility: ${error instanceof Error ? error.message : String(error)}`);
}
}
}
/**
* Check if a library can benefit from signals
*/
canBenefitFromSignals(libName) {
const signalsCompatibleLibraries = [
'@ngrx/store',
'@ngrx/signals',
'rxjs',
'@angular/forms'
];
return signalsCompatibleLibraries.some(lib => libName.includes(lib));
}
/**
* Check if a library can benefit from standalone components
*/
canBenefitFromStandalone(libName) {
const standaloneCompatibleLibraries = [
'@angular/material',
'@angular/cdk',
'ng-bootstrap',
'ngx-bootstrap',
'primeng'
];
return standaloneCompatibleLibraries.some(lib => libName.includes(lib));
}
/**
* Gets all available migrations for Angular 16 including base and version-specific ones
*
* Combines inherited migrations from the base handler with Angular 16 specific
* migrations like required inputs, router data as input, and non-destructive hydration.
* Each migration includes command, description, and optional flag.
*
* @returns Array of available migration objects
* @protected
* @override
*
* @example
* ```typescript
* const migrations = handler.getAvailableMigrations();
* // Returns migrations for: Required Inputs, Router Data as Input, etc.
* ```
*/
getAvailableMigrations() {
const baseMigrations = super.getAvailableMigrations();
// Add Angular 16 specific migrations
const angular16Migrations = [
{
name: 'Required Inputs',
command: 'npx ng generate @angular/core:required-inputs',
description: 'Migrate to required inputs API',
optional: true
},
{
name: 'Router Data as Input',
command: 'npx ng generate @angular/router:router-data-input',
description: 'Enable router data as component input',
optional: true
},
{
name: 'Non-destructive Hydration',
command: 'npx ng generate @angular/platform-browser:hydration',
description: 'Enable non-destructive hydration for SSR',
optional: true
}
];
return [...baseMigrations, ...angular16Migrations];
}
/**
* Run Angular 16 specific migrations
*/
async runVersionSpecificMigrations(projectPath) {
const migrations = this.getAvailableMigrations();
// Run Angular 16 specific migrations
const angular16Migrations = [
'Self-closing Tags',
'Required Inputs',
'Router Data as Input',
'inject() Function',
'Standalone Components',
'Cleanup Unused Imports'
];
for (const migrationName of angular16Migrations) {
const migration = migrations.find(m => m.name === migrationName);
if (migration) {
try {
this.progressReporter?.updateMessage(`Running ${migration.name} migration...`);
const command = migration.command + ' --interactive=false --defaults';
await this.runCommand(command, projectPath);
this.progressReporter?.info(`✓ ${migration.name} migration completed`);
}
catch (error) {
this.progressReporter?.warn(`${migration.name} migration skipped: ${error instanceof Error ? error.message : String(error)}`);
}
}
}
}
}
exports.Angular16Handler = Angular16Handler;
//# sourceMappingURL=Angular16Handler.js.map