Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 54 additions & 7 deletions src/material/schematics/ng-update/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,72 @@
* 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';

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';
Copy link

@mauxnier mauxnier Jan 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does it work for offline projects that use angular?

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,
`<link href="${materialSymbolsFont}" rel="stylesheet">`,
);
}
}
};
}

/** Function that will be called when the migration completed. */
Expand Down
Original file line number Diff line number Diff line change
@@ -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<unknown>;

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,
`
<!doctype html>
<html lang="en">
<head>
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body></body>
</html>
`,
);

await runMigration();

expect(stripWhitespace(tree.readText(INDEX_HTML_FILE_PATH))).toBe(
stripWhitespace(`
<!doctype html>
<html lang="en">
<head>
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined" rel="stylesheet">
</head>
<body></body>
</html>
`),
);
});

it('should not add Material Symbols font to index html file if it is already imported', async () => {
writeFile(
INDEX_HTML_FILE_PATH,
`
<!doctype html>
<html lang="en">
<head>
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined" rel="stylesheet">
</head>
<body></body>
</html>
`,
);

await runMigration();

expect(stripWhitespace(tree.readText(INDEX_HTML_FILE_PATH))).toBe(
stripWhitespace(`
<!doctype html>
<html lang="en">
<head>
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined" rel="stylesheet">
</head>
<body></body>
</html>
`),
);
});
});
Loading