yeoman-generator 4.8.3 → 4.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/actions/fs.js +230 -0
- package/lib/actions/install.js +1 -1
- package/lib/index.js +293 -86
- package/lib/util/storage.js +78 -5
- package/package.json +5 -4
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/* eslint max-params: [1, 5] */
|
|
2
|
+
const assert = require('assert');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @mixin
|
|
6
|
+
* @alias actions/fs
|
|
7
|
+
*/
|
|
8
|
+
const fs = module.exports;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Read file from templates folder.
|
|
12
|
+
* mem-fs-editor method's shortcut, for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}.
|
|
13
|
+
* Shortcut for this.fs.read(this.templatePath(filepath))
|
|
14
|
+
*
|
|
15
|
+
* @param {String} filepath - absolute file path or relative to templates folder.
|
|
16
|
+
* @param {...*} args - for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}
|
|
17
|
+
* @returns {*} for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}
|
|
18
|
+
*/
|
|
19
|
+
fs.readTemplate = function(filepath, ...args) {
|
|
20
|
+
return this.fs.read(this.templatePath(filepath), ...args);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Copy file from templates folder to destination folder.
|
|
25
|
+
* mem-fs-editor method's shortcut, for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}.
|
|
26
|
+
* Shortcut for this.fs.copy(this.templatePath(from), this.destinationPath(to))
|
|
27
|
+
*
|
|
28
|
+
* @param {String} from - absolute file path or relative to templates folder.
|
|
29
|
+
* @param {String} to - absolute file path or relative to destination folder.
|
|
30
|
+
* @param {...*} args - for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}
|
|
31
|
+
* @returns {*} for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}
|
|
32
|
+
*/
|
|
33
|
+
fs.copyTemplate = function(from, to, ...args) {
|
|
34
|
+
return this.fs.copy(this.templatePath(from), this.destinationPath(to), ...args);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Read file from destination folder
|
|
39
|
+
* mem-fs-editor method's shortcut, for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}.
|
|
40
|
+
* Shortcut for this.fs.read(this.destinationPath(filepath)).
|
|
41
|
+
*
|
|
42
|
+
* @param {String} filepath - absolute file path or relative to destination folder.
|
|
43
|
+
* @param {...*} args - for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}
|
|
44
|
+
* @returns {*} for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}
|
|
45
|
+
*/
|
|
46
|
+
fs.readDestination = function(filepath, ...args) {
|
|
47
|
+
return this.fs.read(this.destinationPath(filepath), ...args);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Read JSON file from destination folder
|
|
52
|
+
* mem-fs-editor method's shortcut, for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}.
|
|
53
|
+
* Shortcut for this.fs.readJSON(this.destinationPath(filepath)).
|
|
54
|
+
*
|
|
55
|
+
* @param {String} filepath - absolute file path or relative to destination folder.
|
|
56
|
+
* @param {...*} args - for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}
|
|
57
|
+
* @returns {*} for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}
|
|
58
|
+
*/
|
|
59
|
+
fs.readDestinationJSON = function(filepath, ...args) {
|
|
60
|
+
return this.fs.readJSON(this.destinationPath(filepath), ...args);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Write file to destination folder
|
|
65
|
+
* mem-fs-editor method's shortcut, for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}.
|
|
66
|
+
* Shortcut for this.fs.write(this.destinationPath(filepath)).
|
|
67
|
+
*
|
|
68
|
+
* @param {String} filepath - absolute file path or relative to destination folder.
|
|
69
|
+
* @param {...*} args - for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}
|
|
70
|
+
* @returns {*} for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}
|
|
71
|
+
*/
|
|
72
|
+
fs.writeDestination = function(filepath, ...args) {
|
|
73
|
+
return this.fs.write(this.destinationPath(filepath), ...args);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Write json file to destination folder
|
|
78
|
+
* mem-fs-editor method's shortcut, for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}.
|
|
79
|
+
* Shortcut for this.fs.writeJSON(this.destinationPath(filepath)).
|
|
80
|
+
*
|
|
81
|
+
* @param {String} filepath - absolute file path or relative to destination folder.
|
|
82
|
+
* @param {...*} args - for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}
|
|
83
|
+
* @returns {*} for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}
|
|
84
|
+
*/
|
|
85
|
+
fs.writeDestinationJSON = function(filepath, ...args) {
|
|
86
|
+
return this.fs.writeJSON(this.destinationPath(filepath), ...args);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Delete file from destination folder
|
|
91
|
+
* mem-fs-editor method's shortcut, for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}.
|
|
92
|
+
* Shortcut for this.fs.delete(this.destinationPath(filepath)).
|
|
93
|
+
*
|
|
94
|
+
* @param {String} filepath - absolute file path or relative to destination folder.
|
|
95
|
+
* @param {...*} args - for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}
|
|
96
|
+
* @returns {*} for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}
|
|
97
|
+
*/
|
|
98
|
+
fs.deleteDestination = function(filepath, ...args) {
|
|
99
|
+
return this.fs.delete(this.destinationPath(filepath), ...args);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Copy file from destination folder to another destination folder.
|
|
104
|
+
* mem-fs-editor method's shortcut, for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}.
|
|
105
|
+
* Shortcut for this.fs.copy(this.destinationPath(from), this.destinationPath(to)).
|
|
106
|
+
*
|
|
107
|
+
* @param {String} from - absolute file path or relative to destination folder.
|
|
108
|
+
* @param {String} to - absolute file path or relative to destination folder.
|
|
109
|
+
* @param {...*} args - for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}
|
|
110
|
+
* @returns {*} for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}
|
|
111
|
+
*/
|
|
112
|
+
fs.copyDestination = function(from, to, ...args) {
|
|
113
|
+
return this.fs.copy(this.destinationPath(from), this.destinationPath(to), ...args);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Move file from destination folder to another destination folder.
|
|
118
|
+
* mem-fs-editor method's shortcut, for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}.
|
|
119
|
+
* Shortcut for this.fs.move(this.destinationPath(from), this.destinationPath(to)).
|
|
120
|
+
*
|
|
121
|
+
* @param {String} from - absolute file path or relative to destination folder.
|
|
122
|
+
* @param {String} to - absolute file path or relative to destination folder.
|
|
123
|
+
* @returns {*} for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}
|
|
124
|
+
*/
|
|
125
|
+
fs.moveDestination = function(from, to, ...args) {
|
|
126
|
+
return this.fs.move(this.destinationPath(from), this.destinationPath(to), ...args);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Exists file on destination folder.
|
|
131
|
+
* mem-fs-editor method's shortcut, for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}.
|
|
132
|
+
* Shortcut for this.fs.exists(this.destinationPath(filepath)).
|
|
133
|
+
*
|
|
134
|
+
* @param {String} filepath - absolute file path or relative to destination folder.
|
|
135
|
+
* @param {...*} args - for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}
|
|
136
|
+
* @returns {*} for more information see [mem-fs-editor]{@link https://github.com/SBoudrias/mem-fs-editor}
|
|
137
|
+
*/
|
|
138
|
+
fs.existsDestination = function(filepath, ...args) {
|
|
139
|
+
return this.fs.exists(this.destinationPath(filepath), ...args);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Copy a template from templates folder to the destination.
|
|
144
|
+
*
|
|
145
|
+
* @param {String|Array} source - template file, absolute or relative to templatePath().
|
|
146
|
+
* @param {String|Array} [destination] - destination, absolute or relative to destinationPath().
|
|
147
|
+
* @param {Object} [templateData] - ejs data
|
|
148
|
+
* @param {Object} [templateOptions] - ejs options
|
|
149
|
+
* @param {Object} [copyOptions] - mem-fs-editor copy options
|
|
150
|
+
*/
|
|
151
|
+
fs.renderTemplate = function(
|
|
152
|
+
source = '',
|
|
153
|
+
destination = source,
|
|
154
|
+
templateData = this._templateData(),
|
|
155
|
+
templateOptions,
|
|
156
|
+
copyOptions
|
|
157
|
+
) {
|
|
158
|
+
if (typeof templateData === 'string') {
|
|
159
|
+
templateData = this._templateData(templateData);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
templateOptions = { context: this, ...templateOptions };
|
|
163
|
+
|
|
164
|
+
source = Array.isArray(source) ? source : [source];
|
|
165
|
+
const templatePath = this.templatePath(...source);
|
|
166
|
+
destination = Array.isArray(destination) ? destination : [destination];
|
|
167
|
+
const destinationPath = this.destinationPath(...destination);
|
|
168
|
+
|
|
169
|
+
this.fs.copyTpl(
|
|
170
|
+
templatePath,
|
|
171
|
+
destinationPath,
|
|
172
|
+
templateData,
|
|
173
|
+
templateOptions,
|
|
174
|
+
copyOptions
|
|
175
|
+
);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Copy templates from templates folder to the destination.
|
|
180
|
+
*
|
|
181
|
+
* @param {Array} templates - template file, absolute or relative to templatePath().
|
|
182
|
+
* @param {function} [templates.when] - conditional if the template should be written.
|
|
183
|
+
* First argument is the templateData, second is the generator.
|
|
184
|
+
* @param {String|Array} templates.source - template file, absolute or relative to templatePath().
|
|
185
|
+
* @param {String|Array} [templates.destination] - destination, absolute or relative to destinationPath().
|
|
186
|
+
* @param {Object} [templates.templateOptions] - ejs options
|
|
187
|
+
* @param {Object} [templates.copyOptions] - mem-fs-editor copy options
|
|
188
|
+
* @param {Object} [templateData] - ejs data
|
|
189
|
+
*/
|
|
190
|
+
fs.renderTemplates = function(templates, templateData = this._templateData()) {
|
|
191
|
+
assert(Array.isArray(templates), 'Templates must an array');
|
|
192
|
+
if (typeof templateData === 'string') {
|
|
193
|
+
templateData = this._templateData(templateData);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const self = this;
|
|
197
|
+
const renderEachTemplate = template => {
|
|
198
|
+
if (template.when && !template.when(templateData, this)) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const { source, destination, templateOptions, copyOptions } = template;
|
|
203
|
+
self.renderTemplate(source, destination, templateData, templateOptions, copyOptions);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
templates.forEach(template => renderEachTemplate(template));
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Utility method to get a formatted data for templates.
|
|
211
|
+
*
|
|
212
|
+
* @param {String} path - path to the storage key.
|
|
213
|
+
* @return {Object} data to be passed to the templates.
|
|
214
|
+
*/
|
|
215
|
+
fs._templateData = function(path) {
|
|
216
|
+
if (path) {
|
|
217
|
+
return this.config.getPath(path);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const allConfig = this.config.getAll();
|
|
221
|
+
if (this.generatorConfig) {
|
|
222
|
+
Object.assign(allConfig, this.generatorConfig.getAll());
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (this.instanceConfig) {
|
|
226
|
+
Object.assign(allConfig, this.instanceConfig.getAll());
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return allConfig;
|
|
230
|
+
};
|
package/lib/actions/install.js
CHANGED
|
@@ -110,7 +110,7 @@ install.scheduleInstallTask = function(installer, paths, options, spawnOptions)
|
|
|
110
110
|
* npm: false
|
|
111
111
|
* });
|
|
112
112
|
*
|
|
113
|
-
* @param {Object} [options]
|
|
113
|
+
* @param {Object} [options] - options
|
|
114
114
|
* @param {Boolean|Object} [options.npm=true] - whether to run `npm install` or can be options to pass to `dargs` as arguments
|
|
115
115
|
* @param {Boolean|Object} [options.bower=true] - whether to run `bower install` or can be options to pass to `dargs` as arguments
|
|
116
116
|
* @param {Boolean|Object} [options.yarn=false] - whether to run `yarn install` or can be options to pass to `dargs` as arguments
|
package/lib/index.js
CHANGED
|
@@ -21,11 +21,11 @@ const promptSuggestion = require('./util/prompt-suggestion');
|
|
|
21
21
|
|
|
22
22
|
const EMPTY = '@@_YEOMAN_EMPTY_MARKER_@@';
|
|
23
23
|
const debug = createDebug('yeoman:generator');
|
|
24
|
-
const ENV_VER_WITH_VER_API = '
|
|
24
|
+
const ENV_VER_WITH_VER_API = '2.9.0';
|
|
25
25
|
|
|
26
26
|
// Ensure a prototype method is a candidate run by default
|
|
27
27
|
const methodIsValid = function(name) {
|
|
28
|
-
return name.charAt(0)
|
|
28
|
+
return !['_', '#'].includes(name.charAt(0)) && name !== 'constructor';
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
// New runWithOptions should take precedence if exists.
|
|
@@ -98,6 +98,7 @@ class Generator extends EventEmitter {
|
|
|
98
98
|
];
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
/* eslint-disable complexity */
|
|
101
102
|
/**
|
|
102
103
|
* @classdesc The `Generator` class provides the common API shared by all generators.
|
|
103
104
|
* It define options, arguments, file, prompt, log, API, etc.
|
|
@@ -111,6 +112,7 @@ class Generator extends EventEmitter {
|
|
|
111
112
|
* @mixes actions/install
|
|
112
113
|
* @mixes actions/spawn-command
|
|
113
114
|
* @mixes actions/user
|
|
115
|
+
* @mixes actions/fs
|
|
114
116
|
* @mixes nodejs/EventEmitter
|
|
115
117
|
*
|
|
116
118
|
* @param {string[]} args - Provide arguments at initialization
|
|
@@ -147,8 +149,11 @@ class Generator extends EventEmitter {
|
|
|
147
149
|
this._args = args || [];
|
|
148
150
|
this._options = {};
|
|
149
151
|
this._arguments = [];
|
|
152
|
+
this._prompts = [];
|
|
150
153
|
this._composedWith = [];
|
|
151
154
|
this._transformStreams = [];
|
|
155
|
+
this._namespace = this.options.namespace;
|
|
156
|
+
this._namespaceId = this.options.namespaceId;
|
|
152
157
|
|
|
153
158
|
this.option('help', {
|
|
154
159
|
type: Boolean,
|
|
@@ -174,6 +179,12 @@ class Generator extends EventEmitter {
|
|
|
174
179
|
default: false
|
|
175
180
|
});
|
|
176
181
|
|
|
182
|
+
this.option('ask-answered', {
|
|
183
|
+
type: Boolean,
|
|
184
|
+
description: 'Show prompts for already configured options',
|
|
185
|
+
default: false
|
|
186
|
+
});
|
|
187
|
+
|
|
177
188
|
this.resolved = this.options.resolved || __dirname;
|
|
178
189
|
this.env = this.options.env;
|
|
179
190
|
|
|
@@ -210,13 +221,7 @@ class Generator extends EventEmitter {
|
|
|
210
221
|
}
|
|
211
222
|
}
|
|
212
223
|
|
|
213
|
-
|
|
214
|
-
this.fs = this.env.fs || require('mem-fs-editor').create(this.env.sharedFs);
|
|
215
|
-
} catch (_) {
|
|
216
|
-
throw new Error(
|
|
217
|
-
"Current environment don't provides some necessary feature this generator needs"
|
|
218
|
-
);
|
|
219
|
-
}
|
|
224
|
+
this.fs = require('mem-fs-editor').create(this.env.sharedFs);
|
|
220
225
|
|
|
221
226
|
this.description = this.description || '';
|
|
222
227
|
|
|
@@ -266,6 +271,15 @@ class Generator extends EventEmitter {
|
|
|
266
271
|
|
|
267
272
|
this.appname = this.determineAppname();
|
|
268
273
|
this.config = this._getStorage();
|
|
274
|
+
if (this._namespaceId && this._namespaceId.generator) {
|
|
275
|
+
this.generatorConfig = this.config.createStorage(`:${this._namespaceId.generator}`);
|
|
276
|
+
if (this._namespaceId.instanceId) {
|
|
277
|
+
this.instanceConfig = this.generatorConfig.createStorage(
|
|
278
|
+
`#${this._namespaceId.instanceId}`
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
269
283
|
this._globalConfig = this._getGlobalStorage();
|
|
270
284
|
|
|
271
285
|
// Ensure source/destination path, can be configured from subclasses
|
|
@@ -368,6 +382,11 @@ class Generator extends EventEmitter {
|
|
|
368
382
|
this.env.runLoop.addSubQueue(customQueue.queueName, beforeQueue);
|
|
369
383
|
});
|
|
370
384
|
}
|
|
385
|
+
|
|
386
|
+
this.compose = this.options.compose;
|
|
387
|
+
|
|
388
|
+
// Expose utilities for dependency-less generators.
|
|
389
|
+
this._ = _;
|
|
371
390
|
}
|
|
372
391
|
|
|
373
392
|
checkEnvironmentVersion(packageDependency, version) {
|
|
@@ -423,6 +442,48 @@ class Generator extends EventEmitter {
|
|
|
423
442
|
this._debug(...args);
|
|
424
443
|
}
|
|
425
444
|
|
|
445
|
+
/**
|
|
446
|
+
* Register stored config prompts and optional option alternative.
|
|
447
|
+
*
|
|
448
|
+
* @param {Inquirer|Inquirer[]} questions - Inquirer question or questions.
|
|
449
|
+
* @param {Object|Boolean} [questions.exportOption] - Additional data to export this question as an option.
|
|
450
|
+
* @param {Storage|String} [question.storage=this.config] - Storage to store the answers.
|
|
451
|
+
*/
|
|
452
|
+
registerConfigPrompts(questions) {
|
|
453
|
+
questions = Array.isArray(questions) ? questions : [questions];
|
|
454
|
+
const getOptionTypeFromInquirerType = type => {
|
|
455
|
+
if (type === 'number') {
|
|
456
|
+
return Number;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (type === 'confirm') {
|
|
460
|
+
return Boolean;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
if (type === 'checkbox') {
|
|
464
|
+
return Array;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return String;
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
questions.forEach(q => {
|
|
471
|
+
const question = { ...q };
|
|
472
|
+
if (q.exportOption) {
|
|
473
|
+
let option = typeof q.exportOption === 'boolean' ? {} : q.exportOption;
|
|
474
|
+
this.option({
|
|
475
|
+
name: q.name,
|
|
476
|
+
type: getOptionTypeFromInquirerType(q.type),
|
|
477
|
+
description: q.message,
|
|
478
|
+
...option,
|
|
479
|
+
storage: q.storage || this.config
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
this._prompts.push(question);
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
|
|
426
487
|
/**
|
|
427
488
|
* Prompt user to answer questions. The signature of this method is the same as {@link https://github.com/SBoudrias/Inquirer.js Inquirer.js}
|
|
428
489
|
*
|
|
@@ -430,17 +491,79 @@ class Generator extends EventEmitter {
|
|
|
430
491
|
* every question descriptor. When set to true, Yeoman will store/fetch the
|
|
431
492
|
* user's answers as defaults.
|
|
432
493
|
*
|
|
433
|
-
* @param {
|
|
494
|
+
* @param {object|object[]} questions Array of question descriptor objects. See {@link https://github.com/SBoudrias/Inquirer.js/blob/master/README.md Documentation}
|
|
495
|
+
* @param {Storage} [questions.storage] Store/fetch the question on the storage.
|
|
496
|
+
* @param {Storage} [storage] Storage object
|
|
434
497
|
* @return {Promise} prompt promise
|
|
435
498
|
*/
|
|
436
|
-
prompt(questions) {
|
|
499
|
+
prompt(questions, storage) {
|
|
500
|
+
const checkInquirer = () => {
|
|
501
|
+
if (this.inquireSupportsPrefilled === undefined) {
|
|
502
|
+
this.checkEnvironmentVersion();
|
|
503
|
+
this.inquireSupportsPrefilled = this.checkEnvironmentVersion('inquirer', '7.1.0');
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
if (storage !== undefined) {
|
|
508
|
+
checkInquirer();
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const storageForQuestion = {};
|
|
512
|
+
|
|
513
|
+
const getAnswerFromStorage = function(question) {
|
|
514
|
+
let questionStorage = question.storage || storage;
|
|
515
|
+
questionStorage =
|
|
516
|
+
typeof questionStorage === 'string' ? this[questionStorage] : questionStorage;
|
|
517
|
+
if (questionStorage) {
|
|
518
|
+
checkInquirer();
|
|
519
|
+
|
|
520
|
+
const name = question.name;
|
|
521
|
+
storageForQuestion[name] = questionStorage;
|
|
522
|
+
const value = questionStorage.getPath(name);
|
|
523
|
+
if (value !== undefined) {
|
|
524
|
+
question.default = value;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return [name, value];
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
return undefined;
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
if (!Array.isArray(questions)) {
|
|
534
|
+
questions = [questions];
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Shows the prompt even if the answer already exists.
|
|
538
|
+
questions.forEach(question => {
|
|
539
|
+
if (question.askAnswered === undefined) {
|
|
540
|
+
question.askAnswered = this.options.askAnswered === true;
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
// Pre-fill answers with storage values.
|
|
545
|
+
const answers = {};
|
|
546
|
+
questions
|
|
547
|
+
.map(getAnswerFromStorage)
|
|
548
|
+
.filter(a => a)
|
|
549
|
+
.forEach(([key, value]) => {
|
|
550
|
+
answers[key] = value;
|
|
551
|
+
});
|
|
552
|
+
|
|
437
553
|
questions = promptSuggestion.prefillQuestions(this._globalConfig, questions);
|
|
438
554
|
questions = promptSuggestion.prefillQuestions(this.config, questions);
|
|
439
555
|
|
|
440
|
-
return this.env.adapter.prompt(questions).then(answers => {
|
|
556
|
+
return this.env.adapter.prompt(questions, answers).then(answers => {
|
|
557
|
+
Object.entries(storageForQuestion).forEach(([name, questionStorage]) => {
|
|
558
|
+
const answer = answers[name] === undefined ? null : answers[name];
|
|
559
|
+
questionStorage.setPath(name, answer);
|
|
560
|
+
});
|
|
561
|
+
|
|
441
562
|
if (!this.options['skip-cache'] && !this.options.skipCache) {
|
|
442
563
|
promptSuggestion.storeAnswers(this._globalConfig, questions, answers, false);
|
|
443
|
-
|
|
564
|
+
if (!this.options.skipLocalCache) {
|
|
565
|
+
promptSuggestion.storeAnswers(this.config, questions, answers, true);
|
|
566
|
+
}
|
|
444
567
|
}
|
|
445
568
|
|
|
446
569
|
return answers;
|
|
@@ -452,16 +575,29 @@ class Generator extends EventEmitter {
|
|
|
452
575
|
* generate generator usage. By default, generators get all the cli options
|
|
453
576
|
* parsed by nopt as a `this.options` hash object.
|
|
454
577
|
*
|
|
455
|
-
* @param {String} name - Option name
|
|
578
|
+
* @param {String} [name] - Option name
|
|
456
579
|
* @param {Object} config - Option options
|
|
457
580
|
* @param {any} config.type - Either Boolean, String or Number
|
|
458
581
|
* @param {string} [config.description] - Description for the option
|
|
459
582
|
* @param {any} [config.default] - Default value
|
|
460
583
|
* @param {any} [config.alias] - Option name alias (example `-h` and --help`)
|
|
461
584
|
* @param {any} [config.hide] - Boolean whether to hide from help
|
|
585
|
+
* @param {Storage} [config.storage] - Storage to persist the option
|
|
462
586
|
* @return {this} This generator
|
|
463
587
|
*/
|
|
464
588
|
option(name, config) {
|
|
589
|
+
if (Array.isArray(name)) {
|
|
590
|
+
name.forEach(option => {
|
|
591
|
+
this.option(option);
|
|
592
|
+
});
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
if (typeof name === 'object') {
|
|
597
|
+
config = name;
|
|
598
|
+
name = config.name;
|
|
599
|
+
}
|
|
600
|
+
|
|
465
601
|
config = config || {};
|
|
466
602
|
|
|
467
603
|
// Alias default to defaults for backward compatibility.
|
|
@@ -500,6 +636,12 @@ class Generator extends EventEmitter {
|
|
|
500
636
|
}
|
|
501
637
|
|
|
502
638
|
this.parseOptions();
|
|
639
|
+
if (config.storage && this.options[name] !== undefined) {
|
|
640
|
+
const storage =
|
|
641
|
+
typeof config.storage === 'string' ? this[config.storage] : config.storage;
|
|
642
|
+
storage.set(name, this.options[name]);
|
|
643
|
+
}
|
|
644
|
+
|
|
503
645
|
return this;
|
|
504
646
|
}
|
|
505
647
|
|
|
@@ -703,6 +845,84 @@ class Generator extends EventEmitter {
|
|
|
703
845
|
});
|
|
704
846
|
}
|
|
705
847
|
|
|
848
|
+
/**
|
|
849
|
+
* @private
|
|
850
|
+
* Schedule a generator's method on a run queue.
|
|
851
|
+
*
|
|
852
|
+
* @param {String} name: The method name to schedule.
|
|
853
|
+
* @param {TaskOptions} [taskOptions]: options.
|
|
854
|
+
*/
|
|
855
|
+
queueOwnTask(name, taskOptions = {}) {
|
|
856
|
+
const property = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(this), name);
|
|
857
|
+
const item = property.value ? property.value : property.get.call(this);
|
|
858
|
+
|
|
859
|
+
const priority = this._queues[name];
|
|
860
|
+
taskOptions = {
|
|
861
|
+
...priority,
|
|
862
|
+
cancellable: true,
|
|
863
|
+
run: false,
|
|
864
|
+
...taskOptions
|
|
865
|
+
};
|
|
866
|
+
|
|
867
|
+
// Name points to a function; run it!
|
|
868
|
+
if (typeof item === 'function') {
|
|
869
|
+
taskOptions.taskName = name;
|
|
870
|
+
taskOptions.method = item;
|
|
871
|
+
this.queueTask(taskOptions);
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// Not a queue hash; stop
|
|
876
|
+
if (!priority) {
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
this.queueTaskGroup(item, taskOptions);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* @private
|
|
885
|
+
* Schedule every generator's methods on a run queue.
|
|
886
|
+
*
|
|
887
|
+
* @param {TaskOptions} [taskOptions]: options.
|
|
888
|
+
*/
|
|
889
|
+
queueOwnTasks(taskOptions) {
|
|
890
|
+
this._running = true;
|
|
891
|
+
this._taskStatus = { cancelled: false, timestamp: new Date() };
|
|
892
|
+
|
|
893
|
+
const methods = Object.getOwnPropertyNames(Object.getPrototypeOf(this));
|
|
894
|
+
const validMethods = methods.filter(methodIsValid);
|
|
895
|
+
if (validMethods.length === 0 && this._prompts.length === 0) {
|
|
896
|
+
const error = new Error(
|
|
897
|
+
'This Generator is empty. Add at least one method for it to run.'
|
|
898
|
+
);
|
|
899
|
+
this.emit('error', error);
|
|
900
|
+
throw error;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
if (this._prompts.length > 0) {
|
|
904
|
+
this.queueTask({
|
|
905
|
+
method: () => this.prompt(this._prompts, this.config),
|
|
906
|
+
taskName: 'Prompt registered questions',
|
|
907
|
+
queueName: 'prompting',
|
|
908
|
+
cancellable: true
|
|
909
|
+
});
|
|
910
|
+
|
|
911
|
+
if (validMethods.length === 0) {
|
|
912
|
+
this.queueTask({
|
|
913
|
+
method: () => {
|
|
914
|
+
this.renderTemplate();
|
|
915
|
+
},
|
|
916
|
+
taskName: 'Empty generator: copy templates',
|
|
917
|
+
queueName: 'writing',
|
|
918
|
+
cancellable: true
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
validMethods.forEach(methodName => this.queueOwnTask(methodName, taskOptions));
|
|
924
|
+
}
|
|
925
|
+
|
|
706
926
|
/**
|
|
707
927
|
* Schedule tasks on a run queue.
|
|
708
928
|
*
|
|
@@ -730,6 +950,10 @@ class Generator extends EventEmitter {
|
|
|
730
950
|
namespace = self.options.namespace;
|
|
731
951
|
}
|
|
732
952
|
|
|
953
|
+
// Task status allows to ignore (cancel) current queued tasks.
|
|
954
|
+
// Each queueOwnTasks (complete run) create a new taskStatus.
|
|
955
|
+
const taskStatus = this._taskStatus || {};
|
|
956
|
+
|
|
733
957
|
debug(
|
|
734
958
|
`Queueing ${namespace}#${methodName} with options %o`,
|
|
735
959
|
_.omit(task, ['method'])
|
|
@@ -740,23 +964,18 @@ class Generator extends EventEmitter {
|
|
|
740
964
|
continueQueue => {
|
|
741
965
|
debug(`Running ${namespace}#${methodName}`);
|
|
742
966
|
self.emit(`method:${methodName}`);
|
|
743
|
-
const taskCancelled = task.cancellable &&
|
|
967
|
+
const taskCancelled = task.cancellable && taskStatus.cancelled;
|
|
968
|
+
if (taskCancelled) {
|
|
969
|
+
continueQueue();
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
744
972
|
|
|
745
973
|
runAsync(function() {
|
|
746
|
-
if (taskCancelled) {
|
|
747
|
-
return Promise.resolve();
|
|
748
|
-
}
|
|
749
|
-
|
|
750
974
|
self.async = () => this.async();
|
|
751
975
|
self.runningState = { namespace, queueName, methodName };
|
|
752
976
|
return method.apply(self, self.args);
|
|
753
977
|
})()
|
|
754
978
|
.then(function() {
|
|
755
|
-
if (taskCancelled) {
|
|
756
|
-
continueQueue();
|
|
757
|
-
return;
|
|
758
|
-
}
|
|
759
|
-
|
|
760
979
|
delete self.runningState;
|
|
761
980
|
const eventName = `done$${namespace || 'unknownnamespace'}#${methodName}`;
|
|
762
981
|
debug(`Emiting event ${eventName}`);
|
|
@@ -798,6 +1017,21 @@ class Generator extends EventEmitter {
|
|
|
798
1017
|
*/
|
|
799
1018
|
cancelCancellableTasks() {
|
|
800
1019
|
this._running = false;
|
|
1020
|
+
// Task status references is registered at each running task
|
|
1021
|
+
this._taskStatus.cancelled = true;
|
|
1022
|
+
// Create a new task status.
|
|
1023
|
+
delete this._taskStatus;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
/**
|
|
1027
|
+
* Start the generator again.
|
|
1028
|
+
*
|
|
1029
|
+
* @param {Object} [options]: options.
|
|
1030
|
+
*/
|
|
1031
|
+
startOver(options = {}) {
|
|
1032
|
+
this.cancelCancellableTasks();
|
|
1033
|
+
Object.assign(this.options, options);
|
|
1034
|
+
this.queueOwnTasks();
|
|
801
1035
|
}
|
|
802
1036
|
|
|
803
1037
|
/**
|
|
@@ -853,8 +1087,6 @@ class Generator extends EventEmitter {
|
|
|
853
1087
|
}
|
|
854
1088
|
|
|
855
1089
|
const promise = new Promise((resolve, reject) => {
|
|
856
|
-
const self = this;
|
|
857
|
-
this._running = true;
|
|
858
1090
|
this.debug('Generator is starting');
|
|
859
1091
|
this.emit('run');
|
|
860
1092
|
|
|
@@ -868,74 +1100,20 @@ class Generator extends EventEmitter {
|
|
|
868
1100
|
this.on('error', reject);
|
|
869
1101
|
}
|
|
870
1102
|
|
|
871
|
-
const methods = Object.getOwnPropertyNames(Object.getPrototypeOf(this));
|
|
872
|
-
const validMethods = methods.filter(methodIsValid);
|
|
873
|
-
if (!validMethods.length) {
|
|
874
|
-
return this.emit(
|
|
875
|
-
'error',
|
|
876
|
-
new Error('This Generator is empty. Add at least one method for it to run.')
|
|
877
|
-
);
|
|
878
|
-
}
|
|
879
|
-
|
|
880
1103
|
this.env.runLoop.once('end', () => {
|
|
881
1104
|
this.debug('Generator has ended');
|
|
882
1105
|
this.emit('end');
|
|
883
1106
|
resolve();
|
|
884
1107
|
});
|
|
885
1108
|
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
Object.getPrototypeOf(self),
|
|
889
|
-
name
|
|
890
|
-
);
|
|
891
|
-
const item = property.value ? property.value : property.get.call(self);
|
|
892
|
-
|
|
893
|
-
const priority = self._queues[name];
|
|
894
|
-
let taskOptions = {
|
|
895
|
-
...priority,
|
|
896
|
-
cancellable: true,
|
|
897
|
-
run: false,
|
|
898
|
-
generatorReject: usePromise ? undefined : reject
|
|
899
|
-
};
|
|
900
|
-
|
|
901
|
-
// Name points to a function; run it!
|
|
902
|
-
if (typeof item === 'function') {
|
|
903
|
-
taskOptions.taskName = name;
|
|
904
|
-
taskOptions.method = item;
|
|
905
|
-
return self.queueTask(taskOptions);
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
// Not a queue hash; stop
|
|
909
|
-
if (!priority) {
|
|
910
|
-
return;
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
self.queueTaskGroup(item, taskOptions);
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
validMethods.forEach(addInQueue);
|
|
917
|
-
|
|
918
|
-
const writeFiles = () => {
|
|
919
|
-
this.env.runLoop.add('conflicts', this._writeFiles.bind(this), {
|
|
920
|
-
once: 'write memory fs to disk'
|
|
921
|
-
});
|
|
922
|
-
};
|
|
923
|
-
|
|
924
|
-
this.env.sharedFs.on('change', writeFiles);
|
|
925
|
-
writeFiles();
|
|
926
|
-
|
|
927
|
-
// Add the default conflicts handling
|
|
928
|
-
this.env.runLoop.add('conflicts', done => {
|
|
929
|
-
this.conflicter.resolve(err => {
|
|
930
|
-
if (err) {
|
|
931
|
-
return this.emit('error', err);
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
done();
|
|
935
|
-
});
|
|
1109
|
+
this.queueOwnTasks({
|
|
1110
|
+
generatorReject: usePromise ? undefined : reject
|
|
936
1111
|
});
|
|
937
1112
|
|
|
1113
|
+
this.queueBasicTasks();
|
|
1114
|
+
|
|
938
1115
|
this._composedWith.forEach(runGenerator);
|
|
1116
|
+
this._composedWith = [];
|
|
939
1117
|
});
|
|
940
1118
|
|
|
941
1119
|
// For composed generators, otherwise error will not be catched.
|
|
@@ -948,6 +1126,31 @@ class Generator extends EventEmitter {
|
|
|
948
1126
|
return promise;
|
|
949
1127
|
}
|
|
950
1128
|
|
|
1129
|
+
/**
|
|
1130
|
+
* Queue generator's basic tasks, only once execution is required for each environment.
|
|
1131
|
+
*/
|
|
1132
|
+
queueBasicTasks() {
|
|
1133
|
+
const writeFiles = () => {
|
|
1134
|
+
this.env.runLoop.add('conflicts', this._writeFiles.bind(this), {
|
|
1135
|
+
once: 'write memory fs to disk'
|
|
1136
|
+
});
|
|
1137
|
+
};
|
|
1138
|
+
|
|
1139
|
+
this.env.sharedFs.on('change', writeFiles);
|
|
1140
|
+
writeFiles();
|
|
1141
|
+
|
|
1142
|
+
// Add the default conflicts handling
|
|
1143
|
+
this.env.runLoop.add('conflicts', done => {
|
|
1144
|
+
this.conflicter.resolve(err => {
|
|
1145
|
+
if (err) {
|
|
1146
|
+
return this.emit('error', err);
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
done();
|
|
1150
|
+
});
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
|
|
951
1154
|
/**
|
|
952
1155
|
* Compose this generator with another one.
|
|
953
1156
|
* @param {String|Object|Array} generator The path to the generator module or an object (see examples)
|
|
@@ -975,7 +1178,9 @@ class Generator extends EventEmitter {
|
|
|
975
1178
|
let instantiatedGenerator;
|
|
976
1179
|
|
|
977
1180
|
if (Array.isArray(generator)) {
|
|
978
|
-
const generators = generator.map(gen =>
|
|
1181
|
+
const generators = generator.map(gen =>
|
|
1182
|
+
this.composeWith(gen, options, returnNewGenerator)
|
|
1183
|
+
);
|
|
979
1184
|
return returnCompose(generators);
|
|
980
1185
|
}
|
|
981
1186
|
|
|
@@ -1005,6 +1210,7 @@ class Generator extends EventEmitter {
|
|
|
1005
1210
|
'skip-cache': this.options.skipCache || this.options['skip-cache'],
|
|
1006
1211
|
forceInstall: this.options.forceInstall || this.options['force-install'],
|
|
1007
1212
|
'force-install': this.options.forceInstall || this.options['force-install'],
|
|
1213
|
+
skipLocalCache: this.options.skipLocalCache,
|
|
1008
1214
|
destinationRoot: this._destinationRoot
|
|
1009
1215
|
},
|
|
1010
1216
|
options
|
|
@@ -1292,6 +1498,7 @@ class Generator extends EventEmitter {
|
|
|
1292
1498
|
_.extend(Generator.prototype, require('./actions/install'));
|
|
1293
1499
|
_.extend(Generator.prototype, require('./actions/help'));
|
|
1294
1500
|
_.extend(Generator.prototype, require('./actions/spawn-command'));
|
|
1501
|
+
_.extend(Generator.prototype, require('./actions/fs'));
|
|
1295
1502
|
Generator.prototype.user = require('./actions/user');
|
|
1296
1503
|
|
|
1297
1504
|
module.exports = Generator;
|
package/lib/util/storage.js
CHANGED
|
@@ -2,6 +2,32 @@
|
|
|
2
2
|
const assert = require('assert');
|
|
3
3
|
const _ = require('lodash');
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Proxy handler for Storage
|
|
7
|
+
*/
|
|
8
|
+
const proxyHandler = {
|
|
9
|
+
get(storage, property) {
|
|
10
|
+
return storage.get(property);
|
|
11
|
+
},
|
|
12
|
+
set(storage, property, value) {
|
|
13
|
+
storage.set(property, value);
|
|
14
|
+
return true;
|
|
15
|
+
},
|
|
16
|
+
ownKeys(storage) {
|
|
17
|
+
return Reflect.ownKeys(storage._store);
|
|
18
|
+
},
|
|
19
|
+
has(target, prop) {
|
|
20
|
+
return target.get(prop) !== undefined;
|
|
21
|
+
},
|
|
22
|
+
getOwnPropertyDescriptor(target, key) {
|
|
23
|
+
return {
|
|
24
|
+
get: () => this.get(target, key),
|
|
25
|
+
enumerable: true,
|
|
26
|
+
configurable: true
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
5
31
|
/**
|
|
6
32
|
* Storage instances handle a json file where Generator authors can store data.
|
|
7
33
|
*
|
|
@@ -11,7 +37,10 @@ const _ = require('lodash');
|
|
|
11
37
|
* @param {String} [name] The name of the new storage (this is a namespace)
|
|
12
38
|
* @param {mem-fs-editor} fs A mem-fs editor instance
|
|
13
39
|
* @param {String} configPath The filepath used as a storage.
|
|
14
|
-
* @param {
|
|
40
|
+
* @param {Object} [options] Storage options.
|
|
41
|
+
* @param {Boolean} [options.lodashPath=false] Set true to treat name as a lodash path.
|
|
42
|
+
* @param {Boolean} [options.disableCache=false] Set true to disable json object cache.
|
|
43
|
+
* @param {Boolean} [options.disableCacheByFile=false] Set true to cleanup cache for every fs change.
|
|
15
44
|
*
|
|
16
45
|
* @example
|
|
17
46
|
* class extend Generator {
|
|
@@ -21,21 +50,43 @@ const _ = require('lodash');
|
|
|
21
50
|
* }
|
|
22
51
|
*/
|
|
23
52
|
class Storage {
|
|
24
|
-
constructor(name, fs, configPath,
|
|
53
|
+
constructor(name, fs, configPath, options = {}) {
|
|
25
54
|
if (name !== undefined && typeof name !== 'string') {
|
|
26
55
|
configPath = fs;
|
|
27
56
|
fs = name;
|
|
28
57
|
name = undefined;
|
|
29
58
|
}
|
|
30
59
|
|
|
60
|
+
if (typeof options === 'boolean') {
|
|
61
|
+
options = { lodashPath: options };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
_.defaults(options, {
|
|
65
|
+
lodash: false,
|
|
66
|
+
disableCache: false,
|
|
67
|
+
disableCacheByFile: false
|
|
68
|
+
});
|
|
69
|
+
|
|
31
70
|
assert(configPath, 'A config filepath is required to create a storage');
|
|
32
71
|
|
|
33
72
|
this.path = configPath;
|
|
34
73
|
this.name = name;
|
|
35
74
|
this.fs = fs;
|
|
36
|
-
this.existed = Object.keys(this._store).length > 0;
|
|
37
75
|
this.indent = 2;
|
|
38
|
-
this.lodashPath = lodashPath;
|
|
76
|
+
this.lodashPath = options.lodashPath;
|
|
77
|
+
this.disableCache = options.disableCache;
|
|
78
|
+
this.disableCacheByFile = options.disableCacheByFile;
|
|
79
|
+
|
|
80
|
+
this.existed = Object.keys(this._store).length > 0;
|
|
81
|
+
|
|
82
|
+
this.fs.store.on('change', filename => {
|
|
83
|
+
// At mem-fs 1.1.3 filename is not passed to the event.
|
|
84
|
+
if (this.disableCacheByFile || (filename && filename !== this.path)) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
delete this._cachedStore;
|
|
89
|
+
});
|
|
39
90
|
}
|
|
40
91
|
|
|
41
92
|
/**
|
|
@@ -44,7 +95,11 @@ class Storage {
|
|
|
44
95
|
* @private
|
|
45
96
|
*/
|
|
46
97
|
get _store() {
|
|
47
|
-
const store = this.fs.readJSON(this.path, {});
|
|
98
|
+
const store = this._cachedStore || this.fs.readJSON(this.path, {});
|
|
99
|
+
if (!this.disableCache) {
|
|
100
|
+
this._cachedStore = store;
|
|
101
|
+
}
|
|
102
|
+
|
|
48
103
|
if (!this.name) {
|
|
49
104
|
return store || {};
|
|
50
105
|
}
|
|
@@ -164,6 +219,24 @@ class Storage {
|
|
|
164
219
|
this.set(val);
|
|
165
220
|
return val;
|
|
166
221
|
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Create a child storage.
|
|
225
|
+
* @param {String} path - relative path of the key to create a new storage.
|
|
226
|
+
* @return {Storage} Returns a new Storage.
|
|
227
|
+
*/
|
|
228
|
+
createStorage(path) {
|
|
229
|
+
const childName = this.name ? `${this.name}.${path}` : path;
|
|
230
|
+
return new Storage(childName, this.fs, this.path, true);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Creates a proxy object.
|
|
235
|
+
* @return {Object} proxy.
|
|
236
|
+
*/
|
|
237
|
+
createProxy() {
|
|
238
|
+
return new Proxy(this, proxyHandler);
|
|
239
|
+
}
|
|
167
240
|
}
|
|
168
241
|
|
|
169
242
|
module.exports = Storage;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yeoman-generator",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.11.0",
|
|
4
4
|
"description": "Rails-inspired generator system that provides scaffolding for your apps",
|
|
5
5
|
"homepage": "http://yeoman.io",
|
|
6
6
|
"author": "Yeoman",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"inquirer": "^6.3.1",
|
|
32
32
|
"jsdoc": "^3.6.3",
|
|
33
33
|
"lint-staged": "^8.1.7",
|
|
34
|
+
"mem-fs": "^1.2.0",
|
|
34
35
|
"mocha": "^7.1.1",
|
|
35
36
|
"mockery": "^2.1.0",
|
|
36
37
|
"nock": "^12.0.3",
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
"sinon": "^7.3.2",
|
|
41
42
|
"tui-jsdoc-template": "^1.2.2",
|
|
42
43
|
"yeoman-assert": "^3.1.1",
|
|
43
|
-
"yeoman-test": "^2.
|
|
44
|
+
"yeoman-test": "^2.6.0"
|
|
44
45
|
},
|
|
45
46
|
"license": "BSD-2-Clause",
|
|
46
47
|
"repository": "yeoman/generator",
|
|
@@ -68,6 +69,7 @@
|
|
|
68
69
|
"istextorbinary": "^2.5.1",
|
|
69
70
|
"lodash": "^4.17.11",
|
|
70
71
|
"make-dir": "^3.0.0",
|
|
72
|
+
"mem-fs-editor": "^7.0.1",
|
|
71
73
|
"minimist": "^1.2.5",
|
|
72
74
|
"pretty-bytes": "^5.2.0",
|
|
73
75
|
"read-chunk": "^3.2.0",
|
|
@@ -81,8 +83,7 @@
|
|
|
81
83
|
},
|
|
82
84
|
"optionalDependencies": {
|
|
83
85
|
"grouped-queue": "^1.1.0",
|
|
84
|
-
"
|
|
85
|
-
"yeoman-environment": "^2.3.4"
|
|
86
|
+
"yeoman-environment": "^2.9.5"
|
|
86
87
|
},
|
|
87
88
|
"lint-staged": {
|
|
88
89
|
"*.js": [
|