Skip to content

Commit 63d5b43

Browse files
Broccohansl
authored andcommitted
fix(@schematics/angular): Fix usage of sourceRoot configuration value
fixes #10653
1 parent 7548255 commit 63d5b43

File tree

20 files changed

+176
-19
lines changed

20 files changed

+176
-19
lines changed

packages/schematics/angular/class/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
} from '@angular-devkit/schematics';
2323
import { getWorkspace } from '../utility/config';
2424
import { parseName } from '../utility/parse-name';
25+
import { buildDefaultPath } from '../utility/project';
2526
import { Schema as ClassOptions } from './schema';
2627

2728
export default function (options: ClassOptions): Rule {
@@ -33,8 +34,7 @@ export default function (options: ClassOptions): Rule {
3334
const project = workspace.projects[options.project];
3435

3536
if (options.path === undefined) {
36-
const projectDirName = project.projectType === 'application' ? 'app' : 'lib';
37-
options.path = `/${project.root}/src/${projectDirName}`;
37+
options.path = buildDefaultPath(project);
3838
}
3939

4040
options.type = !!options.type ? `.${options.type}` : '';

packages/schematics/angular/class/index_spec.ts

+8
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,12 @@ describe('Class Schematic', () => {
8888
const tree = schematicRunner.runSchematic('class', options, appTree);
8989
expect(tree.files.indexOf('/zzz/foo.ts')).toBeGreaterThanOrEqual(0);
9090
});
91+
92+
it('should respect the sourceRoot value', () => {
93+
const config = JSON.parse(appTree.readContent('/angular.json'));
94+
config.projects.bar.sourceRoot = 'projects/bar/custom';
95+
appTree.overwrite('/angular.json', JSON.stringify(config, null, 2));
96+
appTree = schematicRunner.runSchematic('class', defaultOptions, appTree);
97+
expect(appTree.files.indexOf('/projects/bar/custom/app/foo.ts')).toBeGreaterThanOrEqual(0);
98+
});
9199
});

packages/schematics/angular/component/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { InsertChange } from '../utility/change';
3131
import { getWorkspace } from '../utility/config';
3232
import { buildRelativePath, findModuleFromOptions } from '../utility/find-module';
3333
import { parseName } from '../utility/parse-name';
34+
import { buildDefaultPath } from '../utility/project';
3435
import { validateHtmlSelector, validateName } from '../utility/validation';
3536
import { Schema as ComponentOptions } from './schema';
3637

@@ -134,8 +135,7 @@ export default function(options: ComponentOptions): Rule {
134135
const project = workspace.projects[options.project];
135136

136137
if (options.path === undefined) {
137-
const projectDirName = project.projectType === 'application' ? 'app' : 'lib';
138-
options.path = `/${project.root}/src/${projectDirName}`;
138+
options.path = buildDefaultPath(project);
139139
}
140140

141141
options.module = findModuleFromOptions(host, options);

packages/schematics/angular/component/index_spec.ts

+15
Original file line numberDiff line numberDiff line change
@@ -287,4 +287,19 @@ describe('Component Schematic', () => {
287287
const content = appTree.readContent('/projects/bar/src/app/sub/test/test.component.ts');
288288
expect(content).toMatch(/selector: 'app-test'/);
289289
});
290+
291+
it('should respect the sourceRoot value', () => {
292+
const config = JSON.parse(appTree.readContent('/angular.json'));
293+
config.projects.bar.sourceRoot = 'projects/bar/custom';
294+
appTree.overwrite('/angular.json', JSON.stringify(config, null, 2));
295+
296+
// should fail without a module in that dir
297+
expect(() => schematicRunner.runSchematic('component', defaultOptions, appTree)).toThrow();
298+
299+
// move the module
300+
appTree.rename('/projects/bar/src/app/app.module.ts', '/projects/bar/custom/app/app.module.ts');
301+
appTree = schematicRunner.runSchematic('component', defaultOptions, appTree);
302+
expect(appTree.files.indexOf('/projects/bar/custom/app/foo/foo.component.ts'))
303+
.toBeGreaterThanOrEqual(0);
304+
});
290305
});

packages/schematics/angular/directive/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { InsertChange } from '../utility/change';
2727
import { getWorkspace } from '../utility/config';
2828
import { buildRelativePath, findModuleFromOptions } from '../utility/find-module';
2929
import { parseName } from '../utility/parse-name';
30+
import { buildDefaultPath } from '../utility/project';
3031
import { validateHtmlSelector } from '../utility/validation';
3132
import { Schema as DirectiveOptions } from './schema';
3233

