Skip to content

Commit b6dfa8d

Browse files
vsavkinBrocco
authored andcommitted
feat(@angular/cli): make appRoot customizable
1 parent 60509ae commit b6dfa8d

File tree

6 files changed

+75
-8
lines changed

6 files changed

+75
-8
lines changed

packages/@angular/cli/commands/generate.ts

+10-7
Original file line numberDiff line numberDiff line change
@@ -137,15 +137,18 @@ export default Command.extend({
137137
dryRun: commandOptions.dryRun
138138
};
139139
const parsedPath = dynamicPathParser(dynamicPathOptions);
140-
const root = appConfig.root + path.sep;
141140
commandOptions.sourceDir = appConfig.root;
142-
commandOptions.appRoot = parsedPath.appRoot.startsWith(root)
143-
? parsedPath.appRoot.substr(root.length)
144-
: parsedPath.appRoot;
141+
const root = appConfig.root + path.sep;
142+
commandOptions.appRoot = parsedPath.appRoot === appConfig.root ? '' :
143+
parsedPath.appRoot.startsWith(root)
144+
? parsedPath.appRoot.substr(root.length)
145+
: parsedPath.appRoot;
146+
145147
commandOptions.path = parsedPath.dir.replace(separatorRegEx, '/');
146-
if (parsedPath.dir.startsWith(root)) {
147-
commandOptions.path = commandOptions.path.substr(root.length);
148-
}
148+
commandOptions.path = parsedPath.dir === appConfig.root ? '' :
149+
parsedPath.dir.startsWith(root)
150+
? commandOptions.path.substr(root.length)
151+
: commandOptions.path;
149152

150153
const cwd = this.project.root;
151154
const schematicName = rawArgs[0];

packages/@angular/cli/lib/config/schema.json

+5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@
3333
"type": "string",
3434
"description": "Name of the app."
3535
},
36+
"appRoot": {
37+
"type": "string",
38+
"description": "Directory where app files are placed.",
39+
"default": "app"
40+
},
3641
"root": {
3742
"type": "string",
3843
"description": "The root directory of the app."

packages/@angular/cli/utilities/dynamic-path-parser.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ export interface DynamicPathOptions {
1313
export function dynamicPathParser(options: DynamicPathOptions) {
1414
const projectRoot = options.project.root;
1515
const sourceDir = options.appConfig.root;
16-
const appRoot = path.join(sourceDir, 'app');
16+
17+
const p = options.appConfig.appRoot === undefined ? 'app' : options.appConfig.appRoot;
18+
const appRoot = path.join(sourceDir, p);
1719
const cwd = process.env.PWD;
1820

1921
const rootPath = path.join(projectRoot, appRoot);

tests/acceptance/dynamic-path-parser.spec.ts

+26
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,32 @@ describe('dynamic path parser', () => {
5050
expect(result.name).toBe(entityName);
5151
});
5252

53+
it('respects the appRoot configuration', () => {
54+
process.env.PWD = project.root;
55+
const options = {
56+
project,
57+
entityName,
58+
appConfig: {...appConfig, appRoot: 'other'},
59+
dryRun: false
60+
};
61+
const result = dynamicPathParser(options);
62+
expect(result.dir).toBe(`src${path.sep}other`);
63+
expect(result.name).toBe(entityName);
64+
});
65+
66+
it('respects the empty appRoot configuration', () => {
67+
process.env.PWD = project.root;
68+
const options = {
69+
project,
70+
entityName,
71+
appConfig: <any>{...appConfig, appRoot: ''},
72+
dryRun: false
73+
};
74+
const result = dynamicPathParser(options);
75+
expect(result.dir).toBe(`src`);
76+
expect(result.name).toBe(entityName);
77+
});
78+
5379
it('parse from proj src dir', () => {
5480
process.env.PWD = path.join(project.root, 'src');
5581
const options = {

tests/acceptance/generate-component.spec.ts

+17
Original file line numberDiff line numberDiff line change
@@ -345,5 +345,22 @@ describe('Acceptance: ng generate component', () => {
345345
})
346346
.then(done, done.fail);
347347
});
348+
349+
describe('should generate components in apps with empty appRoot', () => {
350+
it('should work', (done) => {
351+
const appRoot = path.join(root, 'tmp/foo');
352+
mkdirsSync(path.join(appRoot, 'other', 'src'));
353+
354+
return ng(['generate', 'module', 'm', '--app', 'other']).then(() => {
355+
const expectedModule = path.join(appRoot, 'other', 'src', 'm', 'm.module.ts');
356+
expect(pathExistsSync(expectedModule)).toBe(true);
357+
358+
return ng(['generate', 'component', 'm/c', '--app', 'other', '--module', 'm']).then(() => {
359+
expect(pathExistsSync(path.join(appRoot, 'other', 'src', 'm', 'c', 'c.component.ts'))).toBe(true);
360+
expect(readFileSync(expectedModule, 'utf-8')).toContain(`import { CComponent } from './c/c.component'`);
361+
});
362+
}).then(done, done.fail);
363+
});
364+
});
348365
});
349366
});

tests/helpers/index.ts

+14
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import * as path from 'path';
2+
import {writeFile, readFile} from 'fs-extra';
13
import { ng } from './ng';
24
import { setup, teardown } from './tmp';
35

@@ -10,10 +12,22 @@ export function setupProject() {
1012
setup('./tmp')
1113
.then(() => process.chdir('./tmp'))
1214
.then(() => ng(['new', 'foo', '--skip-install']))
15+
.then(() => addAppToProject())
1316
.then(done, done.fail);
1417
}, 10000);
1518

1619
afterEach((done) => {
1720
teardown('./tmp').then(done, done.fail);
1821
});
1922
}
23+
24+
function addAppToProject(): Promise<any> {
25+
const cliJson = path.join(path.join(process.cwd()), '.angular-cli.json');
26+
return readFile(cliJson, 'utf-8').then(content => {
27+
const json = JSON.parse(content);
28+
json.apps.push(({name: 'other', root: 'other/src', appRoot: ''}));
29+
return json;
30+
}).then(json => {
31+
return writeFile(cliJson, JSON.stringify(json, null, 2))
32+
});
33+
}

0 commit comments

Comments
 (0)