Skip to content

Commit ce5db3c

Browse files
committed
refactor(@angular/cli): update generate command module file resolution
closes #5461
1 parent dbaa04f commit ce5db3c

File tree

11 files changed

+557
-95
lines changed

11 files changed

+557
-95
lines changed

packages/@angular/cli/blueprints/component/index.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
import * as chalk from 'chalk';
2+
import * as fs from 'fs';
3+
import * as path from 'path';
14
import { NodeHost } from '../../lib/ast-tools';
25
import { CliConfig } from '../../models/config';
36
import { getAppFromConfig } from '../../utilities/app-utils';
7+
import { resolveModulePath } from '../../utilities/resolve-module-file';
48

5-
import * as fs from 'fs';
6-
import * as path from 'path';
7-
import * as chalk from 'chalk';
89
const Blueprint = require('../../ember-cli/lib/models/blueprint');
910
const dynamicPathParser = require('../../utilities/dynamic-path-parser');
1011
const findParentModule = require('../../utilities/find-parent-module').default;
@@ -107,14 +108,8 @@ export default Blueprint.extend({
107108
beforeInstall: function (options: any) {
108109
const appConfig = getAppFromConfig(this.options.app);
109110
if (options.module) {
110-
// Resolve path to module
111-
const modulePath = options.module.endsWith('.ts') ? options.module : `${options.module}.ts`;
112-
const parsedPath = dynamicPathParser(this.project, modulePath, appConfig);
113-
this.pathToModule = path.join(this.project.root, parsedPath.dir, parsedPath.base);
114-
115-
if (!fs.existsSync(this.pathToModule)) {
116-
throw 'Module specified does not exist';
117-
}
111+
this.pathToModule =
112+
resolveModulePath(options.module, this.project, this.project.root, appConfig);
118113
} else {
119114
try {
120115
this.pathToModule = findParentModule(this.project.root, appConfig.root, this.generatePath);

packages/@angular/cli/blueprints/directive/index.ts

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import {NodeHost} from '../../lib/ast-tools';
2-
import {CliConfig} from '../../models/config';
3-
import {getAppFromConfig} from '../../utilities/app-utils';
1+
import * as chalk from 'chalk';
2+
import * as path from 'path';
3+
import { NodeHost } from '../../lib/ast-tools';
4+
import { CliConfig } from '../../models/config';
5+
import { getAppFromConfig } from '../../utilities/app-utils';
6+
import { resolveModulePath } from '../../utilities/resolve-module-file';
47

5-
const path = require('path');
6-
const fs = require('fs');
7-
const chalk = require('chalk');
88
const dynamicPathParser = require('../../utilities/dynamic-path-parser');
99
const stringUtils = require('ember-cli-string-utils');
1010
const astUtils = require('../../utilities/ast-utils');
@@ -61,14 +61,8 @@ export default Blueprint.extend({
6161
beforeInstall: function(options: any) {
6262
const appConfig = getAppFromConfig(this.options.app);
6363
if (options.module) {
64-
// Resolve path to module
65-
const modulePath = options.module.endsWith('.ts') ? options.module : `${options.module}.ts`;
66-
const parsedPath = dynamicPathParser(this.project, modulePath, appConfig);
67-
this.pathToModule = path.join(this.project.root, parsedPath.dir, parsedPath.base);
68-
69-
if (!fs.existsSync(this.pathToModule)) {
70-
throw ' ';
71-
}
64+
this.pathToModule =
65+
resolveModulePath(options.module, this.project, this.project.root, appConfig);
7266
} else {
7367
try {
7468
this.pathToModule = findParentModule(this.project.root, appConfig.root, this.generatePath);

packages/@angular/cli/blueprints/guard/index.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1+
import * as chalk from 'chalk';
2+
import * as path from 'path';
13
import { oneLine } from 'common-tags';
24
import { NodeHost } from '../../lib/ast-tools';
35
import { CliConfig } from '../../models/config';
46
import { getAppFromConfig } from '../../utilities/app-utils';
7+
import { resolveModulePath } from '../../utilities/resolve-module-file';
58

6-
const path = require('path');
7-
const fs = require('fs');
8-
const chalk = require('chalk');
99
const dynamicPathParser = require('../../utilities/dynamic-path-parser');
1010
const Blueprint = require('../../ember-cli/lib/models/blueprint');
1111
const stringUtils = require('ember-cli-string-utils');
@@ -36,16 +36,10 @@ export default Blueprint.extend({
3636
],
3737

3838
beforeInstall: function(options: any) {
39-
const appConfig = getAppFromConfig(this.options.app);
4039
if (options.module) {
41-
// Resolve path to module
42-
const modulePath = options.module.endsWith('.ts') ? options.module : `${options.module}.ts`;
43-
const parsedPath = dynamicPathParser(this.project, modulePath, appConfig);
44-
this.pathToModule = path.join(this.project.root, parsedPath.dir, parsedPath.base);
45-
46-
if (!fs.existsSync(this.pathToModule)) {
47-
throw 'Module specified does not exist';
48-
}
40+
const appConfig = getAppFromConfig(this.options.app);
41+
this.pathToModule =
42+
resolveModulePath(options.module, this.project, this.project.root, appConfig);
4943
}
5044
},
5145

packages/@angular/cli/blueprints/pipe/index.ts

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import {NodeHost} from '../../lib/ast-tools';
2-
import {CliConfig} from '../../models/config';
3-
import {getAppFromConfig} from '../../utilities/app-utils';
1+
import * as chalk from 'chalk';
2+
import * as path from 'path';
3+
import { NodeHost } from '../../lib/ast-tools';
4+
import { CliConfig } from '../../models/config';
5+
import { getAppFromConfig } from '../../utilities/app-utils';
6+
import { resolveModulePath } from '../../utilities/resolve-module-file';
47

5-
const path = require('path');
6-
const fs = require('fs');
7-
const chalk = require('chalk');
88
const dynamicPathParser = require('../../utilities/dynamic-path-parser');
99
const stringUtils = require('ember-cli-string-utils');
1010
const astUtils = require('../../utilities/ast-utils');
@@ -56,14 +56,8 @@ export default Blueprint.extend({
5656
beforeInstall: function(options: any) {
5757
const appConfig = getAppFromConfig(this.options.app);
5858
if (options.module) {
59-
// Resolve path to module
60-
const modulePath = options.module.endsWith('.ts') ? options.module : `${options.module}.ts`;
61-
const parsedPath = dynamicPathParser(this.project, modulePath, appConfig);
62-
this.pathToModule = path.join(this.project.root, parsedPath.dir, parsedPath.base);
63-
64-
if (!fs.existsSync(this.pathToModule)) {
65-
throw 'Module specified does not exist';
66-
}
59+
this.pathToModule =
60+
resolveModulePath(options.module, this.project, this.project.root, appConfig);
6761
} else {
6862
try {
6963
this.pathToModule = findParentModule(this.project.root, appConfig.root, this.generatePath);

packages/@angular/cli/blueprints/service/index.ts

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import {NodeHost} from '../../lib/ast-tools';
2-
import {CliConfig} from '../../models/config';
3-
import {getAppFromConfig} from '../../utilities/app-utils';
1+
import * as chalk from 'chalk';
2+
import * as path from 'path';
43
import { oneLine } from 'common-tags';
4+
import { NodeHost } from '../../lib/ast-tools';
5+
import { CliConfig } from '../../models/config';
6+
import { getAppFromConfig } from '../../utilities/app-utils';
7+
import { resolveModulePath } from '../../utilities/resolve-module-file';
58

6-
const path = require('path');
7-
const fs = require('fs');
8-
const chalk = require('chalk');
99
const dynamicPathParser = require('../../utilities/dynamic-path-parser');
1010
const Blueprint = require('../../ember-cli/lib/models/blueprint');
1111
const stringUtils = require('ember-cli-string-utils');
@@ -42,15 +42,9 @@ export default Blueprint.extend({
4242

4343
beforeInstall: function(options: any) {
4444
if (options.module) {
45-
// Resolve path to module
46-
const modulePath = options.module.endsWith('.ts') ? options.module : `${options.module}.ts`;
4745
const appConfig = getAppFromConfig(this.options.app);
48-
const parsedPath = dynamicPathParser(this.project, modulePath, appConfig);
49-
this.pathToModule = path.join(this.project.root, parsedPath.dir, parsedPath.base);
50-
51-
if (!fs.existsSync(this.pathToModule)) {
52-
throw 'Module specified does not exist';
53-
}
46+
this.pathToModule =
47+
resolveModulePath(options.module, this.project, this.project.root, appConfig);
5448
}
5549
},
5650

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import * as path from 'path';
2+
import * as fs from 'fs';
3+
const dynamicPathParser = require('./dynamic-path-parser');
4+
5+
export function resolveModulePath(
6+
moduleNameFromFlag: string, project: any, projectRoot: any, appConfig: any): string {
7+
let baseModuleName = moduleNameFromFlag;
8+
let parentFolders = '';
9+
10+
if (baseModuleName.includes(path.sep)) {
11+
const splitPath = baseModuleName.split(path.sep);
12+
baseModuleName = splitPath.pop();
13+
parentFolders = splitPath.join(path.sep);
14+
}
15+
16+
if (baseModuleName.includes('.')) {
17+
baseModuleName = baseModuleName
18+
.split('.')
19+
.filter(part => part !== 'module' && part !== 'ts')
20+
.join('.');
21+
}
22+
23+
const baseModuleWithFileSuffix = `${baseModuleName}.module.ts`;
24+
25+
const moduleRelativePath = path.join(parentFolders, baseModuleWithFileSuffix);
26+
let fullModulePath = buildFullPath(project, moduleRelativePath, appConfig, projectRoot);
27+
28+
if (!fs.existsSync(fullModulePath)) {
29+
const moduleWithFolderPrefix =
30+
path.join(parentFolders, baseModuleName, baseModuleWithFileSuffix);
31+
fullModulePath = buildFullPath(project, moduleWithFolderPrefix, appConfig, projectRoot);
32+
}
33+
34+
if (!fs.existsSync(fullModulePath)) {
35+
throw 'Specified module does not exist';
36+
}
37+
38+
return fullModulePath;
39+
}
40+
41+
function buildFullPath(project: any, relativeModulePath: string, appConfig: any, projectRoot: any) {
42+
const parsedPath = dynamicPathParser(project, relativeModulePath, appConfig);
43+
const fullModulePath = path.join(projectRoot, parsedPath.dir, parsedPath.base);
44+
45+
return fullModulePath;
46+
}

tests/acceptance/generate-component.spec.js

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,4 +222,97 @@ describe('Acceptance: ng generate component', function () {
222222
expect(existsSync(testPath)).to.equal(false);
223223
});
224224
});
225+
226+
it('should error out when given an incorrect module path', () => {
227+
return Promise.resolve()
228+
.then(() => ng(['generate', 'component', 'baz', '--module', 'foo']))
229+
.catch((error) => {
230+
expect(error).to.equal('Specified module does not exist');
231+
})
232+
});
233+
234+
describe('should import and add to declaration list', () => {
235+
it('when given a root level module with module.ts suffix', () => {
236+
const appRoot = path.join(root, 'tmp/foo');
237+
const modulePath = path.join(appRoot, 'src/app/app.module.ts');
238+
239+
return Promise.resolve()
240+
.then(() => ng(['generate', 'component', 'baz', '--module', 'app.module.ts']))
241+
.then(() => readFile(modulePath, 'utf-8'))
242+
.then(content => {
243+
expect(content).matches(/import.*BazComponent.*from '.\/baz\/baz.component';/);
244+
expect(content).matches(/declarations:\s+\[\r?\n\s+AppComponent,\r?\n\s+BazComponent\r?\n\s+\]/m);
245+
});
246+
});
247+
248+
it('when given a root level module with missing module.ts suffix', () => {
249+
const appRoot = path.join(root, 'tmp/foo');
250+
const modulePath = path.join(appRoot, 'src/app/app.module.ts');
251+
252+
return Promise.resolve()
253+
.then(() => ng(['generate', 'component', 'baz', '--module', 'app']))
254+
.then(() => readFile(modulePath, 'utf-8'))
255+
.then(content => {
256+
expect(content).matches(/import.*BazComponent.*from '.\/baz\/baz.component';/);
257+
expect(content).matches(/declarations:\s+\[\r?\n\s+AppComponent,\r?\n\s+BazComponent\r?\n\s+\]/m);
258+
});
259+
});
260+
261+
it('when given a submodule with module.ts suffix', () => {
262+
const appRoot = path.join(root, 'tmp/foo');
263+
const modulePath = path.join(appRoot, 'src/app/foo/foo.module.ts');
264+
265+
return Promise.resolve()
266+
.then(() => ng(['generate', 'module', 'foo']))
267+
.then(() => ng(['generate', 'component', 'baz', '--module', path.join('foo', 'foo.module.ts')]))
268+
.then(() => readFile(modulePath, 'utf-8'))
269+
.then(content => {
270+
expect(content).matches(/import.*BazComponent.*from '.\/..\/baz\/baz.component';/);
271+
expect(content).matches(/declarations:\s+\[BazComponent]/m);
272+
});
273+
});
274+
275+
it('when given a submodule with missing module.ts suffix', () => {
276+
const appRoot = path.join(root, 'tmp/foo');
277+
const modulePath = path.join(appRoot, 'src/app/foo/foo.module.ts');
278+
279+
return Promise.resolve()
280+
.then(() => ng(['generate', 'module', 'foo']))
281+
.then(() => ng(['generate', 'component', 'baz', '--module', path.join('foo', 'foo')]))
282+
.then(() => readFile(modulePath, 'utf-8'))
283+
.then(content => {
284+
expect(content).matches(/import.*BazComponent.*from '.\/..\/baz\/baz.component';/);
285+
expect(content).matches(/declarations:\s+\[BazComponent]/m);
286+
});
287+
});
288+
289+
it('when given a submodule folder', () => {
290+
const appRoot = path.join(root, 'tmp/foo');
291+
const modulePath = path.join(appRoot, 'src/app/foo/foo.module.ts');
292+
293+
return Promise.resolve()
294+
.then(() => ng(['generate', 'module', 'foo']))
295+
.then(() => ng(['generate', 'component', 'baz', '--module', 'foo']))
296+
.then(() => readFile(modulePath, 'utf-8'))
297+
.then(content => {
298+
expect(content).matches(/import.*BazComponent.*from '.\/..\/baz\/baz.component';/);
299+
expect(content).matches(/declarations:\s+\[BazComponent]/m);
300+
});
301+
});
302+
303+
it('when given deep submodule folder with missing module.ts suffix', () => {
304+
const appRoot = path.join(root, 'tmp/foo');
305+
const modulePath = path.join(appRoot, 'src/app/foo/bar/bar.module.ts');
306+
307+
return Promise.resolve()
308+
.then(() => ng(['generate', 'module', 'foo']))
309+
.then(() => ng(['generate', 'module', path.join('foo', 'bar')]))
310+
.then(() => ng(['generate', 'component', 'baz', '--module', path.join('foo', 'bar')]))
311+
.then(() => readFile(modulePath, 'utf-8'))
312+
.then(content => {
313+
expect(content).matches(/import.*BazComponent.*from '.\/..\/..\/baz\/baz.component';/);
314+
expect(content).matches(/declarations:\s+\[BazComponent]/m);
315+
});
316+
});
317+
});
225318
});

0 commit comments

Comments
 (0)