@@ -110,8 +111,7 @@ export default function (options: DirectiveOptions): Rule {
110111
const project = workspace.projects[options.project];
111112

112113
if (options.path === undefined) {
113-
const projectDirName = project.projectType === 'application' ? 'app' : 'lib';
114-
options.path = `/${project.root}/src/${projectDirName}`;
114+
options.path = buildDefaultPath(project);
115115
}
116116

117117
options.module = findModuleFromOptions(host, options);

packages/schematics/angular/directive/index_spec.ts

+15
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,19 @@ describe('Directive Schematic', () => {
154154
const content = tree.readContent('/projects/bar/src/app/foo.directive.ts');
155155
expect(content).toMatch(/selector: '\[foo\]'/);
156156
});
157+
158+
it('should respect the sourceRoot value', () => {
159+
const config = JSON.parse(appTree.readContent('/angular.json'));
160+
config.projects.bar.sourceRoot = 'projects/bar/custom';
161+
appTree.overwrite('/angular.json', JSON.stringify(config, null, 2));
162+
163+
// should fail without a module in that dir
164+
expect(() => schematicRunner.runSchematic('directive', defaultOptions, appTree)).toThrow();
165+
166+
// move the module
167+
appTree.rename('/projects/bar/src/app/app.module.ts', '/projects/bar/custom/app/app.module.ts');
168+
appTree = schematicRunner.runSchematic('directive', defaultOptions, appTree);
169+
expect(appTree.files.indexOf('/projects/bar/custom/app/foo.directive.ts'))
170+
.toBeGreaterThanOrEqual(0);
171+
});
157172
});

packages/schematics/angular/enum/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
} from '@angular-devkit/schematics';
2222
import { getWorkspace } from '../utility/config';
2323
import { parseName } from '../utility/parse-name';
24+
import { buildDefaultPath } from '../utility/project';
2425
import { Schema as EnumOptions } from './schema';
2526

2627

@@ -33,8 +34,7 @@ export default function (options: EnumOptions): Rule {
3334
const project = workspace.projects[options.project];
3435

3536
if (options.path === undefined) {
36-
const projectDirName = project.projectType === 'application' ? 'app' : 'lib';
37-
options.path = `/${project.root}/src/${projectDirName}`;
37+
options.path = buildDefaultPath(project);
3838
}
3939

4040
const parsedPath = parseName(options.path, options.name);

packages/schematics/angular/enum/index_spec.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,11 @@ describe('Enum Schematic', () => {
5454
expect(content).toMatch('export enum Foo {');
5555
});
5656

57-
57+
it('should respect the sourceRoot value', () => {
58+
const config = JSON.parse(appTree.readContent('/angular.json'));
59+
config.projects.bar.sourceRoot = 'projects/bar/custom';
60+
appTree.overwrite('/angular.json', JSON.stringify(config, null, 2));
61+
appTree = schematicRunner.runSchematic('enum', defaultOptions, appTree);
62+
expect(appTree.files.indexOf('/projects/bar/custom/app/foo.enum.ts')).toBeGreaterThanOrEqual(0);
63+
});
5864
});

packages/schematics/angular/guard/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
} from '@angular-devkit/schematics';
2424
import { getWorkspace } from '../utility/config';
2525
import { parseName } from '../utility/parse-name';
26+
import { buildDefaultPath } from '../utility/project';
2627
import { Schema as GuardOptions } from './schema';
2728

2829

@@ -35,8 +36,7 @@ export default function (options: GuardOptions): Rule {
3536
const project = workspace.projects[options.project];
3637

3738
if (options.path === undefined) {
38-
const projectDirName = project.projectType === 'application' ? 'app' : 'lib';
39-
options.path = `/${project.root}/src/${projectDirName}`;
39+
options.path = buildDefaultPath(project);
4040
}
4141

4242
const parsedPath = parseName(options.path, options.name);

packages/schematics/angular/guard/index_spec.ts

+9
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,13 @@ describe('Guard Schematic', () => {
5959
expect(files.indexOf('/projects/bar/src/app/foo.guard.spec.ts')).toEqual(-1);
6060
expect(files.indexOf('/projects/bar/src/app/foo.guard.ts')).toBeGreaterThanOrEqual(0);
6161
});
62+
63+
it('should respect the sourceRoot value', () => {
64+
const config = JSON.parse(appTree.readContent('/angular.json'));
65+
config.projects.bar.sourceRoot = 'projects/bar/custom';
66+
appTree.overwrite('/angular.json', JSON.stringify(config, null, 2));
67+
appTree = schematicRunner.runSchematic('guard', defaultOptions, appTree);
68+
expect(appTree.files.indexOf('/projects/bar/custom/app/foo.guard.ts'))
69+
.toBeGreaterThanOrEqual(0);
70+
});
6271
});

