0244d5fb800f98cb3154491dfb2a587a44adeadb
[idea/contrib.git] / AngularJS / src / ngCli / schematicsInfoProvider.ts
1 try {
2     require('@angular/cli/utilities/schematics');
3 } catch (e) {
4     console.info("No schematics")
5     process.exit(0)
6 }
7
8 import {getCollection, getEngineHost, getSchematic} from '@angular/cli/utilities/schematics';
9 import {Option} from "@angular/cli/models/command";
10 import * as path from "path";
11 import * as fs from "fs";
12
13 interface SchematicsInfo {
14     description?: string;
15     name: string;
16     error?: string;
17     arguments?: Option[];
18     options?: Option[];
19     hidden?: boolean;
20 }
21
22 const engineHost = getEngineHost();
23
24 const includeHidden = process.argv[2] === "--includeHidden";
25
26 let defaultCollectionName;
27 try {
28     defaultCollectionName = require('@angular/cli/utilities/config').getDefaultSchematicCollection();
29 } catch (e) {
30     defaultCollectionName = require('@angular/cli/models/config').CliConfig.getValue('defaults.schematics.collection');
31 }
32
33 const collections = getAvailableSchematicCollections();
34 if (collections.indexOf(defaultCollectionName) < 0) {
35     collections.push(defaultCollectionName);
36 }
37
38 const allSchematics = collections
39 // Update schematics should be executed only with `ng update`
40     .filter(c => c !== "@schematics/update")
41     .map(getCollectionSchematics)
42     .reduce((a, b) => a.concat(...b));
43
44 console.info(JSON.stringify(allSchematics, null, 2))
45
46 function getAvailableSchematicCollections() {
47     let result: string[] = [];
48     let packages: string[] = [];
49     fs.readdirSync(path.resolve(process.cwd(), "node_modules")).forEach(dir => {
50         if (dir.startsWith("@")) {
51             fs.readdirSync(path.resolve(process.cwd(), `node_modules/${dir}`)).forEach(subDir => {
52                 packages.push(`${dir}/${subDir}`)
53             })
54         } else {
55             packages.push(dir)
56         }
57     })
58
59     for (const pkgName of packages) {
60         const pkgPath = path.resolve(process.cwd(), `node_modules/${pkgName}/package.json`)
61         if (fs.existsSync(pkgPath)) {
62             const subInfo = require(pkgPath)
63             if (subInfo !== undefined && subInfo.schematics !== undefined) {
64                 result.push(pkgName);
65             }
66         }
67     }
68     return result;
69 }
70
71 function getCollectionSchematics(collectionName: string): SchematicsInfo[] {
72     let schematicNames: string[];
73     let collection: any;
74     try {
75         collection = getCollection(collectionName);
76         schematicNames = includeHidden
77             ? listAllSchematics(collection)
78             : engineHost.listSchematics(collection);
79     } catch (e) {
80         return [{
81             name: collectionName,
82             error: "" + e.message
83         }]
84     }
85     try {
86         const schematicInfos: any[] = schematicNames
87             .map(name => getSchematic(collection, name).description)
88             //`ng-add` schematics should be executed only with `ng add`
89             .filter(info => (info.name !== "ng-add" || includeHidden) && info.schemaJson !== undefined);
90
91         const newFormat = schematicInfos
92             .map(info => info.schemaJson.properties)
93             .map(prop => Object.keys(prop).map(k => prop[k]))
94             .reduce((a, b) => a.concat(b), [])
95             .find(prop => prop.$default)
96
97         return schematicInfos.map(info => {
98             const required = info.schemaJson.required || [];
99             return {
100                 description: info.description,
101                 name: (collectionName === defaultCollectionName ? "" : collectionName + ":") + info.name,
102                 hidden: info.hidden,
103                 options: filterProps(info.schemaJson,
104                     (key, prop) => newFormat ? prop.$default === undefined : required.indexOf(key) < 0)
105                     .concat(coreOptions()),
106                 arguments: filterProps(info.schemaJson,
107                     (key, prop) => newFormat ? prop.$default !== undefined && prop.$default.$source === "argv" : required.indexOf(key) >= 0)
108             }
109         })
110     } catch (e) {
111         console.error(e.stack || e);
112         return [];
113     }
114 }
115
116 function listAllSchematics(collection: any) {
117     collection = collection.description;
118     const schematics = [];
119     for (const key of Object.keys(collection.schematics)) {
120         const schematic = collection.schematics[key];
121         if (schematic.private) {
122             continue
123         }
124         // If extends is present without a factory it is an alias, do not return it
125         //   unless it is from another collection.
126         if (!schematic.extends || schematic.factory) {
127             schematics.push(key);
128         }
129         else if (schematic.extends && schematic.extends.indexOf(':') !== -1) {
130             schematics.push(key);
131         }
132     }
133     return schematics;
134 }
135
136 function filterProps(schemaJson: any, filter: (k: string, prop: any) => boolean): any[] {
137     const required = schemaJson.required || [];
138     const props = schemaJson.properties;
139     return Object.keys(props).filter(
140         key => filter(key, props[key])
141     ).map(k => Object.assign({name: k, required: required.indexOf(k) >= 0}, props[k]))
142 }
143
144 function coreOptions(): any[] {
145     return [
146         {
147             name: 'dryRun',
148             type: "Boolean",
149             default: false,
150             alias: 'd',
151             description: 'Run through without making any changes.',
152         },
153         {
154             name: 'force',
155             type: "Boolean",
156             default: false,
157             alias: 'f',
158             description: 'Forces overwriting of files.',
159         }
160     ]
161 }