diff --git a/src/material/schematics/ng-update/index.ts b/src/material/schematics/ng-update/index.ts index 52ab85b84b7e..468a25c536f7 100644 --- a/src/material/schematics/ng-update/index.ts +++ b/src/material/schematics/ng-update/index.ts @@ -6,12 +6,17 @@ * found in the LICENSE file at https://angular.dev/license */ -import {Rule, SchematicContext} from '@angular-devkit/schematics'; +import {chain, Rule, SchematicContext, Tree} from '@angular-devkit/schematics'; import { + appendHtmlElementToHead, createMigrationSchematicRule, + getProjectFromWorkspace, + getProjectIndexFiles, + getWorkspaceConfigGracefully, NullableDevkitMigration, TargetVersion, } from '@angular/cdk/schematics'; +import {getWorkspace} from '@schematics/angular/utility/workspace'; import {materialUpgradeData} from './upgrade-data'; @@ -19,12 +24,54 @@ const materialMigrations: NullableDevkitMigration[] = []; /** Entry point for the migration schematics with target of Angular Material v20 */ export function updateToV20(): Rule { - return createMigrationSchematicRule( - TargetVersion.V20, - materialMigrations, - materialUpgradeData, - onMigrationComplete, - ); + return chain([ + createMigrationSchematicRule( + TargetVersion.V20, + materialMigrations, + materialUpgradeData, + onMigrationComplete, + ), + // Updating to the new Material Symbols isn't a migration within materialMigrations since + // the index files are never visited within the migration schematic rule. The + // migrate() function within the update-tool only visits files referenced in + // typescript files which excludes the index template files: + // https://github.com/angular/components/blob/main/src/cdk/schematics/update-tool/index.ts#L71. + updateIconFontToMaterialSymbolsRule(), + ]); +} + +/** + * Finds the index files and adds the import for Material Symbols font if needed. As of v20, + * Material Symbols becomes the default font icon since Material Icons is deprecated. This + * rule ensures the Material Symbols font is imported for existing applications. + * @returns Rule that adds the import for the Material Symbols icon font to the index files + */ +function updateIconFontToMaterialSymbolsRule(): Rule { + return async (tree: Tree, context: SchematicContext) => { + const workspace = await getWorkspaceConfigGracefully(tree); + const projectNames = workspace!.projects.keys(); + + let indexFiles: string[] = []; + for (const projectName of projectNames) { + const project = getProjectFromWorkspace(await getWorkspace(tree), projectName); + indexFiles = [...indexFiles, ...getProjectIndexFiles(project)]; + } + + const materialSymbolsFont = + 'https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined'; + for (const indexFile of indexFiles) { + // Add Material Symbols font if not imported in index file. References to the deprecated + // Material Icons are not removed since some applications may have manual overrides in their + // component styles that still reference it. + if (!tree.read(indexFile)?.includes(materialSymbolsFont)) { + appendHtmlElementToHead( + tree, + indexFile, + ``, + ); + } + } + }; } /** Function that will be called when the migration completed. */ diff --git a/src/material/schematics/ng-update/test-cases/v20-mat-symbols-icon-font.spec.ts b/src/material/schematics/ng-update/test-cases/v20-mat-symbols-icon-font.spec.ts new file mode 100644 index 000000000000..4ce196537d38 --- /dev/null +++ b/src/material/schematics/ng-update/test-cases/v20-mat-symbols-icon-font.spec.ts @@ -0,0 +1,87 @@ +import {UnitTestTree} from '@angular-devkit/schematics/testing'; +import {createTestCaseSetup} from '@angular/cdk/schematics/testing'; +import {MIGRATION_PATH} from '../../paths'; + +const INDEX_HTML_FILE_PATH = '/projects/cdk-testing/src/index.html'; + +describe('v20 material symbols icon font migration', () => { + let tree: UnitTestTree; + let writeFile: (filename: string, content: string) => void; + let runMigration: () => Promise; + + function stripWhitespace(content: string): string { + return content.replace(/\s/g, ''); + } + + beforeEach(async () => { + const testSetup = await createTestCaseSetup('migration-v20', MIGRATION_PATH, []); + tree = testSetup.appTree; + writeFile = testSetup.writeFile; + runMigration = testSetup.runFixers; + }); + + it('should add Material Symbols font to index html file', async () => { + writeFile( + INDEX_HTML_FILE_PATH, + ` + + + + + + + + + `, + ); + + await runMigration(); + + expect(stripWhitespace(tree.readText(INDEX_HTML_FILE_PATH))).toBe( + stripWhitespace(` + + + + + + + + + + `), + ); + }); + + it('should not add Material Symbols font to index html file if it is already imported', async () => { + writeFile( + INDEX_HTML_FILE_PATH, + ` + + + + + + + + + + `, + ); + + await runMigration(); + + expect(stripWhitespace(tree.readText(INDEX_HTML_FILE_PATH))).toBe( + stripWhitespace(` + + + + + + + + + + `), + ); + }); +});