Skip to content

Commit 8ea00d9

Browse files
author
trik
committed
fix(ng-add): ng add @angular/material fails in library projects
Skip app related ng-add steps when working with library projects and warn about missing setup steps
1 parent 668f791 commit 8ea00d9

File tree

6 files changed

+126
-47
lines changed

6 files changed

+126
-47
lines changed

src/cdk/schematics/testing/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@
99
export * from './post-scheduled-tasks';
1010
export * from './test-app';
1111
export * from './test-case-setup';
12+
export * from './test-library';
1213
export * from './file-content';
1314
export * from './resolve-bazel-path';

src/cdk/schematics/testing/test-app.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,10 @@
99
import {Tree} from '@angular-devkit/schematics';
1010
import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing';
1111

12+
import {createTestProject} from './test-project';
13+
1214
/** Create a base app used for testing. */
1315
export async function createTestApp(runner: SchematicTestRunner, appOptions = {}, tree?: Tree):
1416
Promise<UnitTestTree> {
15-
const workspaceTree = await runner.runExternalSchematicAsync('@schematics/angular', 'workspace', {
16-
name: 'workspace',
17-
version: '6.0.0',
18-
newProjectRoot: 'projects',
19-
}, tree).toPromise();
20-
21-
return runner.runExternalSchematicAsync('@schematics/angular', 'application',
22-
{name: 'material', ...appOptions}, workspaceTree).toPromise();
17+
return createTestProject(runner, 'application', appOptions, tree);
2318
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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 {Tree} from '@angular-devkit/schematics';
10+
import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing';
11+
12+
import {createTestProject} from './test-project';
13+
14+
/** Create a base library used for testing. */
15+
export async function createTestLibrary(runner: SchematicTestRunner, appOptions = {}, tree?: Tree):
16+
Promise<UnitTestTree> {
17+
return createTestProject(runner, 'library', appOptions, tree);
18+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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 {Tree} from '@angular-devkit/schematics';
10+
import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing';
11+
12+
/** Create a base project used for testing. */
13+
export async function createTestProject(
14+
runner: SchematicTestRunner, projectType: 'application'|'library', appOptions = {}, tree?: Tree):
15+
Promise<UnitTestTree> {
16+
const workspaceTree = await runner.runExternalSchematicAsync('@schematics/angular', 'workspace', {
17+
name: 'workspace',
18+
version: '6.0.0',
19+
newProjectRoot: 'projects',
20+
}, tree).toPromise();
21+
22+
return runner.runExternalSchematicAsync('@schematics/angular', projectType,
23+
{name: 'material', ...appOptions}, workspaceTree).toPromise();
24+
}

src/material/schematics/ng-add/index.spec.ts

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ import {Tree} from '@angular-devkit/schematics';
44
import {SchematicTestRunner} from '@angular-devkit/schematics/testing';
55
import {
66
addModuleImportToRootModule,
7-
getProjectFromWorkspace, getProjectIndexFiles,
7+
getProjectFromWorkspace,
8+
getProjectIndexFiles,
89
getProjectStyleFile,
910
getProjectTargetOptions,
1011
} from '@angular/cdk/schematics';
11-
import {createTestApp, getFileContent} from '@angular/cdk/schematics/testing';
12+
import {createTestApp, createTestLibrary, getFileContent} from '@angular/cdk/schematics/testing';
1213
import {getWorkspace} from '@schematics/angular/utility/config';
1314
import {COLLECTION_PATH} from '../index.spec';
1415
import {addPackageToPackageJson} from './package-config';
@@ -75,10 +76,10 @@ describe('ng-add schematic', () => {
7576
Object.keys(dependencies).sort(),
7677
'Expected the modified "dependencies" to be sorted alphabetically.');
7778

78-
expect(runner.tasks.some(task => task.name === 'node-package')).toBe(true,
79-
'Expected the package manager to be scheduled in order to update lock files.');
80-
expect(runner.tasks.some(task => task.name === 'run-schematic')).toBe(true,
81-
'Expected the setup-project schematic to be scheduled.');
79+
expect(runner.tasks.some(task => task.name === 'node-package'))
80+
.toBe(true, 'Expected the package manager to be scheduled in order to update lock files.');
81+
expect(runner.tasks.some(task => task.name === 'run-schematic'))
82+
.toBe(true, 'Expected the setup-project schematic to be scheduled.');
8283
});
8384

8485
it('should respect version range from CLI ng-add command', async () => {
@@ -152,10 +153,10 @@ describe('ng-add schematic', () => {
152153
// the created links properly align with the existing HTML. Default CLI projects use an
153154
// indentation of two columns.
154155
expect(htmlContent)
155-
.toContain(' <link href="https://fonts.googleapis.com/icon?family=Material+Icons"');
156+
.toContain(' <link href="https://fonts.googleapis.com/icon?family=Material+Icons"');
156157
expect(htmlContent)
157-
.toContain(
158-
' <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap"');
158+
.toContain(
159+
' <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap"');
159160
});
160161
});
161162

@@ -246,7 +247,7 @@ describe('ng-add schematic', () => {
246247
overwriteTargetBuilder(appTree, 'build', 'thirdparty-builder');
247248

248249
await expectAsync(runner.runSchematicAsync('ng-add-setup-project', {}, appTree).toPromise())
249-
.toBeRejectedWithError(/not using the default builders.*build/);
250+
.toBeRejectedWithError(/not using the default builders.*build/);
250251
});
251252

252253
it('should warn if the "test" target has been changed', async () => {
@@ -256,8 +257,8 @@ describe('ng-add schematic', () => {
256257

257258
expect(errorOutput.length).toBe(0);
258259
expect(warnOutput.length).toBe(1);
259-
expect(warnOutput[0]).toMatch(
260-
/not using the default builders.*cannot add the configured theme/);
260+
expect(warnOutput[0])
261+
.toMatch(/not using the default builders.*cannot add the configured theme/);
261262
});
262263
});
263264

@@ -335,9 +336,8 @@ describe('ng-add schematic', () => {
335336
});
336337

337338
it('should add the global typography class if the body has no classes', async () => {
338-
const tree = await runner.runSchematicAsync('ng-add-setup-project', {
339-
typography: true
340-
}, appTree).toPromise();
339+
const tree = await runner.runSchematicAsync('ng-add-setup-project', {typography: true}, appTree)
340+
.toPromise();
341341
const workspace = getWorkspace(tree);
342342
const project = getProjectFromWorkspace(workspace);
343343

@@ -358,9 +358,8 @@ describe('ng-add schematic', () => {
358358
</html>
359359
`);
360360

361-
const tree = await runner.runSchematicAsync('ng-add-setup-project', {
362-
typography: true
363-
}, appTree).toPromise();
361+
const tree = await runner.runSchematicAsync('ng-add-setup-project', {typography: true}, appTree)
362+
.toPromise();
364363

365364
const workspace = getWorkspace(tree);
366365
const project = getProjectFromWorkspace(workspace);
@@ -381,9 +380,8 @@ describe('ng-add schematic', () => {
381380
</html>
382381
`);
383382

384-
const tree = await runner.runSchematicAsync('ng-add-setup-project', {
385-
typography: true
386-
}, appTree).toPromise();
383+
const tree = await runner.runSchematicAsync('ng-add-setup-project', {typography: true}, appTree)
384+
.toPromise();
387385

388386
const workspace = getWorkspace(tree);
389387
const project = getProjectFromWorkspace(workspace);
@@ -404,9 +402,9 @@ describe('ng-add schematic', () => {
404402
</html>
405403
`);
406404

407-
const tree = await runner.runSchematicAsync('ng-add-setup-project', {
408-
typography: false
409-
}, appTree).toPromise();
405+
const tree =
406+
await runner.runSchematicAsync('ng-add-setup-project', {typography: false}, appTree)
407+
.toPromise();
410408

411409
const workspace = getWorkspace(tree);
412410
const project = getProjectFromWorkspace(workspace);
@@ -419,3 +417,33 @@ describe('ng-add schematic', () => {
419417
});
420418
});
421419
});
420+
421+
describe('ng-add schematic - library project', () => {
422+
let runner: SchematicTestRunner;
423+
let libraryTree: Tree;
424+
let errorOutput: string[];
425+
let warnOutput: string[];
426+
427+
beforeEach(async () => {
428+
runner = new SchematicTestRunner('schematics', require.resolve('../collection.json'));
429+
libraryTree = await createTestLibrary(runner);
430+
431+
errorOutput = [];
432+
warnOutput = [];
433+
runner.logger.subscribe(e => {
434+
if (e.level === 'error') {
435+
errorOutput.push(e.message);
436+
} else if (e.level === 'warn') {
437+
warnOutput.push(e.message);
438+
}
439+
});
440+
});
441+
442+
it('should warn if a library project is targeted', async () => {
443+
await runner.runSchematicAsync('ng-add-setup-project', {}, libraryTree).toPromise();
444+
445+
expect(errorOutput.length).toBe(0);
446+
expect(warnOutput.length).toBe(1);
447+
expect(warnOutput[0]).toMatch(/Your project is a library/);
448+
});
449+
});

src/material/schematics/ng-add/setup-project.ts

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,25 @@ const noopAnimationsModuleName = 'NoopAnimationsModule';
3333
* - Adds Browser Animation to app.module
3434
*/
3535
export default function(options: Schema): Rule {
36-
return chain([
37-
addAnimationsModule(options),
38-
addThemeToAppStyles(options),
39-
addFontsToIndex(options),
40-
addMaterialAppStyles(options),
41-
addTypographyClass(options),
42-
]);
36+
return (host: Tree, context: SchematicContext) => {
37+
const workspace = getWorkspace(host);
38+
const project = getProjectFromWorkspace(workspace, options.project);
39+
40+
if (project.projectType === 'application') {
41+
return chain([
42+
addAnimationsModule(options),
43+
addThemeToAppStyles(options),
44+
addFontsToIndex(options),
45+
addMaterialAppStyles(options),
46+
addTypographyClass(options),
47+
]);
48+
}
49+
context.logger.warn(
50+
'Unable to set up Angular Material in a library project as there is no special ' +
51+
'setup required. If you intended to run the schematic for a different project, ' +
52+
'pass the `--project` option.');
53+
return host;
54+
};
4355
}
4456

4557
/**
@@ -66,13 +78,13 @@ function addAnimationsModule(options: Schema) {
6678
return;
6779
}
6880

69-
addModuleImportToRootModule(host, browserAnimationsModuleName,
70-
'@angular/platform-browser/animations', project);
81+
addModuleImportToRootModule(
82+
host, browserAnimationsModuleName, '@angular/platform-browser/animations', project);
7183
} else if (!hasNgModuleImport(host, appModulePath, browserAnimationsModuleName)) {
7284
// Do not add the NoopAnimationsModule module if the project already explicitly uses
7385
// the BrowserAnimationsModule.
74-
addModuleImportToRootModule(host, noopAnimationsModuleName,
75-
'@angular/platform-browser/animations', project);
86+
addModuleImportToRootModule(
87+
host, noopAnimationsModuleName, '@angular/platform-browser/animations', project);
7688
}
7789

7890
return host;
@@ -99,16 +111,17 @@ function addMaterialAppStyles(options: Schema) {
99111
const buffer = host.read(styleFilePath);
100112

101113
if (!buffer) {
102-
logger.error(`Could not read the default style file within the project ` +
103-
`(${styleFilePath})`);
114+
logger.error(
115+
`Could not read the default style file within the project ` +
116+
`(${styleFilePath})`);
104117
logger.info(`Please consider manually setting up the Robot font.`);
105118
return;
106119
}
107120

108121
const htmlContent = buffer.toString();
109122
const insertion = '\n' +
110-
`html, body { height: 100%; }\n` +
111-
`body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }\n`;
123+
`html, body { height: 100%; }\n` +
124+
`body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }\n`;
112125

113126
if (htmlContent.includes(insertion)) {
114127
return;

0 commit comments

Comments
 (0)