mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(ng-add): do not add theme file if existing theme is set up (#13468)
* fix(ng-add): do not add theme file if existing theme is set up * No longer adds other theme files to the workspace configuration if a custom theme has been generated in a previous run already. * Instead of silently doing nothing if a prebuilt theme is being generated while another prebuilt theme is already defined, we now replace the previous prebuilt theme with the new theme. * Do not overwrite existing custom-theme files
This commit is contained in:
committed by
Andrew Seguin
parent
21de68e6d7
commit
d1e59a21fc
@@ -207,4 +207,77 @@ describe('ng-add schematic', () => {
|
||||
'Expected the project app module to not import the "NoopAnimationsModule".');
|
||||
});
|
||||
});
|
||||
|
||||
describe('theme files', () => {
|
||||
|
||||
/** Path to the default prebuilt theme file that will be added when running ng-add. */
|
||||
const defaultPrebuiltThemePath =
|
||||
'./node_modules/@angular/material/prebuilt-themes/indigo-pink.css';
|
||||
|
||||
/** Writes a specific style file to the workspace in the given tree */
|
||||
function writeStyleFileToWorkspace(tree: Tree, stylePath: string) {
|
||||
const workspace = getWorkspace(tree);
|
||||
const project = getProjectFromWorkspace(workspace);
|
||||
const buildOptions = getProjectTargetOptions(project, 'build');
|
||||
|
||||
if (!buildOptions.styles) {
|
||||
buildOptions.styles = [stylePath];
|
||||
} else {
|
||||
buildOptions.styles.push(stylePath);
|
||||
}
|
||||
|
||||
tree.overwrite('/angular.json', JSON.stringify(workspace, null, 2));
|
||||
}
|
||||
|
||||
it('should replace existing prebuilt theme files', () => {
|
||||
const existingThemePath =
|
||||
'./node_modules/@angular/material/prebuilt-themes/purple-green.css';
|
||||
writeStyleFileToWorkspace(appTree, existingThemePath);
|
||||
|
||||
const tree = runner.runSchematic('ng-add-setup-project', {}, appTree);
|
||||
const workspace = getWorkspace(tree);
|
||||
const project = getProjectFromWorkspace(workspace);
|
||||
const styles = getProjectTargetOptions(project, 'build').styles;
|
||||
|
||||
expect(styles).not.toContain(existingThemePath,
|
||||
'Expected the existing prebuilt theme file to be removed.');
|
||||
expect(styles).toContain(defaultPrebuiltThemePath,
|
||||
'Expected the default prebuilt theme to be added.');
|
||||
});
|
||||
|
||||
it('should not replace existing custom theme files', () => {
|
||||
spyOn(console, 'warn');
|
||||
writeStyleFileToWorkspace(appTree, './projects/material/custom-theme.scss');
|
||||
|
||||
const tree = runner.runSchematic('ng-add-setup-project', {}, appTree);
|
||||
const workspace = getWorkspace(tree);
|
||||
const project = getProjectFromWorkspace(workspace);
|
||||
const styles = getProjectTargetOptions(project, 'build').styles;
|
||||
|
||||
expect(styles).not.toContain(defaultPrebuiltThemePath,
|
||||
'Expected the default prebuilt theme to be not configured.');
|
||||
expect(console.warn).toHaveBeenCalledWith(
|
||||
jasmine.stringMatching(/Cannot add.*already a custom theme/));
|
||||
});
|
||||
|
||||
it('should not add a theme file multiple times', () => {
|
||||
writeStyleFileToWorkspace(appTree, defaultPrebuiltThemePath);
|
||||
|
||||
const tree = runner.runSchematic('ng-add-setup-project', {}, appTree);
|
||||
const workspace = getWorkspace(tree);
|
||||
const project = getProjectFromWorkspace(workspace);
|
||||
const styles = getProjectTargetOptions(project, 'build').styles;
|
||||
|
||||
expect(styles).toEqual(['projects/material/src/styles.css', defaultPrebuiltThemePath],
|
||||
'Expected the "styles.css" file and default prebuilt theme to be the only styles');
|
||||
});
|
||||
|
||||
it('should not overwrite existing custom theme files', () => {
|
||||
appTree.create('/projects/material/custom-theme.scss', 'custom-theme');
|
||||
const tree = runner.runSchematic('ng-add-setup-project', {theme: 'custom'}, appTree);
|
||||
|
||||
expect(tree.readContent('/projects/material/custom-theme.scss')).toBe('custom-theme',
|
||||
'Expected the old custom theme content to be unchanged.');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,7 +19,13 @@ import {getWorkspace} from '@schematics/angular/utility/config';
|
||||
import {join} from 'path';
|
||||
import {Schema} from '../schema';
|
||||
import {createCustomTheme} from './custom-theme';
|
||||
import {red, bold} from 'chalk';
|
||||
|
||||
/** Path segment that can be found in paths that refer to a prebuilt theme. */
|
||||
const prebuiltThemePathSegment = '@angular/material/prebuilt-themes';
|
||||
|
||||
/** Default file name of the custom theme that can be generated. */
|
||||
const defaultCustomThemeFilename = 'custom-theme.scss';
|
||||
|
||||
/** Add pre-built styles to the main project style file. */
|
||||
export function addThemeToAppStyles(options: Schema): (host: Tree) => Tree {
|
||||
@@ -60,11 +66,17 @@ function insertCustomTheme(project: WorkspaceProject, projectName: string, host:
|
||||
|
||||
// Normalize the path through the devkit utilities because we want to avoid having
|
||||
// unnecessary path segments and windows backslash delimiters.
|
||||
const customThemePath = normalize(join(project.sourceRoot, 'custom-theme.scss'));
|
||||
const customThemePath = normalize(join(project.sourceRoot, defaultCustomThemeFilename));
|
||||
|
||||
if (host.exists(customThemePath)) {
|
||||
console.warn(red(`Cannot create a custom Angular Material theme because
|
||||
"${customThemePath}" already exists. Skipping custom theme generation.`));
|
||||
return;
|
||||
}
|
||||
|
||||
host.create(customThemePath, themeContent);
|
||||
|
||||
return addStyleToTarget(project, 'build', host, customThemePath, workspace);
|
||||
addThemeStyleToTarget(project, 'build', host, customThemePath, workspace);
|
||||
return;
|
||||
}
|
||||
|
||||
const insertion = new InsertChange(stylesPath, 0, themeContent);
|
||||
@@ -81,25 +93,42 @@ function insertPrebuiltTheme(project: WorkspaceProject, host: Tree, theme: strin
|
||||
// Path needs to be always relative to the `package.json` or workspace root.
|
||||
const themePath = `./node_modules/@angular/material/prebuilt-themes/${theme}.css`;
|
||||
|
||||
addStyleToTarget(project, 'build', host, themePath, workspace);
|
||||
addStyleToTarget(project, 'test', host, themePath, workspace);
|
||||
addThemeStyleToTarget(project, 'build', host, themePath, workspace);
|
||||
addThemeStyleToTarget(project, 'test', host, themePath, workspace);
|
||||
}
|
||||
|
||||
/** Adds a style entry to the given project target. */
|
||||
function addStyleToTarget(project: WorkspaceProject, targetName: string, host: Tree,
|
||||
/** Adds a theming style entry to the given project target options. */
|
||||
function addThemeStyleToTarget(project: WorkspaceProject, targetName: string, host: Tree,
|
||||
assetPath: string, workspace: WorkspaceSchema) {
|
||||
|
||||
const targetOptions = getProjectTargetOptions(project, targetName);
|
||||
|
||||
if (!targetOptions.styles) {
|
||||
targetOptions.styles = [assetPath];
|
||||
} else {
|
||||
const existingStyles = targetOptions.styles.map(s => typeof s === 'string' ? s : s.input);
|
||||
const hasGivenTheme = existingStyles.find(s => s.includes(assetPath));
|
||||
const hasOtherTheme = existingStyles.find(s => s.includes('material/prebuilt'));
|
||||
|
||||
if (!hasGivenTheme && !hasOtherTheme) {
|
||||
targetOptions.styles.unshift(assetPath);
|
||||
for (let [index, stylePath] of existingStyles.entries()) {
|
||||
// If the given asset is already specified in the styles, we don't need to do anything.
|
||||
if (stylePath === assetPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
// In case a prebuilt theme is already set up, we can safely replace the theme with the new
|
||||
// theme file. If a custom theme is set up, we are not able to safely replace the custom
|
||||
// theme because these files can contain custom styles, while prebuilt themes are
|
||||
// always packaged and considered replaceable.
|
||||
if (stylePath.includes(defaultCustomThemeFilename)) {
|
||||
console.warn(red(`Cannot add "${bold(assetPath)} to the CLI project configuration ` +
|
||||
`because there is already a custom theme file referenced. Please manually add ` +
|
||||
`the "${bold(assetPath)}" style file to your configuration.`));
|
||||
return;
|
||||
} else if (stylePath.includes(prebuiltThemePathSegment)) {
|
||||
targetOptions.styles.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
targetOptions.styles.unshift(assetPath);
|
||||
}
|
||||
|
||||
host.overwrite('angular.json', JSON.stringify(workspace, null, 2));
|
||||
|
||||
Reference in New Issue
Block a user