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' ) ;
5
3
import { oneLine } from 'common-tags' ;
6
4
import { CliConfig } from '../models/config' ;
7
5
6
+ import 'rxjs/add/observable/of' ;
7
+ import 'rxjs/add/operator/ignoreElements' ;
8
+ import {
9
+ SchematicOutput ,
10
+ getCollection ,
11
+ getEngineHost ,
12
+ getSchematic ,
13
+ runSchematic ,
14
+ } from '../utilities/schematics' ;
15
+ import { DynamicPathOptions , dynamicPathParser } from '../utilities/dynamic-path-parser' ;
16
+ import { getAppFromConfig } from '../utilities/app-utils' ;
17
+ import * as path from 'path' ;
18
+
8
19
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' ) ;
11
20
const SilentError = require ( 'silent-error' ) ;
12
21
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 ) ) ) ;
19
-
20
- return blueprints ;
22
+ function mapSchematicOptions ( schematic : any ) : any [ ] {
23
+ const properties = schematic . description . schemaJson . properties ;
24
+ const keys = Object . keys ( properties ) ;
25
+ const options = keys
26
+ . map ( key => ( { ...properties [ key ] , ...{ name : stringUtils . dasherize ( key ) } } ) )
27
+ . map ( opt => {
28
+ let type ;
29
+ switch ( opt . type ) {
30
+ case 'string' :
31
+ type = String ;
32
+ break ;
33
+ case 'boolean' :
34
+ type = Boolean ;
35
+ break ;
36
+ }
37
+ let aliases : string [ ] = [ ] ;
38
+ if ( opt . alias ) {
39
+ aliases = [ ...aliases , opt . alias ] ;
40
+ }
41
+ if ( opt . aliases ) {
42
+ aliases = [ ...aliases , ...opt . aliases ] ;
43
+ }
44
+
45
+ return {
46
+ ...opt ,
47
+ aliases,
48
+ type,
49
+ default : undefined // do not carry over schematics defaults
50
+ } ;
51
+ } ) ;
52
+
53
+ return options ;
21
54
}
22
55
23
56
export default Command . extend ( {
24
57
name : 'generate' ,
25
- description : 'Generates and/or modifies files based on a blueprint .' ,
58
+ description : 'Generates and/or modifies files based on a schematic .' ,
26
59
aliases : [ 'g' ] ,
27
60
28
61
availableOptions : [
@@ -34,117 +67,145 @@ export default Command.extend({
34
67
description : 'Run through without making any changes.'
35
68
} ,
36
69
{
37
- name : 'lint-fix ' ,
70
+ name : 'force ' ,
38
71
type : Boolean ,
39
- aliases : [ 'lf' ] ,
40
- description : 'Use lint to fix files after generation.'
72
+ default : false ,
73
+ aliases : [ 'f' ] ,
74
+ description : 'Forces overwriting of files.'
75
+ } ,
76
+ {
77
+ name : 'app' ,
78
+ type : String ,
79
+ aliases : [ 'a' ] ,
80
+ description : 'Specifies app name to use.'
81
+ } ,
82
+ {
83
+ name : 'collection' ,
84
+ type : String ,
85
+ aliases : [ 'c' ] ,
86
+ description : 'Schematics collection to use.'
41
87
} ,
42
88
{
43
- name : 'verbose ' ,
89
+ name : 'lint-fix ' ,
44
90
type : Boolean ,
45
- default : false ,
46
- aliases : [ 'v' ] ,
47
- description : 'Adds more details to output logging.'
91
+ aliases : [ 'lf' ] ,
92
+ description : 'Use lint to fix files after generation.'
48
93
}
49
94
] ,
50
95
51
96
anonymousOptions : [
52
- '<blueprint >'
97
+ '<schemtatic >'
53
98
] ,
54
99
55
- beforeRun : function ( rawArgs : string [ ] ) {
56
- if ( ! rawArgs . length ) {
57
- return ;
100
+ getCollectionName ( rawArgs : string [ ] ) {
101
+ let collectionName = CliConfig . getValue ( 'defaults.schematics.collection' ) ;
102
+ if ( rawArgs ) {
103
+ const parsedArgs = this . parseArgs ( rawArgs , false ) ;
104
+ if ( parsedArgs . options . collection ) {
105
+ collectionName = parsedArgs . options . collection ;
106
+ }
58
107
}
108
+ return collectionName ;
109
+ } ,
110
+
111
+ beforeRun : function ( rawArgs : string [ ] ) {
112
+ const collection = getCollection ( this . getCollectionName ( rawArgs ) ) ;
59
113
60
114
const isHelp = [ '--help' , '-h' ] . includes ( rawArgs [ 0 ] ) ;
61
115
if ( isHelp ) {
62
116
return ;
63
117
}
64
118
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.` ) ;
119
+ const schematicName = rawArgs [ 0 ] ;
120
+ if ( ! schematicName ) {
121
+ return Promise . reject ( new SilentError ( oneLine `
122
+ The "ng generate" command requires a
123
+ schematic name to be specified.
124
+ For more details, use "ng help".
125
+ ` ) ) ;
79
126
}
80
127
81
128
if ( / ^ \d / . test ( rawArgs [ 1 ] ) ) {
82
129
SilentError . debugOrThrow ( '@angular/cli/commands/generate' ,
83
- `The \`ng generate ${ name } ${ rawArgs [ 1 ] } \` file name cannot begin with a digit.` ) ;
130
+ `The \`ng generate ${ schematicName } ${ rawArgs [ 1 ] } \` file name cannot begin with a digit.` ) ;
84
131
}
85
132
86
- rawArgs [ 0 ] = blueprint . name ;
87
- this . registerOptions ( blueprint ) ;
88
- } ,
133
+ this . schematic = getSchematic ( collection , schematicName ) ;
89
134
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 ) ) ;
135
+ const schematicOptions = mapSchematicOptions ( this . schematic ) ;
136
+
137
+ this . registerOptions ( { availableOptions : schematicOptions } ) ;
96
138
} ,
97
139
98
140
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
- ` ) ) ;
106
- }
141
+ const entityName = rawArgs [ 1 ] ;
142
+ commandOptions . name = stringUtils . dasherize ( entityName . split ( path . sep ) . pop ( ) ) ;
107
143
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 ,
144
+ const appConfig = getAppFromConfig ( commandOptions . app ) ;
145
+ const dynamicPathOptions : DynamicPathOptions = {
120
146
project : this . project ,
121
- settings : this . settings ,
122
- testing : this . testing ,
123
- args : rawArgs ,
124
- ...commandOptions
147
+ entityName : entityName ,
148
+ appConfig : appConfig ,
149
+ dryRun : commandOptions . dryRun
125
150
} ;
151
+ const parsedPath = dynamicPathParser ( dynamicPathOptions ) ;
152
+ commandOptions . sourceDir = appConfig . root ;
153
+ commandOptions . path = parsedPath . dir . replace ( appConfig . root + path . sep , '' ) ;
154
+
155
+ const cwd = this . project . root ;
156
+ let dir = cwd ;
157
+ const sourceRoot = path . join ( this . project . root , appConfig . root ) ;
158
+ const appRoot = path . join ( sourceRoot , 'app' ) ;
159
+ if ( cwd . indexOf ( appRoot ) === 0 ) {
160
+ dir = cwd ;
161
+ } else if ( cwd . indexOf ( sourceRoot ) === 0 ) {
162
+ dir = path . join ( sourceRoot , 'app' ) ;
163
+ } else {
164
+ dir = path . join ( this . project . root , appConfig . root , 'app' ) ;
165
+ }
126
166
127
- return blueprint . install ( blueprintOptions )
128
- . then ( ( ) => {
167
+ const collectionName = this . getCollectionName ( rawArgs ) ;
168
+ const collection = getCollection ( collectionName ) ;
169
+ const schematicName = rawArgs [ 0 ] ;
170
+ const schematic = getSchematic ( collection , schematicName ) ;
171
+ return runSchematic ( schematic , cwd , dir , commandOptions , this . project . root )
172
+ . then ( ( output : SchematicOutput ) => {
173
+ const modifiedFiles = output . modifiedFiles ;
129
174
const lintFix = commandOptions . lintFix !== undefined ?
130
175
commandOptions . lintFix : CliConfig . getValue ( 'defaults.lintFix' ) ;
131
176
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 ) => / .t s $ / . test ( file ) )
145
- } ]
146
- } ) ;
177
+ if ( lintFix && modifiedFiles ) {
178
+ const LintTask = require ( '../tasks/lint' ) . default ;
179
+ const lintTask = new LintTask ( {
180
+ ui : this . ui ,
181
+ project : this . project
182
+ } ) ;
183
+
184
+ return lintTask . run ( {
185
+ fix : true ,
186
+ force : true ,
187
+ silent : true ,
188
+ configs : [ {
189
+ files : modifiedFiles
190
+ . filter ( ( file : string ) => / .t s $ / . test ( file ) )
191
+ . map ( ( file : string ) => path . join ( this . project . root , file ) )
192
+ } ]
193
+ } ) ;
147
194
}
148
195
} ) ;
196
+
197
+
198
+ } ,
199
+
200
+ printDetailedHelp : function ( ) {
201
+ const engineHost = getEngineHost ( ) ;
202
+ const collectionName = this . getCollectionName ( ) ;
203
+ const collection = getCollection ( collectionName ) ;
204
+ const schematicNames : string [ ] = engineHost . listSchematics ( collection ) ;
205
+ this . ui . writeLine ( cyan ( 'Available schematics:' ) ) ;
206
+ schematicNames . forEach ( schematicName => {
207
+ this . ui . writeLine ( yellow ( ` ${ schematicName } ` ) ) ;
208
+ } ) ;
209
+ this . ui . writeLine ( '' ) ;
149
210
}
150
211
} ) ;
0 commit comments