diff --git a/packages/angular/src/utils/nx-devkit/ast-utils.spec.ts b/packages/angular/src/utils/nx-devkit/ast-utils.spec.ts index f438acd2cf04b..79748e8fcf38a 100644 --- a/packages/angular/src/utils/nx-devkit/ast-utils.spec.ts +++ b/packages/angular/src/utils/nx-devkit/ast-utils.spec.ts @@ -3,6 +3,7 @@ import { addImportToDirective, addImportToModule, addImportToPipe, + addProviderToBootstrapApplication, isStandalone, } from './ast-utils'; import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; @@ -260,4 +261,45 @@ describe('Angular AST Utils', () => { // ASSERT expect(isStandalone(tsSourceFile, 'Pipe')).toBeTruthy(); }); + + it('should add a provider to the bootstrapApplication call', () => { + // ARRANGE + const tree = createTreeWithEmptyWorkspace(); + tree.write( + 'main.ts', + `import { bootstrapApplication } from '@angular/platform-browser'; +import { + provideRouter, + withEnabledBlockingInitialNavigation, +} from '@angular/router'; +import { AppComponent } from './app/app.component'; +import { appRoutes } from './app/app.routes'; + +bootstrapApplication(AppComponent, { + providers: [ + provideRouter(appRoutes, withEnabledBlockingInitialNavigation()), + ], +}).catch((err) => console.error(err));` + ); + + // ACT + addProviderToBootstrapApplication(tree, 'main.ts', 'provideStore()'); + + // ASSERT + expect(tree.read('main.ts', 'utf-8')).toMatchInlineSnapshot(` + "import { bootstrapApplication } from '@angular/platform-browser'; + import { + provideRouter, + withEnabledBlockingInitialNavigation, + } from '@angular/router'; + import { AppComponent } from './app/app.component'; + import { appRoutes } from './app/app.routes'; + + bootstrapApplication(AppComponent, { + providers: [provideStore(), + provideRouter(appRoutes, withEnabledBlockingInitialNavigation()), + ], + }).catch((err) => console.error(err));" + `); + }); }); diff --git a/packages/angular/src/utils/nx-devkit/ast-utils.ts b/packages/angular/src/utils/nx-devkit/ast-utils.ts index 9e4dc05a799f5..3e1c9c1a86c44 100644 --- a/packages/angular/src/utils/nx-devkit/ast-utils.ts +++ b/packages/angular/src/utils/nx-devkit/ast-utils.ts @@ -9,6 +9,7 @@ import { removeChange, replaceChange, } from '@nrwl/workspace/src/utilities/ast-utils'; +import { tsquery } from '@phenomnomnominal/tsquery'; type DecoratorName = 'Component' | 'Directive' | 'NgModule' | 'Pipe'; @@ -605,6 +606,38 @@ function getListOfRoutes( return null; } +export function addProviderToBootstrapApplication( + tree: Tree, + filePath: string, + providerToAdd: string +) { + const PROVIDERS_ARRAY_SELECTOR = + 'CallExpression:has(Identifier[name=bootstrapApplication]) ObjectLiteralExpression > PropertyAssignment:has(Identifier[name=providers]) > ArrayLiteralExpression'; + + const fileContents = tree.read(filePath, 'utf-8'); + const ast = tsquery.ast(fileContents); + const providersArrayNodes = tsquery(ast, PROVIDERS_ARRAY_SELECTOR, { + visitAllChildren: true, + }); + if (providersArrayNodes.length === 0) { + throw new Error( + `Providers does not exist in the bootstrapApplication call within ${filePath}.` + ); + } + + const arrayNode = providersArrayNodes[0]; + + const newFileContents = `${fileContents.slice( + 0, + arrayNode.getStart() + 1 + )}${providerToAdd},${fileContents.slice( + arrayNode.getStart() + 1, + fileContents.length + )}`; + + tree.write(filePath, newFileContents); +} + export function addProviderToModule( host: Tree, source: ts.SourceFile, diff --git a/packages/angular/src/utils/nx-devkit/route-utils.ts b/packages/angular/src/utils/nx-devkit/route-utils.ts index 867b26c7af8ef..abad36e14cd1b 100644 --- a/packages/angular/src/utils/nx-devkit/route-utils.ts +++ b/packages/angular/src/utils/nx-devkit/route-utils.ts @@ -131,12 +131,18 @@ export function addProviderToRoute( } ); - const routeText = selectedRouteNode.getText(); if (routeProivdersNodes.length === 0) { const newFileContents = `${routesFileContents.slice( 0, selectedRouteNode.getEnd() - 1 - )}, providers: [${providerToAdd}]${routesFileContents.slice( + )}${ + routesFileContents + .slice(0, selectedRouteNode.getEnd() - 1) + .trim() + .endsWith(',') + ? '' + : ', ' + }providers: [${providerToAdd}]${routesFileContents.slice( selectedRouteNode.getEnd() - 1, routesFileContents.length )}`;