packages/schematics/angular/interface/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
} from '@angular-devkit/schematics';
2222
import { getWorkspace } from '../utility/config';
2323
import { parseName } from '../utility/parse-name';
24+
import { buildDefaultPath } from '../utility/project';
2425
import { Schema as InterfaceOptions } from './schema';
2526

2627

@@ -33,8 +34,7 @@ export default function (options: InterfaceOptions): Rule {
3334
const project = workspace.projects[options.project];
3435

3536
if (options.path === undefined) {
36-
const projectDirName = project.projectType === 'application' ? 'app' : 'lib';
37-
options.path = `/${project.root}/src/${projectDirName}`;
37+
options.path = buildDefaultPath(project);
3838
}
3939

4040
const parsedPath = parseName(options.path, options.name);

packages/schematics/angular/interface/index_spec.ts

+8
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,12 @@ describe('Interface Schematic', () => {
6363
expect(tree.files.indexOf('/projects/bar/src/app/foo.model.ts')).toBeGreaterThanOrEqual(0);
6464
});
6565

66+
it('should respect the sourceRoot value', () => {
67+
const config = JSON.parse(appTree.readContent('/angular.json'));
68+
config.projects.bar.sourceRoot = 'projects/bar/custom';
69+
appTree.overwrite('/angular.json', JSON.stringify(config, null, 2));
70+
appTree = schematicRunner.runSchematic('interface', defaultOptions, appTree);
71+
expect(appTree.files.indexOf('/projects/bar/custom/app/foo.ts'))
72+
.toBeGreaterThanOrEqual(0);
73+
});
6674
});

packages/schematics/angular/module/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { InsertChange } from '../utility/change';
2727
import { getWorkspace } from '../utility/config';
2828
import { findModuleFromOptions } from '../utility/find-module';
2929
import { parseName } from '../utility/parse-name';
30+
import { buildDefaultPath } from '../utility/project';
3031
import { Schema as ModuleOptions } from './schema';
3132

3233

@@ -79,8 +80,7 @@ export default function (options: ModuleOptions): Rule {
7980
const project = workspace.projects[options.project];
8081

8182
if (options.path === undefined) {
82-
const projectDirName = project.projectType === 'application' ? 'app' : 'lib';
83-
options.path = `/${project.root}/src/${projectDirName}`;
83+
options.path = buildDefaultPath(project);
8484
}
8585
if (options.module) {
8686
options.module = findModuleFromOptions(host, options);

packages/schematics/angular/module/index_spec.ts

+9
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,13 @@ describe('Module Schematic', () => {
115115
expect(files.indexOf('/projects/bar/src/app/two-word/two-word.module.spec.ts'))
116116
.toBeGreaterThanOrEqual(0);
117117
});
118+
119+
it('should respect the sourceRoot value', () => {
120+
const config = JSON.parse(appTree.readContent('/angular.json'));
121+
config.projects.bar.sourceRoot = 'projects/bar/custom';
122+
appTree.overwrite('/angular.json', JSON.stringify(config, null, 2));
123+
appTree = schematicRunner.runSchematic('module', defaultOptions, appTree);
124+
expect(appTree.files.indexOf('/projects/bar/custom/app/foo/foo.module.ts'))
125+
.toBeGreaterThanOrEqual(0);
126+
});
118127
});

packages/schematics/angular/pipe/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { InsertChange } from '../utility/change';
2727
import { getWorkspace } from '../utility/config';
2828
import { buildRelativePath, findModuleFromOptions } from '../utility/find-module';
2929
import { parseName } from '../utility/parse-name';
30+
import { buildDefaultPath } from '../utility/project';
3031
import { Schema as PipeOptions } from './schema';
3132

3233

