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
1,151 lines (1,092 loc) • 42.4 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.Angular15Handler = void 0;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const BaseVersionHandler_1 = require("./BaseVersionHandler");
const SSRDetector_1 = require("../utils/SSRDetector");
const DependencyCompatibilityMatrix_1 = require("../utils/DependencyCompatibilityMatrix");
/**
* Angular 15 Handler - Standalone APIs stabilization and Image directive
*
* Key Features in Angular 15:
* - Standalone APIs stabilization (components, directives, pipes)
* - Angular Image directive with built-in optimizations
* - MDC-based Angular Material migration
* - Better stack traces in development
* - Directive composition API
* - Optional guards with inject() function
* - Extended developer experience improvements
*/
class Angular15Handler extends BaseVersionHandler_1.BaseVersionHandler {
version = '15';
getRequiredNodeVersion() {
return '>=14.20.0';
}
getRequiredTypeScriptVersion() {
return '>=4.8.2 <4.10.0';
}
/**
* Get Angular 15 dependencies with correct versions
*/
getDependencyUpdates() {
return [
// Core Angular packages
{ name: '@angular/animations', version: '^15.0.0', type: 'dependencies' },
{ name: '@angular/common', version: '^15.0.0', type: 'dependencies' },
{ name: '@angular/compiler', version: '^15.0.0', type: 'dependencies' },
{ name: '@angular/core', version: '^15.0.0', type: 'dependencies' },
{ name: '@angular/forms', version: '^15.0.0', type: 'dependencies' },
{ name: '@angular/platform-browser', version: '^15.0.0', type: 'dependencies' },
{ name: '@angular/platform-browser-dynamic', version: '^15.0.0', type: 'dependencies' },
{ name: '@angular/router', version: '^15.0.0', type: 'dependencies' },
// Angular CLI and dev dependencies
{ name: '@angular/cli', version: '^15.0.0', type: 'devDependencies' },
{ name: '@angular/compiler-cli', version: '^15.0.0', type: 'devDependencies' },
{ name: '@angular-devkit/build-angular', version: '^15.0.0', type: 'devDependencies' },
// TypeScript and supporting packages
{ name: 'typescript', version: '~4.8.2', type: 'devDependencies' },
{ name: 'zone.js', version: '~0.12.0', type: 'dependencies' },
{ name: 'rxjs', version: '~7.5.0', type: 'dependencies' },
// Angular Material and CDK - MDC-based
{ name: '@angular/material', version: '^15.0.0', type: 'dependencies' },
{ name: '@angular/cdk', version: '^15.0.0', type: 'dependencies' },
// Third-party Angular ecosystem packages
...DependencyCompatibilityMatrix_1.DependencyCompatibilityMatrix.getCompatibleDependencies('15').map(dep => ({
name: dep.name,
version: dep.version,
type: dep.type
}))
];
}
async applyVersionSpecificChanges(projectPath, options) {
this.progressReporter?.updateMessage('Applying Angular 15 transformations...');
// Check if this is an SSR application
const isSSRApp = await SSRDetector_1.SSRDetector.isSSRApplication(projectPath);
this.progressReporter?.info(`Application type: ${isSSRApp ? 'SSR (Server-Side Rendering)' : 'CSR (Client-Side Rendering)'}`);
// 1. Stabilize standalone APIs and components
await this.stabilizeStandaloneAPIs(projectPath);
// 2. Implement Angular Image directive with optimizations
await this.implementImageDirective(projectPath);
// 3. Setup MDC-based Angular Material migration
await this.setupMDCMaterialMigration(projectPath);
// 4. Configure better stack traces for development
await this.configureBetterStackTraces(projectPath);
// 5. Implement directive composition API
await this.implementDirectiveComposition(projectPath);
// 6. Setup optional guards with inject() function
await this.setupOptionalInjectGuards(projectPath);
// 7. Enhanced developer experience improvements
await this.enhancedDeveloperExperience(projectPath);
// 8. Update build configurations for Angular 15
await this.updateBuildConfigurations(projectPath);
// 9. Validate third-party compatibility
await this.validateThirdPartyCompatibility(projectPath);
this.progressReporter?.success('✓ Angular 15 transformations completed');
}
getBreakingChanges() {
return [
// Standalone APIs stabilization
this.createBreakingChange('ng15-standalone-apis-stable', 'api', 'low', 'Standalone APIs stabilized', 'Standalone components, directives, and pipes are now stable APIs', 'No breaking changes - APIs are now stable and ready for production use'),
// TypeScript version requirement
this.createBreakingChange('ng15-typescript-version', 'dependency', 'medium', 'TypeScript 4.8+ required', 'Angular 15 requires TypeScript 4.8.2 or higher', 'Update TypeScript to version 4.8.2 or higher'),
// Node.js version requirement
this.createBreakingChange('ng15-nodejs-version', 'dependency', 'medium', 'Node.js 14.20+ required', 'Angular 15 requires Node.js 14.20.0 or higher', 'Update Node.js to version 14.20.0 or higher'),
// MDC-based Angular Material
this.createBreakingChange('ng15-material-mdc', 'dependency', 'medium', 'Angular Material now MDC-based', 'Angular Material components are now based on Material Design Components (MDC)', 'Update Material components and styles for MDC compatibility'),
// Image directive optimization
this.createBreakingChange('ng15-image-directive', 'api', 'low', 'New optimized Image directive', 'NgOptimizedImage directive provides built-in image optimizations', 'New feature - existing img tags continue to work'),
// Directive composition API
this.createBreakingChange('ng15-directive-composition', 'api', 'low', 'Directive composition API', 'New API for composing directive functionality', 'New feature - existing directive patterns continue to work'),
// Better stack traces
this.createBreakingChange('ng15-stack-traces', 'config', 'low', 'Improved development stack traces', 'Better error reporting and debugging in development mode', 'No action required - automatic improvement')
];
}
// Private implementation methods
/**
* Stabilize standalone APIs and provide migration examples
*/
async stabilizeStandaloneAPIs(projectPath) {
try {
const exampleDir = path.join(projectPath, 'src/app/examples');
await fs.ensureDir(exampleDir);
// Create stable standalone API examples
const standaloneExample = `import { Component, Directive, Pipe, PipeTransform } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
// Stable standalone component with imports
export class StandaloneStableComponent {
message = 'Hello from stable standalone component!';
showDetails = false;
toggleDetails() {
this.showDetails = !this.showDetails;
}
}
// Stable standalone directive
export class HighlightDirective {
constructor(private el: ElementRef) {
this.el.nativeElement.style.backgroundColor = '#ffeb3b';
this.el.nativeElement.style.padding = '4px';
this.el.nativeElement.style.borderRadius = '2px';
}
}
// Stable standalone pipe
export class TitleCasePipe implements PipeTransform {
transform(value: string): string {
if (!value) return value;
return value
.toLowerCase()
.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
}
}
// Example of using standalone APIs together
export class StandaloneCompositionComponent {
title = 'angular 15 standalone apis are stable';
}
`;
const stablePath = path.join(exampleDir, 'standalone-stable-apis.component.ts');
if (!await fs.pathExists(stablePath)) {
await fs.writeFile(stablePath, standaloneExample);
this.progressReporter?.info('✓ Created stable standalone APIs examples');
}
}
catch (error) {
this.progressReporter?.warn(`Could not stabilize standalone APIs: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Implement Angular Image directive with optimizations
*/
async implementImageDirective(projectPath) {
try {
const exampleDir = path.join(projectPath, 'src/app/examples');
await fs.ensureDir(exampleDir);
// Create optimized image directive example
const imageExample = `import { Component } from '@angular/core';
import { NgOptimizedImage } from '@angular/common';
export class OptimizedImageExampleComponent {
// Component logic for handling image events if needed
onImageLoad(event: Event) {
console.log('Image loaded:', event);
}
onImageError(event: Event) {
console.log('Image failed to load:', event);
}
}
/*
Key benefits of NgOptimizedImage directive:
1. Automatic performance optimizations:
- Lazy loading by default
- Proper sizing to prevent layout shift
- Preload critical images with 'priority'
- Responsive image loading with 'sizes'
2. Built-in best practices:
- Requires width and height attributes
- Warns about missing alt text
- Validates image loading performance
- Prevents common performance pitfalls
3. Developer experience:
- Clear error messages for misconfigurations
- Runtime warnings for performance issues
- Easy migration from standard img tags
- TypeScript support for all attributes
Usage guidelines:
- Use 'priority' for above-the-fold images
- Set appropriate 'sizes' for responsive images
- Provide 'alt' text for accessibility
- Include 'width' and 'height' to prevent layout shift
- Use 'loading="lazy"' for below-the-fold images
*/
`;
const imagePath = path.join(exampleDir, 'optimized-image-example.component.ts');
if (!await fs.pathExists(imagePath)) {
await fs.writeFile(imagePath, imageExample);
this.progressReporter?.info('✓ Created optimized image directive example');
}
}
catch (error) {
this.progressReporter?.warn(`Could not implement image directive: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Setup MDC-based Angular Material migration
*/
async setupMDCMaterialMigration(projectPath) {
const packageJsonPath = path.join(projectPath, 'package.json');
if (await fs.pathExists(packageJsonPath)) {
try {
const packageJson = await fs.readJson(packageJsonPath);
// Check if Angular Material is being used
if (packageJson.dependencies?.['@angular/material']) {
// Create MDC migration guide
const migrationDir = path.join(projectPath, 'src/app/migrations');
await fs.ensureDir(migrationDir);
const mdcMigrationGuide = `/**
* Angular Material 15+ MDC Migration Guide
*
* Angular Material 15 introduces Material Design Components (MDC) as the foundation
* for all components. This provides better alignment with Material Design specifications.
*
* Key Changes:
*
* 1. Component Structure:
* - Internal DOM structure of components has changed
* - CSS classes have been updated to follow MDC naming
* - Some component APIs have been refined
*
* 2. Styling Changes:
* - Update custom CSS that targets internal component structure
* - Use Angular Material's theming APIs instead of direct CSS targeting
* - Check for styling regressions after upgrade
*
* 3. Migration Steps:
* - Run: ng update @angular/material
* - Test all Material components thoroughly
* - Update custom CSS that targets Material component internals
* - Verify accessibility improvements
*
* 4. Components Affected:
* - MatButton: New ripple implementation
* - MatCard: Updated elevation and styling
* - MatCheckbox: MDC-based implementation
* - MatChips: Significant API changes
* - MatFormField: Improved accessibility
* - MatList: Better semantic structure
* - MatMenu: Enhanced keyboard navigation
* - MatRadio: MDC-based styling
* - MatSelect: Improved dropdown behavior
* - MatSlider: New MDC implementation
* - MatTabs: Enhanced accessibility
* - MatTooltip: Better positioning
*
* 5. Testing Checklist:
* □ Visual regression testing for all Material components
* □ Accessibility testing (screen readers, keyboard navigation)
* □ Custom theme compatibility
* □ Component behavior in different screen sizes
* □ Custom CSS overrides still working
* □ Form validation and interaction
*/
// Example of updated Material component usage in Angular 15
import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatCheckboxModule } from '@angular/material/checkbox';
export class MDCMigrationExampleComponent {}
`;
const guidePath = path.join(migrationDir, 'mdc-migration-guide.ts');
if (!await fs.pathExists(guidePath)) {
await fs.writeFile(guidePath, mdcMigrationGuide);
this.progressReporter?.info('✓ Created MDC migration guide for Angular Material');
}
}
}
catch (error) {
this.progressReporter?.warn(`Could not setup MDC migration: ${error instanceof Error ? error.message : String(error)}`);
}
}
}
/**
* Configure better stack traces for development
*/
async configureBetterStackTraces(projectPath) {
const angularJsonPath = path.join(projectPath, 'angular.json');
if (await fs.pathExists(angularJsonPath)) {
try {
const angularJson = await fs.readJson(angularJsonPath);
// Update development configurations for better stack traces
for (const projectName in angularJson.projects) {
const project = angularJson.projects[projectName];
if (project.architect?.build) {
// Enhanced development configuration
if (!project.architect.build.configurations) {
project.architect.build.configurations = {};
}
// Update development configuration for better debugging
project.architect.build.configurations.development = {
...project.architect.build.configurations.development,
sourceMap: {
scripts: true,
styles: true,
vendor: true,
hidden: false
},
optimization: false,
namedChunks: true,
vendorChunk: true,
buildOptimizer: false
};
}
if (project.architect?.serve) {
// Enhanced serve configuration
project.architect.serve.options = {
...project.architect.serve.options,
buildTarget: `${projectName}:build:development`
};
}
}
await fs.writeJson(angularJsonPath, angularJson, { spaces: 2 });
this.progressReporter?.info('✓ Configured better stack traces for development');
}
catch (error) {
this.progressReporter?.warn(`Could not configure stack traces: ${error instanceof Error ? error.message : String(error)}`);
}
}
}
/**
* Implement directive composition API
*/
async implementDirectiveComposition(projectPath) {
try {
const exampleDir = path.join(projectPath, 'src/app/examples');
await fs.ensureDir(exampleDir);
// Create directive composition example
const compositionExample = `import { Component, Directive, HostBinding, HostListener, Input } from '@angular/core';
// Base directives for composition
export class ClickableDirective {
cursor = 'pointer';
tabindex = '0';
onClick(event: MouseEvent) {
console.log('Clickable element clicked:', event.target);
}
onKeydown(event: KeyboardEvent) {
event.preventDefault();
(event.target as HTMLElement).click();
}
}
export class HighlightableDirective {
highlightColor = '#ffeb3b';
onMouseEnter() {
this.setHighlight(this.highlightColor);
}
onMouseLeave() {
this.setHighlight('');
}
private setHighlight(color: string) {
const element = document.querySelector('[appHighlightable]') as HTMLElement;
if (element) {
element.style.backgroundColor = color;
}
}
}
export class LoadingStateDirective {
set appLoadingState(isLoading: boolean) {
this.updateLoadingState(isLoading);
}
private updateLoadingState(isLoading: boolean) {
const element = document.querySelector('[appLoadingState]') as HTMLElement;
if (element) {
element.style.opacity = isLoading ? '0.5' : '1';
element.style.pointerEvents = isLoading ? 'none' : 'auto';
if (isLoading) {
element.setAttribute('aria-busy', 'true');
} else {
element.removeAttribute('aria-busy');
}
}
}
}
// Composed directive using multiple behaviors
export class InteractiveCardDirective {
// This directive composes multiple behaviors
// without implementing them directly
}
// Example component using directive composition
export class DirectiveCompositionExampleComponent {
isLoading1 = false;
isLoading2 = false;
toggleLoading1() {
this.isLoading1 = !this.isLoading1;
}
toggleLoading2() {
this.isLoading2 = !this.isLoading2;
}
}
/*
Directive Composition API Benefits:
1. Reusability:
- Create small, focused directives
- Compose them into more complex behaviors
- Avoid code duplication
2. Maintainability:
- Single responsibility principle
- Easy to test individual behaviors
- Clear separation of concerns
3. Developer Experience:
- Type-safe composition
- Automatic input/output forwarding
- Cleaner template syntax
- Better IDE support
4. Performance:
- No runtime overhead
- Compile-time composition
- Optimized by Angular compiler
*/
`;
const compositionPath = path.join(exampleDir, 'directive-composition-example.component.ts');
if (!await fs.pathExists(compositionPath)) {
await fs.writeFile(compositionPath, compositionExample);
this.progressReporter?.info('✓ Created directive composition API example');
}
}
catch (error) {
this.progressReporter?.warn(`Could not implement directive composition: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Setup optional guards with inject() function
*/
async setupOptionalInjectGuards(projectPath) {
try {
const guardsDir = path.join(projectPath, 'src/app/guards');
await fs.ensureDir(guardsDir);
// Create inject-based functional guards
const injectGuardsExample = `import { inject } from '@angular/core';
import { Router, CanActivateFn, CanActivateChildFn, CanDeactivateFn } from '@angular/router';
import { AuthService } from '../services/auth.service';
import { Observable, of } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
// Functional guard using inject() - Angular 15+ approach
export const authGuard: CanActivateFn = (route, state) => {
const authService = inject(AuthService);
const router = inject(Router);
return authService.isAuthenticated().pipe(
map(isAuthenticated => {
if (isAuthenticated) {
return true;
} else {
router.navigate(['/login'], {
queryParams: { returnUrl: state.url }
});
return false;
}
}),
catchError(() => {
router.navigate(['/login']);
return of(false);
})
);
};
// Role-based functional guard
export const roleGuard = (requiredRole: string): CanActivateFn => {
return (route, state) => {
const authService = inject(AuthService);
const router = inject(Router);
return authService.getUserRole().pipe(
map(userRole => {
if (userRole === requiredRole || userRole === 'admin') {
return true;
} else {
router.navigate(['/unauthorized']);
return false;
}
}),
catchError(() => {
router.navigate(['/login']);
return of(false);
})
);
};
};
// Child route guard using inject()
export const childAuthGuard: CanActivateChildFn = (route, state) => {
// Reuse the auth guard logic for child routes
return authGuard(route, state);
};
// Unsaved changes guard using inject()
export const unsavedChangesGuard: CanDeactivateFn<any> = (component) => {
const router = inject(Router);
if (component.hasUnsavedChanges && component.hasUnsavedChanges()) {
return confirm('You have unsaved changes. Do you want to leave this page?');
}
return true;
};
// Advanced permission guard with dependency injection
export const permissionGuard = (permission: string): CanActivateFn => {
return (route, state) => {
const authService = inject(AuthService);
const router = inject(Router);
// Extract additional permission from route data
const routePermission = route.data?.['permission'];
const requiredPermission = routePermission || permission;
return authService.hasPermission(requiredPermission).pipe(
map(hasPermission => {
if (hasPermission) {
return true;
} else {
router.navigate(['/access-denied'], {
queryParams: {
required: requiredPermission,
attempted: state.url
}
});
return false;
}
})
);
};
};
// Example service that guards depend on
// (This would typically be in a separate file)
export interface AuthService {
isAuthenticated(): Observable<boolean>;
getUserRole(): Observable<string>;
hasPermission(permission: string): Observable<boolean>;
}
/*
Example usage in routing configuration:
const routes: Routes = [
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [authGuard]
},
{
path: 'admin',
component: AdminComponent,
canActivate: [authGuard, roleGuard('admin')]
},
{
path: 'profile',
component: ProfileComponent,
canActivate: [authGuard],
canDeactivate: [unsavedChangesGuard]
},
{
path: 'users',
component: UsersComponent,
canActivate: [permissionGuard('users.read')],
canActivateChild: [childAuthGuard],
children: [
{
path: 'edit/:id',
component: UserEditComponent,
data: { permission: 'users.write' }
}
]
}
];
Benefits of functional guards with inject():
1. Simpler syntax - no need for classes
2. Better tree-shaking - only import what you need
3. Easier composition - guards can be easily combined
4. Type safety - full TypeScript support
5. Testability - easier to unit test pure functions
6. Reusability - guards can be parameterized
*/
`;
const guardsPath = path.join(guardsDir, 'functional-guards.ts');
if (!await fs.pathExists(guardsPath)) {
await fs.writeFile(guardsPath, injectGuardsExample);
this.progressReporter?.info('✓ Created functional guards with inject() example');
}
}
catch (error) {
this.progressReporter?.warn(`Could not setup inject guards: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Enhanced developer experience improvements
*/
async enhancedDeveloperExperience(projectPath) {
const angularJsonPath = path.join(projectPath, 'angular.json');
if (await fs.pathExists(angularJsonPath)) {
try {
const angularJson = await fs.readJson(angularJsonPath);
// Enhanced development server configuration
for (const projectName in angularJson.projects) {
const project = angularJson.projects[projectName];
if (project.architect?.serve) {
// Improved development server settings
project.architect.serve.options = {
...project.architect.serve.options,
hmr: true,
liveReload: true,
poll: 1000, // For better file watching
open: false, // Don't auto-open browser
host: 'localhost',
disableHostCheck: false
};
// Enhanced development configuration
if (!project.architect.serve.configurations) {
project.architect.serve.configurations = {};
}
project.architect.serve.configurations.development = {
...project.architect.serve.configurations.development,
buildTarget: `${projectName}:build:development`,
hmr: true,
liveReload: true
};
}
}
await fs.writeJson(angularJsonPath, angularJson, { spaces: 2 });
this.progressReporter?.info('✓ Enhanced developer experience configuration');
}
catch (error) {
this.progressReporter?.warn(`Could not enhance developer experience: ${error instanceof Error ? error.message : String(error)}`);
}
}
}
/**
* Update build configurations for Angular 15
*/
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 15
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: {
minify: true,
inlineCritical: true
},
fonts: true
},
sourceMap: false,
namedChunks: false,
aot: true,
extractLicenses: true,
vendorChunk: false,
buildOptimizer: true,
// Angular 15 specific optimizations
preserveSymlinks: false,
extractCss: true
};
}
}
await fs.writeJson(angularJsonPath, angularJson, { spaces: 2 });
this.progressReporter?.info('✓ Updated build configurations for Angular 15');
}
catch (error) {
this.progressReporter?.warn(`Could not update build configurations: ${error instanceof Error ? error.message : String(error)}`);
}
}
}
/**
* Validate third-party compatibility for Angular 15
*/
async validateThirdPartyCompatibility(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 MDC compatibility
if (libName === '@angular/material' && !version.includes('15')) {
warnings.push(`${libName}@${version} should be updated to v15 for MDC-based components`);
}
// Check for standalone components compatibility
if (this.canBenefitFromStandalone(libName)) {
recommendations.push(`${libName} can benefit from stable standalone APIs in Angular 15+`);
}
// Check for directive composition opportunities
if (this.canBenefitFromDirectiveComposition(libName)) {
recommendations.push(`${libName} can benefit from directive composition API in Angular 15+`);
}
}
}
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 standalone components
*/
canBenefitFromStandalone(libName) {
const standaloneCompatibleLibraries = [
'@angular/material',
'@angular/cdk',
'ng-bootstrap',
'ngx-bootstrap',
'primeng',
'@ngrx/store'
];
return standaloneCompatibleLibraries.some(lib => libName.includes(lib));
}
/**
* Check if a library can benefit from directive composition
*/
canBenefitFromDirectiveComposition(libName) {
const compositionCompatibleLibraries = [
'@angular/cdk',
'ngx-bootstrap',
'primeng'
];
return compositionCompatibleLibraries.some(lib => libName.includes(lib));
}
}
exports.Angular15Handler = Angular15Handler;
//# sourceMappingURL=Angular15Handler.js.map