diff --git a/packages/schematics/angular/migrations/use-application-builder/migration.ts b/packages/schematics/angular/migrations/use-application-builder/migration.ts index dcf0526a3945..510d11bdc345 100644 --- a/packages/schematics/angular/migrations/use-application-builder/migration.ts +++ b/packages/schematics/angular/migrations/use-application-builder/migration.ts @@ -39,6 +39,7 @@ import { function* updateBuildTarget( projectName: string, + projectRoot: string, buildTarget: TargetDefinition, serverTarget: TargetDefinition | undefined, tree: Tree, @@ -46,6 +47,8 @@ function* updateBuildTarget( ): Iterable { // Update builder target and options buildTarget.builder = Builders.Application; + const baseServiceWorker = buildTarget.options?.['serviceWorker']; + const baseNgswConfigPath = buildTarget.options?.['ngswConfigPath']; for (const [, options] of allTargetOptions(buildTarget, false)) { if (options['index'] === '') { @@ -57,7 +60,18 @@ function* updateBuildTarget( if (serverTarget && typeof options['browser'] === 'string') { options['server'] = dirname(options['browser']) + '/main.server.ts'; } - options['serviceWorker'] = options['ngswConfigPath'] ?? options['serviceWorker']; + if (options['serviceWorker'] === true) { + // The application builder only accepts a path, while the browser builder defaults it in code. + options['serviceWorker'] = + options['ngswConfigPath'] ?? baseNgswConfigPath ?? join(projectRoot, 'ngsw-config.json'); + } else if ( + options['serviceWorker'] === undefined && + baseServiceWorker === true && + typeof options['ngswConfigPath'] === 'string' + ) { + // A configuration that only changes the config path of the service worker enabled in the base options. + options['serviceWorker'] = options['ngswConfigPath']; + } if (typeof options['polyfills'] === 'string') { options['polyfills'] = [options['polyfills']]; @@ -197,7 +211,9 @@ function updateProjects(tree: Tree, context: SchematicContext) { const serverTarget = project.targets.get('server'); - rules.push(...updateBuildTarget(name, buildTarget, serverTarget, tree, context)); + rules.push( + ...updateBuildTarget(name, project.root, buildTarget, serverTarget, tree, context), + ); // Delete all redundant targets for (const [key, target] of project.targets) { diff --git a/packages/schematics/angular/migrations/use-application-builder/migration_spec.ts b/packages/schematics/angular/migrations/use-application-builder/migration_spec.ts index 05259962dc5d..3eded0bd8137 100644 --- a/packages/schematics/angular/migrations/use-application-builder/migration_spec.ts +++ b/packages/schematics/angular/migrations/use-application-builder/migration_spec.ts @@ -113,6 +113,78 @@ describe(`Migration to use the application builder`, () => { }); }); + it(`should replace 'serviceWorker: true' with the default 'ngsw-config.json' path`, async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const workspaceContent = tree.readJson('/angular.json') as Record; + workspaceContent['projects']['app']['architect']['build']['options']['serviceWorker'] = true; + tree.overwrite('/angular.json', JSON.stringify(workspaceContent)); + + const newTree = await schematicRunner.runSchematic(schematicName, {}, tree); + const { + projects: { app }, + } = JSON.parse(newTree.readContent('/angular.json')); + + const { serviceWorker, ngswConfigPath } = app.architect['build'].options; + expect(serviceWorker).toBe('/project/app/ngsw-config.json'); + expect(ngswConfigPath).toBeUndefined(); + }); + + it(`should replace 'serviceWorker: true' in a configuration with the base 'ngswConfigPath'`, async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const workspaceContent = tree.readJson('/angular.json') as Record; + const build = workspaceContent['projects']['app']['architect']['build']; + build['options']['ngswConfigPath'] = 'src/ngsw-config.json'; + build['configurations'] = { production: { serviceWorker: true } }; + tree.overwrite('/angular.json', JSON.stringify(workspaceContent)); + + const newTree = await schematicRunner.runSchematic(schematicName, {}, tree); + const { + projects: { app }, + } = JSON.parse(newTree.readContent('/angular.json')); + + expect(app.architect['build'].configurations.production.serviceWorker).toBe( + 'src/ngsw-config.json', + ); + expect(app.architect['build'].options.serviceWorker).toBeUndefined(); + }); + + it(`should not enable the service worker when 'serviceWorker' is false`, async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const workspaceContent = tree.readJson('/angular.json') as Record; + const build = workspaceContent['projects']['app']['architect']['build']; + build['options']['serviceWorker'] = false; + build['options']['ngswConfigPath'] = 'src/ngsw-config.json'; + tree.overwrite('/angular.json', JSON.stringify(workspaceContent)); + + const newTree = await schematicRunner.runSchematic(schematicName, {}, tree); + const { + projects: { app }, + } = JSON.parse(newTree.readContent('/angular.json')); + + const { serviceWorker, ngswConfigPath } = app.architect['build'].options; + expect(serviceWorker).toBeFalse(); + expect(ngswConfigPath).toBeUndefined(); + }); + + it(`should use a configuration 'ngswConfigPath' when the base options enable the service worker`, async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const workspaceContent = tree.readJson('/angular.json') as Record; + const build = workspaceContent['projects']['app']['architect']['build']; + build['options']['serviceWorker'] = true; + build['configurations'] = { production: { ngswConfigPath: 'src/ngsw-config.prod.json' } }; + tree.overwrite('/angular.json', JSON.stringify(workspaceContent)); + + const newTree = await schematicRunner.runSchematic(schematicName, {}, tree); + const { + projects: { app }, + } = JSON.parse(newTree.readContent('/angular.json')); + + expect(app.architect['build'].options.serviceWorker).toBe('/project/app/ngsw-config.json'); + expect(app.architect['build'].configurations.production.serviceWorker).toBe( + 'src/ngsw-config.prod.json', + ); + }); + it(`should remove 'builderMode' from karma options`, async () => { addWorkspaceTarget(tree, 'test', { 'builder': Builders.Karma,