|
| 1 | +/** |
| 2 | + * @license |
| 3 | + * Copyright Google LLC All Rights Reserved. |
| 4 | + * |
| 5 | + * Use of this source code is governed by an MIT-style license that can be |
| 6 | + * found in the LICENSE file at https://angular.io/license |
| 7 | + */ |
| 8 | + |
| 9 | +import {extname} from '@angular-devkit/core'; |
| 10 | +import {SchematicContext} from '@angular-devkit/schematics'; |
| 11 | +import {DevkitMigration, ResolvedResource, TargetVersion} from '@angular/cdk/schematics'; |
| 12 | +import {addThemeBaseMixins, checkThemeBaseMixins} from './migration'; |
| 13 | + |
| 14 | +/** Adds an @include for theme base mixins that aren't already included by the app. */ |
| 15 | +export class ThemeBaseMigration extends DevkitMigration<null> { |
| 16 | + /** Number of files that have been migrated. */ |
| 17 | + static migratedFileCount = 0; |
| 18 | + |
| 19 | + /** All base mixins that we have found an existing @include for. */ |
| 20 | + static foundBaseMixins = new Set<string>(); |
| 21 | + |
| 22 | + /** All base mixins that appear to be missing an @include. */ |
| 23 | + static missingBaseMixins = new Set<string>(); |
| 24 | + |
| 25 | + /** Whether to run this migration. */ |
| 26 | + enabled = this.targetVersion === TargetVersion.V17; |
| 27 | + |
| 28 | + /** |
| 29 | + * All Sass stylesheets visited. (We save a record, so we can go back through them in the |
| 30 | + * `postAnalysis` phase). |
| 31 | + */ |
| 32 | + visitedSassStylesheets: ResolvedResource[] = []; |
| 33 | + |
| 34 | + /** |
| 35 | + * Visit each stylesheet, noting which base mixins are accounted for (because the user is calling |
| 36 | + * `mat.<component>-theme()`), and which ones are missing (because the user is calling one of the |
| 37 | + * theme-partial mixins: `mat.<component-color>()`, `mat.<component>-typography()`, |
| 38 | + * or `mat.<component>-density()`. |
| 39 | + * |
| 40 | + * We don't make any modifications at this point. Instead, the results of visiting each stylesheet |
| 41 | + * are aggregated into a static variable which is used to determine which mixins to add in |
| 42 | + * `postAnalysis` phase. |
| 43 | + */ |
| 44 | + override visitStylesheet(stylesheet: ResolvedResource): void { |
| 45 | + if (extname(stylesheet.filePath) === '.scss') { |
| 46 | + this.visitedSassStylesheets.push(stylesheet); |
| 47 | + |
| 48 | + const content = stylesheet.content; |
| 49 | + const {found, missing} = checkThemeBaseMixins(content); |
| 50 | + for (const mixin of found) { |
| 51 | + ThemeBaseMigration.foundBaseMixins.add(mixin); |
| 52 | + ThemeBaseMigration.missingBaseMixins.delete(mixin); |
| 53 | + } |
| 54 | + for (const mixin of missing) { |
| 55 | + if (!ThemeBaseMigration.foundBaseMixins.has(mixin)) { |
| 56 | + ThemeBaseMigration.missingBaseMixins.add(mixin); |
| 57 | + } |
| 58 | + } |
| 59 | + } |
| 60 | + } |
| 61 | + |
| 62 | + /** |
| 63 | + * Perform the necessary updates detected while visiting the stylesheets. The |
| 64 | + * `mat.<component>-base()` mixins behave similarly to `mat.core()`, in that they needed to be |
| 65 | + * included once globally. So we locate calls to `mat.core()` and add the missing mixins |
| 66 | + * identified by earlier at these locations. |
| 67 | + */ |
| 68 | + override postAnalysis() { |
| 69 | + // If we're not missing any mixins, there's nothing to migrate. |
| 70 | + if (ThemeBaseMigration.missingBaseMixins.size === 0) { |
| 71 | + return; |
| 72 | + } |
| 73 | + // If we have all-component-bases, we don't need any others and there is nothing to migrate. |
| 74 | + if (ThemeBaseMigration.foundBaseMixins.has('all-component-bases')) { |
| 75 | + return; |
| 76 | + } |
| 77 | + // If we're missing all-component-bases, we just need to add it, not the individual mixins. |
| 78 | + if (ThemeBaseMigration.missingBaseMixins.has('all-component-bases')) { |
| 79 | + ThemeBaseMigration.missingBaseMixins = new Set(['all-component-bases']); |
| 80 | + } |
| 81 | + for (const stylesheet of this.visitedSassStylesheets) { |
| 82 | + const content = stylesheet.content; |
| 83 | + const migratedContent = content |
| 84 | + ? addThemeBaseMixins(content, ThemeBaseMigration.missingBaseMixins) |
| 85 | + : content; |
| 86 | + |
| 87 | + if (migratedContent && migratedContent !== content) { |
| 88 | + this.fileSystem |
| 89 | + .edit(stylesheet.filePath) |
| 90 | + .remove(0, stylesheet.content.length) |
| 91 | + .insertLeft(0, migratedContent); |
| 92 | + ThemeBaseMigration.migratedFileCount++; |
| 93 | + } |
| 94 | + } |
| 95 | + if (ThemeBaseMigration.migratedFileCount === 0) { |
| 96 | + const mixinsText = [...ThemeBaseMigration.missingBaseMixins] |
| 97 | + .sort() |
| 98 | + .map(m => `mat.${m}($theme)`) |
| 99 | + .join('\n'); |
| 100 | + this.failures.push({ |
| 101 | + filePath: this.context.tree.root.path, |
| 102 | + message: |
| 103 | + `The following mixins could not be automatically added, please add them manually` + |
| 104 | + ` if needed:\n${mixinsText}`, |
| 105 | + }); |
| 106 | + } |
| 107 | + } |
| 108 | + |
| 109 | + /** Logs out the number of migrated files at the end of the migration. */ |
| 110 | + static override globalPostMigration( |
| 111 | + _tree: unknown, |
| 112 | + _targetVersion: TargetVersion, |
| 113 | + context: SchematicContext, |
| 114 | + ): void { |
| 115 | + const fileCount = ThemeBaseMigration.migratedFileCount; |
| 116 | + const mixinCount = ThemeBaseMigration.missingBaseMixins.size; |
| 117 | + |
| 118 | + if (fileCount > 0 && mixinCount > 0) { |
| 119 | + const fileCountText = fileCount === 1 ? '1 file' : `${fileCount} files`; |
| 120 | + const mixinCountText = |
| 121 | + mixinCount === 1 ? '1 theme base mixin' : `${mixinCount} theme base mixins`; |
| 122 | + context.logger.info( |
| 123 | + `Added ${mixinCountText} to ${fileCountText}.` + |
| 124 | + ' Please search for, and address, any "TODO(v17)" comments.', |
| 125 | + ); |
| 126 | + } |
| 127 | + |
| 128 | + // Reset to avoid leaking between tests. |
| 129 | + ThemeBaseMigration.migratedFileCount = 0; |
| 130 | + ThemeBaseMigration.missingBaseMixins = new Set(); |
| 131 | + ThemeBaseMigration.foundBaseMixins = new Set(); |
| 132 | + } |
| 133 | +} |
0 commit comments