6
6
* found in the LICENSE file at https://angular.io/license
7
7
*/
8
8
import {
9
+ analytics ,
9
10
experimental ,
10
11
json ,
11
12
logging ,
@@ -17,20 +18,11 @@ import {
17
18
virtualFs ,
18
19
} from '@angular-devkit/core' ;
19
20
import { NodeJsSyncHost } from '@angular-devkit/core/node' ;
20
- import {
21
- DryRunEvent ,
22
- Engine ,
23
- SchematicEngine ,
24
- UnsuccessfulWorkflowExecution ,
25
- workflow ,
26
- } from '@angular-devkit/schematics' ;
21
+ import { DryRunEvent , UnsuccessfulWorkflowExecution , workflow } from '@angular-devkit/schematics' ;
27
22
import {
28
23
FileSystemCollection ,
29
- FileSystemCollectionDesc ,
30
- FileSystemEngineHostBase ,
24
+ FileSystemEngine ,
31
25
FileSystemSchematic ,
32
- FileSystemSchematicDesc ,
33
- NodeModulesEngineHost ,
34
26
NodeWorkflow ,
35
27
validateOptionsWithSchema ,
36
28
} from '@angular-devkit/schematics/tools' ;
@@ -50,6 +42,12 @@ import { Arguments, CommandContext, CommandDescription, Option } from './interfa
50
42
import { parseArguments , parseFreeFormArguments } from './parser' ;
51
43
52
44
45
+ export const schematicsAnalyticsWhitelist = [
46
+ '@schematics/angular' ,
47
+ '@schematics/update' ,
48
+ ] ;
49
+
50
+
53
51
export interface BaseSchematicSchema {
54
52
debug ?: boolean ;
55
53
dryRun ?: boolean ;
@@ -80,8 +78,7 @@ export abstract class SchematicCommand<
80
78
readonly allowAdditionalArgs : boolean = false ;
81
79
private _host = new NodeJsSyncHost ( ) ;
82
80
private _workspace : experimental . workspace . Workspace ;
83
- private readonly _engine : Engine < FileSystemCollectionDesc , FileSystemSchematicDesc > ;
84
- protected _workflow : workflow . BaseWorkflow ;
81
+ protected _workflow : NodeWorkflow ;
85
82
86
83
protected collectionName = '@schematics/angular' ;
87
84
protected schematicName ?: string ;
@@ -90,10 +87,8 @@ export abstract class SchematicCommand<
90
87
context : CommandContext ,
91
88
description : CommandDescription ,
92
89
logger : logging . Logger ,
93
- private readonly _engineHost : FileSystemEngineHostBase = new NodeModulesEngineHost ( ) ,
94
90
) {
95
91
super ( context , description , logger ) ;
96
- this . _engine = new SchematicEngine ( this . _engineHost ) ;
97
92
}
98
93
99
94
public async initialize ( options : T & Arguments ) {
@@ -110,6 +105,15 @@ export abstract class SchematicCommand<
110
105
) ;
111
106
112
107
this . description . options . push ( ...options . filter ( x => ! x . hidden ) ) ;
108
+
109
+ // Remove any user analytics from schematics that are NOT part of our whitelist.
110
+ for ( const o of this . description . options ) {
111
+ if ( o . userAnalytics ) {
112
+ if ( ! schematicsAnalyticsWhitelist . includes ( this . collectionName ) ) {
113
+ o . userAnalytics = undefined ;
114
+ }
115
+ }
116
+ }
113
117
}
114
118
}
115
119
@@ -198,12 +202,8 @@ export abstract class SchematicCommand<
198
202
}
199
203
}
200
204
201
- protected getEngineHost ( ) {
202
- return this . _engineHost ;
203
- }
204
- protected getEngine ( ) :
205
- Engine < FileSystemCollectionDesc , FileSystemSchematicDesc > {
206
- return this . _engine ;
205
+ protected getEngine ( ) : FileSystemEngine {
206
+ return this . _workflow . engine ;
207
207
}
208
208
209
209
protected getCollection ( collectionName : string ) : FileSystemCollection {
@@ -260,8 +260,57 @@ export abstract class SchematicCommand<
260
260
root : normalize ( this . workspace . root ) ,
261
261
} ,
262
262
) ;
263
+ workflow . engineHost . registerContextTransform ( context => {
264
+ // This is run by ALL schematics, so if someone uses `externalSchematics(...)` which
265
+ // is whitelisted, it would move to the right analytics (even if their own isn't).
266
+ const collectionName : string = context . schematic . collection . description . name ;
267
+ if ( schematicsAnalyticsWhitelist . includes ( collectionName ) ) {
268
+ return {
269
+ ...context ,
270
+ analytics : this . analytics ,
271
+ } ;
272
+ } else {
273
+ return {
274
+ ...context ,
275
+ analytics : new analytics . NoopAnalytics ( ) ,
276
+ } ;
277
+ }
278
+ } ) ;
263
279
264
- this . _engineHost . registerOptionsTransform ( validateOptionsWithSchema ( workflow . registry ) ) ;
280
+ workflow . engineHost . registerOptionsTransform ( validateOptionsWithSchema ( workflow . registry ) ) ;
281
+
282
+ // This needs to be the last transform as it reports the flags to analytics (if enabled).
283
+ workflow . engineHost . registerOptionsTransform ( async (
284
+ schematic ,
285
+ options : { [ prop : string ] : number | string } ,
286
+ context ,
287
+ ) : Promise < { [ prop : string ] : number | string } > => {
288
+ const analytics = context && context . analytics ;
289
+ if ( ! schematic . schemaJson || ! context || ! analytics ) {
290
+ return options ;
291
+ }
292
+
293
+ const collectionName = context . schematic . collection . description . name ;
294
+ const schematicName = context . schematic . description . name ;
295
+
296
+ if ( ! schematicsAnalyticsWhitelist . includes ( collectionName ) ) {
297
+ return options ;
298
+ }
299
+
300
+ const args = await parseJsonSchemaToOptions ( this . _workflow . registry , schematic . schemaJson ) ;
301
+ const dimensions : ( boolean | number | string ) [ ] = [ ] ;
302
+ for ( const option of args ) {
303
+ const ua = option . userAnalytics ;
304
+
305
+ if ( option . name in options && ua ) {
306
+ dimensions [ ua ] = options [ option . name ] ;
307
+ }
308
+ }
309
+
310
+ analytics . event ( 'schematics' , collectionName + ':' + schematicName , { dimensions } ) ;
311
+
312
+ return options ;
313
+ } ) ;
265
314
266
315
if ( options . defaults ) {
267
316
workflow . registry . addPreTransform ( schema . transforms . addUndefinedDefaults ) ;
0 commit comments