/** * Build Config Updater * * Updates TypeScript configuration (tsconfig.json paths) and * Angular library configuration (ng-package.json) after library extraction. * * tsconfig.json paths added: * "@myapp/ui": ["projects/ui/src/index.ts"] * "@myapp/ui/button": ["projects/ui/src/lib/button/index.ts"] * * ng-package.json updated with secondaryEntryPoints if needed. */ import * as path from 'node:path'; import * as fs from 'node:fs'; import type { LibraryExtractionOptions } from './library-extraction.orchestrator.js'; /** * Result of build config update operations. */ export interface BuildConfigUpdateResult { tsconfigUpdated: boolean; ngPackageUpdated: boolean; pathsAdded: Record; } /** * Updates TypeScript and Angular build configurations after library extraction. */ export class BuildConfigUpdater { /** * Update tsconfig.json paths and ng-package.json for the new library. */ async update( projectRoot: string, options: LibraryExtractionOptions, libraryPath: string, ): Promise { const pathsAdded: Record = {}; const tsconfigUpdated = this.updateTsConfig( projectRoot, options, libraryPath, pathsAdded, ); const ngPackageUpdated = this.updateNgPackage(libraryPath, options); return { tsconfigUpdated, ngPackageUpdated, pathsAdded }; } // --------------------------------------------------------------------------- // tsconfig.json paths // --------------------------------------------------------------------------- private updateTsConfig( projectRoot: string, options: LibraryExtractionOptions, libraryPath: string, pathsAdded: Record, ): boolean { const tsconfigPath = path.join(projectRoot, 'tsconfig.json'); if (!fs.existsSync(tsconfigPath)) { return false; } let tsconfig: { compilerOptions?: { paths?: Record }; }; try { tsconfig = JSON.parse( fs.readFileSync(tsconfigPath, 'utf-8'), ) as typeof tsconfig; } catch { return false; } tsconfig.compilerOptions ??= {}; tsconfig.compilerOptions.paths ??= {}; const paths = tsconfig.compilerOptions.paths; const relative = path .relative(projectRoot, libraryPath) .replaceAll('\\', '/'); let modified = false; // Main entry point const mainKey = `${options.libraryPrefix}/${options.libraryName}`; const mainValue = `${relative}/src/index.ts`; if (!paths[mainKey]) { paths[mainKey] = [mainValue]; pathsAdded[mainKey] = [mainValue]; modified = true; } // Secondary entry points for (const ep of options.secondaryEntryPoints ?? []) { const epKey = `${options.libraryPrefix}/${options.libraryName}/${ep}`; const epValue = `${relative}/src/lib/${ep}/index.ts`; if (!paths[epKey]) { paths[epKey] = [epValue]; pathsAdded[epKey] = [epValue]; modified = true; } } if (modified) { fs.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2) + '\n'); } return modified; } // --------------------------------------------------------------------------- // ng-package.json // --------------------------------------------------------------------------- private updateNgPackage( libraryPath: string, options: LibraryExtractionOptions, ): boolean { const ngPackagePath = path.join(libraryPath, 'ng-package.json'); if (!fs.existsSync(ngPackagePath)) { return false; } let ngPackage: { lib?: { entryFile?: string; secondaryEntryPoints?: string[] }; }; try { ngPackage = JSON.parse( fs.readFileSync(ngPackagePath, 'utf-8'), ) as typeof ngPackage; } catch { return false; } ngPackage.lib ??= {}; const existingEntryPoints = ngPackage.lib.secondaryEntryPoints ?? []; const toAdd = (options.secondaryEntryPoints ?? []).filter( (ep) => !existingEntryPoints.includes(ep), ); if (toAdd.length === 0) { return false; } ngPackage.lib.secondaryEntryPoints = [...existingEntryPoints, ...toAdd]; fs.writeFileSync(ngPackagePath, JSON.stringify(ngPackage, null, 2) + '\n'); return true; } }