@@ -94,8 +95,7 @@ export default function (options: PipeOptions): Rule {
9495
const project = workspace.projects[options.project];
9596

9697
if (options.path === undefined) {
97-
const projectDirName = project.projectType === 'application' ? 'app' : 'lib';
98-
options.path = `/${project.root}/src/${projectDirName}`;
98+
options.path = buildDefaultPath(project);
9999
}
100100

101101
const parsedPath = parseName(options.path, options.name);

packages/schematics/angular/pipe/index_spec.ts

+15
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,19 @@ describe('Pipe Schematic', () => {
109109
const content = getFileContent(tree, routingModulePath);
110110
expect(content).toMatch(/import { FooPipe } from '.\/foo.pipe/);
111111
});
112+
113+
it('should respect the sourceRoot value', () => {
114+
const config = JSON.parse(appTree.readContent('/angular.json'));
115+
config.projects.bar.sourceRoot = 'projects/bar/custom';
116+
appTree.overwrite('/angular.json', JSON.stringify(config, null, 2));
117+
118+
// should fail without a module in that dir
119+
expect(() => schematicRunner.runSchematic('pipe', defaultOptions, appTree)).toThrow();
120+
121+
// move the module
122+
appTree.rename('/projects/bar/src/app/app.module.ts', '/projects/bar/custom/app/app.module.ts');
123+
appTree = schematicRunner.runSchematic('pipe', defaultOptions, appTree);
124+
expect(appTree.files.indexOf('/projects/bar/custom/app/foo.pipe.ts'))
125+
.toBeGreaterThanOrEqual(0);
126+
});
112127
});

packages/schematics/angular/service/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
} from '@angular-devkit/schematics';
2222
import { getWorkspace } from '../utility/config';
2323
import { parseName } from '../utility/parse-name';
24+
import { buildDefaultPath } from '../utility/project';
2425
import { Schema as ServiceOptions } from './schema';
2526

2627
export default function (options: ServiceOptions): Rule {
@@ -32,8 +33,7 @@ export default function (options: ServiceOptions): Rule {
3233
const project = workspace.projects[options.project];
3334

3435
if (options.path === undefined) {
35-
const projectDirName = project.projectType === 'application' ? 'app' : 'lib';
36-
options.path = `/${project.root}/src/${projectDirName}`;
36+
options.path = buildDefaultPath(project);
3737
}
3838

3939
const parsedPath = parseName(options.path, options.name);

packages/schematics/angular/service/index_spec.ts

+9
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,13 @@ describe('Service Schematic', () => {
7070
expect(files.indexOf('/projects/bar/src/app/foo/foo.service.ts')).toBeGreaterThanOrEqual(0);
7171
expect(files.indexOf('/projects/bar/src/app/foo/foo.service.spec.ts')).toEqual(-1);
7272
});
73+
74+
it('should respect the sourceRoot value', () => {
75+
const config = JSON.parse(appTree.readContent('/angular.json'));
76+
config.projects.bar.sourceRoot = 'projects/bar/custom';
77+
appTree.overwrite('/angular.json', JSON.stringify(config, null, 2));
78+
appTree = schematicRunner.runSchematic('service', defaultOptions, appTree);
79+
expect(appTree.files.indexOf('/projects/bar/custom/app/foo/foo.service.ts'))
80+
.toBeGreaterThanOrEqual(0);
81+
});
7382
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. 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+
import { WorkspaceProject } from '../utility/config';
9+
10+
11+
/**
12+
* Build a default project path for generating.
13+
* @param project The project to build the path for.
14+
*/
15+
export function buildDefaultPath(project: WorkspaceProject): string {
16+
const root = project.sourceRoot
17+
? `/${project.sourceRoot}/`
18+
: `/${project.root}/src/`;
19+
20+
const projectDirName = project.projectType === 'application' ? 'app' : 'lib';
21+
22+
return `${root}${projectDirName}`;
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. 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+
import { WorkspaceProject } from '../utility/config';
9+
import { buildDefaultPath } from './project';
10+
11+
12+
describe('project', () => {
13+
describe('buildDefaultPath', () => {
14+
let project: WorkspaceProject;
15+
beforeEach(() => {
16+
project = {
17+
projectType: 'application',
18+
root: 'foo',
19+
prefix: 'app',
20+
};
21+
});
22+
23+
it('should handle projectType of application', () => {
24+
const result = buildDefaultPath(project);
25+
expect(result).toEqual('/foo/src/app');
26+
});
27+
28+
it('should handle projectType of library', () => {
29+
project = { ...project, projectType: 'library' };
30+
const result = buildDefaultPath(project);
31+
expect(result).toEqual('/foo/src/lib');
32+
});
33+
34+
it('should handle sourceRoot', () => {
35+
project = { ...project, sourceRoot: 'foo/bar/custom' };
36+
const result = buildDefaultPath(project);
37+
expect(result).toEqual('/foo/bar/custom/app');
38+
});
39+
});
40+
});

0 commit comments

Comments
 (0)