Skip to content

Commit 6e30b29

Browse files
committed
feat(@angular/cli): Update generate & new to use schematics
This feature is related to #6593
1 parent d85652e commit 6e30b29

File tree

15 files changed

+647
-241
lines changed

15 files changed

+647
-241
lines changed

docs/documentation/generate.md

+10
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,13 @@
4949
Adds more details to output logging.
5050
</p>
5151
</details>
52+
53+
<details>
54+
<summary>collection</summary>
55+
<p>
56+
<code>--collection</code> (aliases: <code>-c</code>) <em>default value: @schematics/angular</em>
57+
</p>
58+
<p>
59+
Schematics collection to use.
60+
</p>
61+
</details>

package-lock.json

+27-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
"homepage": "https://github.com/angular/angular-cli",
4242
"dependencies": {
4343
"@angular-devkit/build-optimizer": "0.0.13",
44+
"@angular-devkit/schematics": "0.0.17",
45+
"@schematics/angular": "0.0.27",
4446
"autoprefixer": "^6.5.3",
4547
"chalk": "^2.0.1",
4648
"circular-dependency-plugin": "^3.0.0",
+122-94
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
1-
import * as chalk from 'chalk';
2-
import * as fs from 'fs';
3-
import * as os from 'os';
4-
import * as path from 'path';
1+
import { cyan, yellow } from 'chalk';
2+
const stringUtils = require('ember-cli-string-utils');
53
import { oneLine } from 'common-tags';
64
import { CliConfig } from '../models/config';
75

6+
import 'rxjs/add/observable/of';
7+
import 'rxjs/add/operator/ignoreElements';
8+
import {
9+
getCollection,
10+
getEngineHost
11+
} from '../utilities/schematics';
12+
import { DynamicPathOptions, dynamicPathParser } from '../utilities/dynamic-path-parser';
13+
import { getAppFromConfig } from '../utilities/app-utils';
14+
import * as path from 'path';
15+
import { SchematicAvailableOptions } from '../tasks/schematic-get-options';
16+
817
const Command = require('../ember-cli/lib/models/command');
9-
const Blueprint = require('../ember-cli/lib/models/blueprint');
10-
const parseOptions = require('../ember-cli/lib/utilities/parse-options');
1118
const SilentError = require('silent-error');
1219

13-
function loadBlueprints(): Array<any> {
14-
const blueprintList = fs.readdirSync(path.join(__dirname, '..', 'blueprints'));
15-
const blueprints = blueprintList
16-
.filter(bp => bp.indexOf('-test') === -1)
17-
.filter(bp => bp !== 'ng')
18-
.map(bp => Blueprint.load(path.join(__dirname, '..', 'blueprints', bp)));
20+
const separatorRegEx = /[\/\\]/g;
1921

20-
return blueprints;
21-
}
2222

2323
export default Command.extend({
2424
name: 'generate',
25-
description: 'Generates and/or modifies files based on a blueprint.',
25+
description: 'Generates and/or modifies files based on a schematic.',
2626
aliases: ['g'],
2727

2828
availableOptions: [
@@ -34,117 +34,145 @@ export default Command.extend({
3434
description: 'Run through without making any changes.'
3535
},
3636
{
37-
name: 'lint-fix',
37+
name: 'force',
3838
type: Boolean,
39-
aliases: ['lf'],
40-
description: 'Use lint to fix files after generation.'
39+
default: false,
40+
aliases: ['f'],
41+
description: 'Forces overwriting of files.'
4142
},
4243
{
43-
name: 'verbose',
44+
name: 'app',
45+
type: String,
46+
aliases: ['a'],
47+
description: 'Specifies app name to use.'
48+
},
49+
{
50+
name: 'collection',
51+
type: String,
52+
aliases: ['c'],
53+
description: 'Schematics collection to use.'
54+
},
55+
{
56+
name: 'lint-fix',
4457
type: Boolean,
45-
default: false,
46-
aliases: ['v'],
47-
description: 'Adds more details to output logging.'
58+
aliases: ['lf'],
59+
description: 'Use lint to fix files after generation.'
4860
}
4961
],
5062

5163
anonymousOptions: [
52-
'<blueprint>'
64+
'<schematic>'
5365
],
5466

55-
beforeRun: function (rawArgs: string[]) {
56-
if (!rawArgs.length) {
57-
return;
67+
getCollectionName(rawArgs: string[]) {
68+
let collectionName = CliConfig.getValue('defaults.schematics.collection');
69+
if (rawArgs) {
70+
const parsedArgs = this.parseArgs(rawArgs, false);
71+
if (parsedArgs.options.collection) {
72+
collectionName = parsedArgs.options.collection;
73+
}
5874
}
75+
return collectionName;
76+
},
77+
78+
beforeRun: function(rawArgs: string[]) {
5979

6080
const isHelp = ['--help', '-h'].includes(rawArgs[0]);
6181
if (isHelp) {
6282
return;
6383
}
6484

65-
this.blueprints = loadBlueprints();
66-
67-
const name = rawArgs[0];
68-
const blueprint = this.blueprints.find((bp: any) => bp.name === name
69-
|| (bp.aliases && bp.aliases.includes(name)));
70-
71-
if (!blueprint) {
72-
SilentError.debugOrThrow('@angular/cli/commands/generate',
73-
`Invalid blueprint: ${name}`);
74-
}
75-
76-
if (!rawArgs[1]) {
77-
SilentError.debugOrThrow('@angular/cli/commands/generate',
78-
`The \`ng generate ${name}\` command requires a name to be specified.`);
85+
const schematicName = rawArgs[0];
86+
if (!schematicName) {
87+
return Promise.reject(new SilentError(oneLine`
88+
The "ng generate" command requires a
89+
schematic name to be specified.
90+
For more details, use "ng help".
91+
`));
7992
}
8093

8194
if (/^\d/.test(rawArgs[1])) {
8295
SilentError.debugOrThrow('@angular/cli/commands/generate',
83-
`The \`ng generate ${name} ${rawArgs[1]}\` file name cannot begin with a digit.`);
96+
`The \`ng generate ${schematicName} ${rawArgs[1]}\` file name cannot begin with a digit.`);
8497
}
8598

86-
rawArgs[0] = blueprint.name;
87-
this.registerOptions(blueprint);
88-
},
99+
const SchematicGetOptionsTask = require('../tasks/schematic-get-options').default;
89100

90-
printDetailedHelp: function () {
91-
if (!this.blueprints) {
92-
this.blueprints = loadBlueprints();
93-
}
94-
this.ui.writeLine(chalk.cyan(' Available blueprints'));
95-
this.ui.writeLine(this.blueprints.map((bp: any) => bp.printBasicHelp(false)).join(os.EOL));
101+
const getOptionsTask = new SchematicGetOptionsTask({
102+
ui: this.ui,
103+
project: this.project
104+
});
105+
const collectionName = this.getCollectionName(rawArgs);
106+
107+
return getOptionsTask.run({
108+
schematicName,
109+
collectionName
110+
})
111+
.then((availableOptions: SchematicAvailableOptions) => {
112+
let anonymousOptions: string[] = [];
113+
if (collectionName === '@schematics/angular' && schematicName === 'interface') {
114+
anonymousOptions = ['<type>'];
115+
}
116+
117+
this.registerOptions({
118+
anonymousOptions: anonymousOptions,
119+
availableOptions: availableOptions
120+
});
121+
});
96122
},
97123

98124
run: function (commandOptions: any, rawArgs: string[]) {
99-
const name = rawArgs[0];
100-
if (!name) {
101-
return Promise.reject(new SilentError(oneLine`
102-
The "ng generate" command requires a
103-
blueprint name to be specified.
104-
For more details, use "ng help".
105-
`));
125+
if (rawArgs[0] === 'module' && !rawArgs[1]) {
126+
throw 'The `ng generate module` command requires a name to be specified.';
106127
}
107128

108-
const blueprint = this.blueprints.find((bp: any) => bp.name === name
109-
|| (bp.aliases && bp.aliases.includes(name)));
110-
111-
const projectName = CliConfig.getValue('project.name');
112-
const blueprintOptions = {
113-
target: this.project.root,
114-
entity: {
115-
name: rawArgs[1],
116-
options: parseOptions(rawArgs.slice(2))
117-
},
118-
projectName,
119-
ui: this.ui,
129+
const entityName = rawArgs[1];
130+
commandOptions.name = stringUtils.dasherize(entityName.split(separatorRegEx).pop());
131+
132+
const appConfig = getAppFromConfig(commandOptions.app);
133+
const dynamicPathOptions: DynamicPathOptions = {
120134
project: this.project,
121-
settings: this.settings,
122-
testing: this.testing,
123-
args: rawArgs,
124-
...commandOptions
135+
entityName: entityName,
136+
appConfig: appConfig,
137+
dryRun: commandOptions.dryRun
125138
};
139+
const parsedPath = dynamicPathParser(dynamicPathOptions);
140+
commandOptions.sourceDir = appConfig.root;
141+
commandOptions.path = parsedPath.dir
142+
.replace(appConfig.root + path.sep, '')
143+
.replace(separatorRegEx, '/');
126144

127-
return blueprint.install(blueprintOptions)
128-
.then(() => {
129-
const lintFix = commandOptions.lintFix !== undefined ?
130-
commandOptions.lintFix : CliConfig.getValue('defaults.lintFix');
131-
132-
if (lintFix && blueprint.modifiedFiles) {
133-
const LintTask = require('../tasks/lint').default;
134-
const lintTask = new LintTask({
135-
ui: this.ui,
136-
project: this.project
137-
});
138-
139-
return lintTask.run({
140-
fix: true,
141-
force: true,
142-
silent: true,
143-
configs: [{
144-
files: blueprint.modifiedFiles.filter((file: string) => /.ts$/.test(file))
145-
}]
146-
});
147-
}
145+
const cwd = this.project.root;
146+
const schematicName = rawArgs[0];
147+
148+
const SchematicRunTask = require('../tasks/schematic-run').default;
149+
const schematicRunTask = new SchematicRunTask({
150+
ui: this.ui,
151+
project: this.project
152+
});
153+
const collectionName = this.getCollectionName(rawArgs);
154+
155+
if (collectionName === '@schematics/angular' && schematicName === 'interface' && rawArgs[2]) {
156+
commandOptions.type = rawArgs[2];
157+
}
158+
159+
return schematicRunTask.run({
160+
taskOptions: commandOptions,
161+
workingDir: cwd,
162+
collectionName,
163+
schematicName
148164
});
165+
},
166+
167+
printDetailedHelp: function () {
168+
const engineHost = getEngineHost();
169+
const collectionName = this.getCollectionName();
170+
const collection = getCollection(collectionName);
171+
const schematicNames: string[] = engineHost.listSchematics(collection);
172+
this.ui.writeLine(cyan('Available schematics:'));
173+
schematicNames.forEach(schematicName => {
174+
this.ui.writeLine(yellow(` ${schematicName}`));
175+
});
176+
this.ui.writeLine('');
149177
}
150178
});

0 commit comments

Comments
 (0)