Skip to content

Commit

Permalink
fix(cdk/schematics): support both application and browser builders (#…
Browse files Browse the repository at this point in the history
…27875)

In #27792 the schematics were updated to use the new default `application` builder instead of the `browser` builder. According to the CLI team we'll have to support both so these changes update our existing logic to account for both cases.
  • Loading branch information
crisbeto authored Oct 4, 2023
1 parent 2f958ac commit c5ab880
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 17 deletions.
4 changes: 2 additions & 2 deletions src/cdk/schematics/utils/project-index-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
*/

import {Path, workspaces} from '@angular-devkit/core';
import {defaultTargetBuilders, getTargetsByBuilderName} from './project-targets';
import {getProjectBuildTargets} from './project-targets';

/** Gets the path of the index file in the given project. */
export function getProjectIndexFiles(project: workspaces.ProjectDefinition): Path[] {
const paths = getTargetsByBuilderName(project, defaultTargetBuilders.build)
const paths = getProjectBuildTargets(project)
.filter(t => t.options?.['index'])
.map(t => t.options!['index'] as Path);

Expand Down
5 changes: 4 additions & 1 deletion src/cdk/schematics/utils/project-main-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import {getProjectTargetOptions} from './project-targets';
/** Looks for the main TypeScript file in the given project and returns its path. */
export function getProjectMainFile(project: workspaces.ProjectDefinition): Path {
const buildOptions = getProjectTargetOptions(project, 'build');
const mainPath = buildOptions['browser'] as Path | undefined;

// `browser` is for the `@angular-devkit/build-angular:application` builder while
// `main` is for the `@angular-devkit/build-angular:browser` builder.
const mainPath = (buildOptions['browser'] || buildOptions['main']) as Path | undefined;

if (!mainPath) {
throw new SchematicsException(
Expand Down
36 changes: 26 additions & 10 deletions src/cdk/schematics/utils/project-targets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,6 @@
import {JsonValue, workspaces} from '@angular-devkit/core';
import {SchematicsException} from '@angular-devkit/schematics';

/** Object that maps a CLI target to its default builder name. */
export const defaultTargetBuilders = {
build: '@angular-devkit/build-angular:application',
test: '@angular-devkit/build-angular:karma',
};

/** Resolves the architect options for the build target of the given project. */
export function getProjectTargetOptions(
project: workspaces.ProjectDefinition,
Expand All @@ -31,12 +25,34 @@ export function getProjectTargetOptions(
return options;
}

/** Gets all targets from the given project that match the specified builder name. */
export function getTargetsByBuilderName(
/** Gets all of the default CLI-provided build targets in a project. */
export function getProjectBuildTargets(
project: workspaces.ProjectDefinition,
): workspaces.TargetDefinition[] {
return getTargetsByBuilderName(
project,
builder =>
builder === '@angular-devkit/build-angular:application' ||
builder === '@angular-devkit/build-angular:browser',
);
}

/** Gets all of the default CLI-provided testing targets in a project. */
export function getProjectTestTargets(
project: workspaces.ProjectDefinition,
): workspaces.TargetDefinition[] {
return getTargetsByBuilderName(
project,
builder => builder === '@angular-devkit/build-angular:karma',
);
}

/** Gets all targets from the given project that pass a predicate check. */
function getTargetsByBuilderName(
project: workspaces.ProjectDefinition,
builderName: string,
predicate: (name: string | undefined) => boolean,
): workspaces.TargetDefinition[] {
return Array.from(project.targets.keys())
.filter(name => project.targets.get(name)?.builder === builderName)
.filter(name => predicate(project.targets.get(name)?.builder))
.map(name => project.targets.get(name)!);
}
69 changes: 69 additions & 0 deletions src/material/schematics/ng-add/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,75 @@ describe('ng-add schematic', () => {
expect(buffer.toString()).toContain('<body class="one two">');
});
});

describe('using browser builder', () => {
beforeEach(() => {
const config = {
version: 1,
projects: {
material: {
projectType: 'application',
root: 'projects/material',
sourceRoot: 'projects/material/src',
prefix: 'app',
architect: {
build: {
builder: '@angular-devkit/build-angular:browser',
options: {
outputPath: 'dist/material',
index: 'projects/material/src/index.html',
main: 'projects/material/src/main.ts',
styles: ['projects/material/src/styles.css'],
},
},
test: {
builder: '@angular-devkit/build-angular:karma',
options: {
outputPath: 'dist/material',
index: 'projects/material/src/index.html',
browser: 'projects/material/src/main.ts',
styles: ['projects/material/src/styles.css'],
},
},
},
},
},
};

appTree.overwrite('/angular.json', JSON.stringify(config, null, 2));
});

it('should add a theme', async () => {
const tree = await runner.runSchematic('ng-add-setup-project', baseOptions, appTree);
const workspace = await getWorkspace(tree);
const project = getProjectFromWorkspace(workspace, baseOptions.project);

expectProjectStyleFile(project, '@angular/material/prebuilt-themes/indigo-pink.css');
});

it('should add material app styles', async () => {
const tree = await runner.runSchematic('ng-add-setup-project', baseOptions, appTree);
const workspace = await getWorkspace(tree);
const project = getProjectFromWorkspace(workspace, baseOptions.project);

const defaultStylesPath = getProjectStyleFile(project)!;
const htmlContent = tree.read(defaultStylesPath)!.toString();

expect(htmlContent).toContain('html, body { height: 100%; }');
expect(htmlContent).toContain(
'body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }',
);
});

it('should add the BrowserAnimationsModule to the project module', async () => {
const tree = await runner.runSchematic('ng-add-setup-project', baseOptions, appTree);
const fileContent = getFileContent(tree, '/projects/material/src/app/app.module.ts');

expect(fileContent)
.withContext('Expected the project app module to import the "BrowserAnimationsModule".')
.toContain('BrowserAnimationsModule');
});
});
});

describe('ng-add schematic - library project', () => {
Expand Down
9 changes: 5 additions & 4 deletions src/material/schematics/ng-add/theming/theming.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ import {
} from '@angular-devkit/schematics';
import {
addBodyClass,
defaultTargetBuilders,
getProjectFromWorkspace,
getProjectStyleFile,
getProjectTargetOptions,
getProjectIndexFiles,
getProjectTestTargets,
getProjectBuildTargets,
} from '@angular/cdk/schematics';
import {InsertChange} from '@schematics/angular/utility/change';
import {getWorkspace, updateWorkspace} from '@schematics/angular/utility/workspace';
Expand Down Expand Up @@ -177,9 +178,9 @@ function validateDefaultTargetBuilder(
targetName: 'build' | 'test',
logger: logging.LoggerApi,
) {
const defaultBuilder = defaultTargetBuilders[targetName];
const targetConfig = project.targets?.get(targetName);
const isDefaultBuilder = targetConfig?.['builder'] === defaultBuilder;
const targets =
targetName === 'test' ? getProjectTestTargets(project) : getProjectBuildTargets(project);
const isDefaultBuilder = targets.length > 0;

// Because the build setup for the Angular CLI can be customized by developers, we can't know
// where to put the theme file in the workspace configuration if custom builders are being
Expand Down

0 comments on commit c5ab880

Please sign in to comment.