yeoman-environment 4.0.0-alpha.5 → 4.0.0-alpha.7
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/bin/bin.cjs +4 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +18 -15
- package/dist/cli/utils.d.ts +8 -0
- package/dist/cli/utils.js +6 -7
- package/dist/commands.d.ts +23 -0
- package/dist/commands.js +45 -0
- package/dist/commit.d.ts +17 -0
- package/dist/commit.js +22 -0
- package/dist/composed-store.d.ts +26 -0
- package/dist/composed-store.js +68 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +17 -0
- package/dist/environment-base.d.ts +259 -0
- package/dist/environment-base.js +641 -0
- package/dist/environment-full.d.ts +115 -0
- package/dist/environment-full.js +321 -0
- package/dist/generator-lookup.d.ts +56 -0
- package/dist/generator-lookup.js +97 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +9 -5
- package/dist/module-lookup.d.ts +59 -0
- package/dist/module-lookup.js +227 -0
- package/dist/package-manager.d.ts +18 -0
- package/dist/package-manager.js +52 -69
- package/dist/store.d.ts +66 -0
- package/dist/store.js +76 -47
- package/dist/util/command.d.ts +34 -0
- package/dist/util/command.js +32 -17
- package/dist/util/namespace.d.ts +26 -0
- package/dist/util/namespace.js +56 -190
- package/dist/util/resolve.d.ts +6 -0
- package/dist/util/resolve.js +40 -0
- package/dist/util/util.d.ts +7 -0
- package/dist/util/util.js +24 -23
- package/package.json +20 -33
- package/readme.md +2 -3
- package/dist/adapter.js +0 -97
- package/dist/adapter.js.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/utils.js.map +0 -1
- package/dist/command.js +0 -74
- package/dist/command.js.map +0 -1
- package/dist/composability.js +0 -78
- package/dist/composability.js.map +0 -1
- package/dist/environment.js +0 -1221
- package/dist/environment.js.map +0 -1
- package/dist/generator-features.js +0 -69
- package/dist/generator-features.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/namespace-composability.js +0 -340
- package/dist/namespace-composability.js.map +0 -1
- package/dist/package-manager.js.map +0 -1
- package/dist/resolver.js +0 -421
- package/dist/resolver.js.map +0 -1
- package/dist/spawn-command.js +0 -30
- package/dist/spawn-command.js.map +0 -1
- package/dist/store.js.map +0 -1
- package/dist/util/binary-diff.js +0 -36
- package/dist/util/binary-diff.js.map +0 -1
- package/dist/util/command.js.map +0 -1
- package/dist/util/conflicter.js +0 -346
- package/dist/util/conflicter.js.map +0 -1
- package/dist/util/esm.js +0 -22
- package/dist/util/esm.js.map +0 -1
- package/dist/util/log.js +0 -165
- package/dist/util/log.js.map +0 -1
- package/dist/util/namespace.js.map +0 -1
- package/dist/util/repository.js +0 -223
- package/dist/util/repository.js.map +0 -1
- package/dist/util/transform.js +0 -149
- package/dist/util/transform.js.map +0 -1
- package/dist/util/util.js.map +0 -1
package/dist/environment.js
DELETED
|
@@ -1,1221 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path, { isAbsolute } from 'node:path';
|
|
3
|
-
import EventEmitter from 'node:events';
|
|
4
|
-
import crypto from 'node:crypto';
|
|
5
|
-
import { pathToFileURL } from 'node:url';
|
|
6
|
-
import { createRequire } from 'node:module';
|
|
7
|
-
import process from 'node:process';
|
|
8
|
-
import chalk from 'chalk';
|
|
9
|
-
import _, { defaults, findLast, last, pick, uniq } from 'lodash-es';
|
|
10
|
-
import GroupedQueue from 'grouped-queue';
|
|
11
|
-
import escapeStrRe from 'escape-string-regexp';
|
|
12
|
-
import untildify from 'untildify';
|
|
13
|
-
import { create as createMemFs } from 'mem-fs';
|
|
14
|
-
import { create as createMemFsEditor } from 'mem-fs-editor';
|
|
15
|
-
import createdLogger from 'debug';
|
|
16
|
-
import isScoped from 'is-scoped';
|
|
17
|
-
import npmlog from 'npmlog';
|
|
18
|
-
import slash from 'slash';
|
|
19
|
-
import { TrackerGroup } from 'are-we-there-yet';
|
|
20
|
-
import { pipeline, transform } from 'p-transform';
|
|
21
|
-
// eslint-disable-next-line n/file-extension-in-import
|
|
22
|
-
import { isFilePending } from 'mem-fs-editor/state';
|
|
23
|
-
// eslint-disable-next-line n/file-extension-in-import
|
|
24
|
-
import { createCommitTransform } from 'mem-fs-editor/transform';
|
|
25
|
-
import Store from './store.js';
|
|
26
|
-
import composability from './composability.js';
|
|
27
|
-
import resolver from './resolver.js';
|
|
28
|
-
import TerminalAdapter from './adapter.js';
|
|
29
|
-
import YeomanRepository from './util/repository.js';
|
|
30
|
-
import Conflicter from './util/conflicter.js';
|
|
31
|
-
import YeomanCommand from './util/command.js';
|
|
32
|
-
import { toNamespace } from './util/namespace.js';
|
|
33
|
-
import { createConflicterCheckTransform, createConflicterStatusTransform, createYoRcTransform, createYoResolveTransform, } from './util/transform.js';
|
|
34
|
-
import commandMixin from './command.js';
|
|
35
|
-
import generatorFeaturesMixin from './generator-features.js';
|
|
36
|
-
import packageManagerMixin from './package-manager.js';
|
|
37
|
-
import spawnCommandMixin from './spawn-command.js';
|
|
38
|
-
import namespaceCompasibilityMixin from './namespace-composability.js';
|
|
39
|
-
// eslint-disable-next-line import/order
|
|
40
|
-
import { requireOrImport } from './util/esm.js';
|
|
41
|
-
const debug = createdLogger('yeoman:environment');
|
|
42
|
-
const require = createRequire(import.meta.url);
|
|
43
|
-
const ENVIRONMENT_VERSION = require('../package.json').version;
|
|
44
|
-
/**
|
|
45
|
-
* Two-step argument splitting function that first splits arguments in quotes,
|
|
46
|
-
* and then splits up the remaining arguments if they are not part of a quote.
|
|
47
|
-
*/
|
|
48
|
-
function splitArgsFromString(argsString) {
|
|
49
|
-
let result = [];
|
|
50
|
-
if (!argsString) {
|
|
51
|
-
return result;
|
|
52
|
-
}
|
|
53
|
-
const quoteSeparatedArgs = argsString.split(/("[^"]*")/).filter(Boolean);
|
|
54
|
-
for (const arg of quoteSeparatedArgs) {
|
|
55
|
-
if (arg.match('\u0022')) {
|
|
56
|
-
result.push(arg.replace(/"/g, ''));
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
result = result.concat(arg.trim().split(' '));
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return result;
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Hint of generator module name
|
|
66
|
-
*/
|
|
67
|
-
function getGeneratorHint(namespace) {
|
|
68
|
-
if (isScoped(namespace)) {
|
|
69
|
-
const splitName = namespace.split('/');
|
|
70
|
-
return `${splitName[0]}/generator-${splitName[1]}`;
|
|
71
|
-
}
|
|
72
|
-
return `generator-${namespace}`;
|
|
73
|
-
}
|
|
74
|
-
const mixins = [commandMixin, generatorFeaturesMixin, packageManagerMixin];
|
|
75
|
-
const Base = mixins.reduce((a, b) => b(a), EventEmitter);
|
|
76
|
-
class Environment extends Base {
|
|
77
|
-
static get UNKNOWN_NAMESPACE() {
|
|
78
|
-
return 'unknownnamespace';
|
|
79
|
-
}
|
|
80
|
-
static get UNKNOWN_RESOLVED() {
|
|
81
|
-
return 'unknown';
|
|
82
|
-
}
|
|
83
|
-
static get queues() {
|
|
84
|
-
return [
|
|
85
|
-
'environment:run',
|
|
86
|
-
'initializing',
|
|
87
|
-
'prompting',
|
|
88
|
-
'configuring',
|
|
89
|
-
'default',
|
|
90
|
-
'writing',
|
|
91
|
-
'transform',
|
|
92
|
-
'conflicts',
|
|
93
|
-
'environment:conflicts',
|
|
94
|
-
'install',
|
|
95
|
-
'end',
|
|
96
|
-
];
|
|
97
|
-
}
|
|
98
|
-
static get lookups() {
|
|
99
|
-
return ['.', 'generators', 'lib/generators', 'dist/generators'];
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Make sure the Environment present expected methods if an old version is
|
|
103
|
-
* passed to a Generator.
|
|
104
|
-
* @param {Environment} env
|
|
105
|
-
* @return {Environment} The updated env
|
|
106
|
-
*/
|
|
107
|
-
static enforceUpdate(env) {
|
|
108
|
-
if (!env.adapter) {
|
|
109
|
-
env.adapter = new TerminalAdapter();
|
|
110
|
-
}
|
|
111
|
-
if (!env.runLoop) {
|
|
112
|
-
env.runLoop = new GroupedQueue(Environment.queues, false);
|
|
113
|
-
}
|
|
114
|
-
if (!env.sharedFs) {
|
|
115
|
-
env.sharedFs = createMemFs();
|
|
116
|
-
}
|
|
117
|
-
if (!env.fs) {
|
|
118
|
-
env.fs = createMemFsEditor(env.sharedFs);
|
|
119
|
-
}
|
|
120
|
-
return env;
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Prepare a commander instance for cli support.
|
|
124
|
-
*
|
|
125
|
-
* @param {Class} GeneratorClass - Generator to create Command
|
|
126
|
-
* @return {Command} Return a Command instance
|
|
127
|
-
*/
|
|
128
|
-
static prepareCommand(GeneratorClass, command = new YeomanCommand()) {
|
|
129
|
-
command = Base.addEnvironmentOptions(command);
|
|
130
|
-
return Environment.prepareGeneratorCommand(command, GeneratorClass);
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Prepare a commander instance for cli support.
|
|
134
|
-
*
|
|
135
|
-
* @param {Command} command - Command to be prepared
|
|
136
|
-
* @param {Class} GeneratorClass - Generator to create Command
|
|
137
|
-
* @return {Command} return command
|
|
138
|
-
*/
|
|
139
|
-
static prepareGeneratorCommand(command, GeneratorClass, namespace) {
|
|
140
|
-
const generator = new GeneratorClass([], { help: true, env: {} });
|
|
141
|
-
Base.addGeneratorOptions(command, generator);
|
|
142
|
-
command.action(async function () {
|
|
143
|
-
let rootCommand = this;
|
|
144
|
-
while (rootCommand.parent) {
|
|
145
|
-
rootCommand = rootCommand.parent;
|
|
146
|
-
}
|
|
147
|
-
command.env = await Environment.createEnv(rootCommand.opts());
|
|
148
|
-
rootCommand.emit('yeoman:environment', command.env);
|
|
149
|
-
if (namespace) {
|
|
150
|
-
await command.env.run([namespace, ...(this.args || [])], this.opts());
|
|
151
|
-
return command.env;
|
|
152
|
-
}
|
|
153
|
-
const generator = await command.env.instantiate(GeneratorClass, this.args, this.opts());
|
|
154
|
-
await command.env.queueGenerator(generator);
|
|
155
|
-
await command.env.start();
|
|
156
|
-
return command.env;
|
|
157
|
-
});
|
|
158
|
-
return command;
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* Factory method to create an environment instance. Take same parameters as the
|
|
162
|
-
* Environment constructor.
|
|
163
|
-
*
|
|
164
|
-
* @deprecated
|
|
165
|
-
* @param {string[]} [args] - arguments.
|
|
166
|
-
* @param {object} [options] - Environment options.
|
|
167
|
-
* @param {Adapter} [adapter] - Terminal adapter.
|
|
168
|
-
*
|
|
169
|
-
* @return {Environment} a new Environment instance
|
|
170
|
-
*/
|
|
171
|
-
static createEnv(args, options, adapter) {
|
|
172
|
-
if (args && !Array.isArray(args)) {
|
|
173
|
-
options = args;
|
|
174
|
-
}
|
|
175
|
-
options = options || {};
|
|
176
|
-
return new Environment(options, adapter);
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Factory method to create an environment instance. Take same parameters as the
|
|
180
|
-
* Environment constructor.
|
|
181
|
-
*
|
|
182
|
-
* @param {String} version - Version of the Environment
|
|
183
|
-
* @param {...any} args - Same arguments as {@link Environment}#createEnv.
|
|
184
|
-
* @return {Environment} a new Environment instance
|
|
185
|
-
*/
|
|
186
|
-
static async createEnvWithVersion(version, ...args) {
|
|
187
|
-
const repository = new YeomanRepository();
|
|
188
|
-
const installedVersion = repository.verifyInstalledVersion('yeoman-environment', version);
|
|
189
|
-
if (!installedVersion) {
|
|
190
|
-
await repository.installPackage('yeoman-environment', version);
|
|
191
|
-
}
|
|
192
|
-
const VersionedEnvironment = repository.requireModule('yeoman-environment', version);
|
|
193
|
-
return VersionedEnvironment.createEnv(...args);
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Convert a generators namespace to its name
|
|
197
|
-
*
|
|
198
|
-
* @param {String} namespace
|
|
199
|
-
* @return {String}
|
|
200
|
-
*/
|
|
201
|
-
static namespaceToName(namespace) {
|
|
202
|
-
return namespace.split(':')[0];
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Lookup for a specific generator.
|
|
206
|
-
*
|
|
207
|
-
* @param {String} namespace
|
|
208
|
-
* @param {Object} [options]
|
|
209
|
-
* @param {Boolean} [options.localOnly=false] - Set true to skip lookups of
|
|
210
|
-
* globally-installed generators.
|
|
211
|
-
* @param {Boolean} [options.packagePath=false] - Set true to return the package
|
|
212
|
-
* path instead of generators file.
|
|
213
|
-
* @param {Boolean} [options.singleResult=true] - Set false to return multiple values.
|
|
214
|
-
* @return {String} generator
|
|
215
|
-
*/
|
|
216
|
-
static lookupGenerator(namespace, options) {
|
|
217
|
-
options =
|
|
218
|
-
typeof options === 'boolean'
|
|
219
|
-
? { singleResult: true, localOnly: options }
|
|
220
|
-
: { singleResult: !(options && options.multiple), ...options };
|
|
221
|
-
options.filePatterns = options.filePatterns || Environment.lookups.map(prefix => path.join(prefix, '*/index.{js,ts}'));
|
|
222
|
-
const name = Environment.namespaceToName(namespace);
|
|
223
|
-
options.packagePatterns = options.packagePatterns || getGeneratorHint(name);
|
|
224
|
-
const envProt = Environment.prototype;
|
|
225
|
-
options.npmPaths = options.npmPaths || envProt.getNpmPaths(options.localOnly).reverse();
|
|
226
|
-
options.packagePatterns = options.packagePatterns || 'generator-*';
|
|
227
|
-
options.packagePaths = options.packagePaths || resolver.packageLookup.findPackagesIn(options.npmPaths, options.packagePatterns);
|
|
228
|
-
let paths = options.singleResult ? undefined : [];
|
|
229
|
-
resolver.packageLookup.sync(options, module => {
|
|
230
|
-
const filename = module.filePath;
|
|
231
|
-
const fileNS = envProt.namespace(filename, Environment.lookups);
|
|
232
|
-
if (namespace === fileNS || (options.packagePath && namespace === Environment.namespaceToName(fileNS))) {
|
|
233
|
-
// Version 2.6.0 returned pattern instead of modulePath for options.packagePath
|
|
234
|
-
const returnPath = options.packagePath
|
|
235
|
-
? module.packagePath
|
|
236
|
-
: options.generatorPath
|
|
237
|
-
? path.posix.join(filename, '../../')
|
|
238
|
-
: filename;
|
|
239
|
-
if (options.singleResult) {
|
|
240
|
-
paths = returnPath;
|
|
241
|
-
return true;
|
|
242
|
-
}
|
|
243
|
-
paths.push(returnPath);
|
|
244
|
-
}
|
|
245
|
-
return false;
|
|
246
|
-
});
|
|
247
|
-
if (options.singleResult) {
|
|
248
|
-
return paths && isAbsolute(paths) ? pathToFileURL(paths).toString() : paths;
|
|
249
|
-
}
|
|
250
|
-
return paths.map(gen => (isAbsolute(gen) ? pathToFileURL(gen).toString() : gen));
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* @classdesc `Environment` object is responsible of handling the lifecyle and bootstrap
|
|
254
|
-
* of generators in a specific environment (your app).
|
|
255
|
-
*
|
|
256
|
-
* It provides a high-level API to create and run generators, as well as further
|
|
257
|
-
* tuning where and how a generator is resolved.
|
|
258
|
-
*
|
|
259
|
-
* An environment is created using a list of `arguments` and a Hash of
|
|
260
|
-
* `options`. Usually, this is the list of arguments you get back from your CLI
|
|
261
|
-
* options parser.
|
|
262
|
-
*
|
|
263
|
-
* An optional adapter can be passed to provide interaction in non-CLI environment
|
|
264
|
-
* (e.g. IDE plugins), otherwise a `TerminalAdapter` is instantiated by default
|
|
265
|
-
*
|
|
266
|
-
* @constructor
|
|
267
|
-
* @implements {import('@yeoman/types').BaseEnvironment}
|
|
268
|
-
* @mixes env/resolver
|
|
269
|
-
* @mixes env/composability
|
|
270
|
-
* @param {String|Array} args
|
|
271
|
-
* @param {Object} opts
|
|
272
|
-
* @param {Boolean} [opts.experimental]
|
|
273
|
-
* @param {Object} [opts.sharedOptions]
|
|
274
|
-
* @param {Console} [opts.console]
|
|
275
|
-
* @param {Stream} [opts.stdin]
|
|
276
|
-
* @param {Stream} [opts.stdout]
|
|
277
|
-
* @param {Stream} [opts.stderr]
|
|
278
|
-
* @param {TerminalAdapter} [adapter] - A TerminalAdapter instance or another object
|
|
279
|
-
* implementing this adapter interface. This is how
|
|
280
|
-
* you'd interface Yeoman with a GUI or an editor.
|
|
281
|
-
*/
|
|
282
|
-
constructor(options, adapter) {
|
|
283
|
-
super();
|
|
284
|
-
this.setMaxListeners(100);
|
|
285
|
-
this.options = options || {};
|
|
286
|
-
this.adapter =
|
|
287
|
-
adapter ||
|
|
288
|
-
new TerminalAdapter({
|
|
289
|
-
console: this.options.console,
|
|
290
|
-
stdin: this.options.stdin,
|
|
291
|
-
stderr: this.options.stderr,
|
|
292
|
-
});
|
|
293
|
-
this.cwd = this.options.cwd || process.cwd();
|
|
294
|
-
this.cwd = path.resolve(this.cwd);
|
|
295
|
-
this.logCwd = this.options.logCwd || this.cwd;
|
|
296
|
-
this.store = new Store();
|
|
297
|
-
this.command = this.options.command;
|
|
298
|
-
this.runLoop = new GroupedQueue(Environment.queues, false);
|
|
299
|
-
this.sharedFs = this.options.sharedFs || createMemFs();
|
|
300
|
-
// Each composed generator might set listeners on these shared resources. Let's make sure
|
|
301
|
-
// Node won't complain about event listeners leaks.
|
|
302
|
-
this.runLoop.setMaxListeners(0);
|
|
303
|
-
this.sharedFs.setMaxListeners(0);
|
|
304
|
-
this.fs = createMemFsEditor(this.sharedFs);
|
|
305
|
-
this.lookups = Environment.lookups;
|
|
306
|
-
this.aliases = [];
|
|
307
|
-
this.alias(/^([^:]+)$/, '$1:app');
|
|
308
|
-
// Used sharedOptions from options if exists.
|
|
309
|
-
this.sharedOptions = this.options.sharedOptions || {};
|
|
310
|
-
// Remove Unecessary sharedOptions from options
|
|
311
|
-
delete this.options.sharedOptions;
|
|
312
|
-
// Create a default sharedData.
|
|
313
|
-
this.sharedOptions.sharedData = this.sharedOptions.sharedData || {};
|
|
314
|
-
// Pass forwardErrorToEnvironment to generators.
|
|
315
|
-
this.sharedOptions.forwardErrorToEnvironment = false;
|
|
316
|
-
this.repository = new YeomanRepository({
|
|
317
|
-
repositoryPath: this.options.yeomanRepository,
|
|
318
|
-
arboristRegistry: this.options.arboristRegistry,
|
|
319
|
-
});
|
|
320
|
-
if (!this.options.experimental) {
|
|
321
|
-
for (const value of process.argv) {
|
|
322
|
-
if (value === '--experimental') {
|
|
323
|
-
this.options.experimental = true;
|
|
324
|
-
debug('Set environment as experimental');
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
this.loadSharedOptions(this.options);
|
|
329
|
-
if (this.sharedOptions.skipLocalCache === undefined) {
|
|
330
|
-
this.sharedOptions.skipLocalCache = true;
|
|
331
|
-
}
|
|
332
|
-
// Store the generators by paths and uniqueBy feature.
|
|
333
|
-
this._generatorsForPath = {};
|
|
334
|
-
this._generators = {};
|
|
335
|
-
// Store the YeomanCompose by paths and uniqueBy feature.
|
|
336
|
-
this._composeStore = {};
|
|
337
|
-
this.enableConflicterIgnore = true;
|
|
338
|
-
}
|
|
339
|
-
/**
|
|
340
|
-
* Load options passed to the Generator that should be used by the Environment.
|
|
341
|
-
*
|
|
342
|
-
* @param {Object} options
|
|
343
|
-
*/
|
|
344
|
-
loadEnvironmentOptions(options) {
|
|
345
|
-
const environmentOptions = pick(options, ['skipInstall', 'nodePackageManager']);
|
|
346
|
-
defaults(this.options, environmentOptions);
|
|
347
|
-
return environmentOptions;
|
|
348
|
-
}
|
|
349
|
-
/**
|
|
350
|
-
* Load options passed to the Environment that should be forwarded to the Generator.
|
|
351
|
-
*
|
|
352
|
-
* @param {Object} options
|
|
353
|
-
*/
|
|
354
|
-
loadSharedOptions(options) {
|
|
355
|
-
const optionsToShare = pick(options, [
|
|
356
|
-
'skipInstall',
|
|
357
|
-
'forceInstall',
|
|
358
|
-
'skipCache',
|
|
359
|
-
'skipLocalCache',
|
|
360
|
-
'skipParseOptions',
|
|
361
|
-
'localConfigOnly',
|
|
362
|
-
'askAnswered',
|
|
363
|
-
]);
|
|
364
|
-
Object.assign(this.sharedOptions, optionsToShare);
|
|
365
|
-
return optionsToShare;
|
|
366
|
-
}
|
|
367
|
-
/**
|
|
368
|
-
* @deprecated
|
|
369
|
-
* Error handler taking `err` instance of Error.
|
|
370
|
-
*
|
|
371
|
-
* The `error` event is emitted with the error object, if no `error` listener
|
|
372
|
-
* is registered, then we throw the error.
|
|
373
|
-
*
|
|
374
|
-
* @param {Object} err
|
|
375
|
-
* @return {Error} err
|
|
376
|
-
*/
|
|
377
|
-
error(error) {
|
|
378
|
-
throw error instanceof Error ? error : new Error(error);
|
|
379
|
-
}
|
|
380
|
-
/**
|
|
381
|
-
* Outputs the general help and usage. Optionally, if generators have been
|
|
382
|
-
* registered, the list of available generators is also displayed.
|
|
383
|
-
*
|
|
384
|
-
* @param {String} name
|
|
385
|
-
*/
|
|
386
|
-
help(name = 'init') {
|
|
387
|
-
const out = [
|
|
388
|
-
'Usage: :binary: GENERATOR [args] [options]',
|
|
389
|
-
'',
|
|
390
|
-
'General options:',
|
|
391
|
-
" --help # Print generator's options and usage",
|
|
392
|
-
' -f, --force # Overwrite files that already exist',
|
|
393
|
-
'',
|
|
394
|
-
'Please choose a generator below.',
|
|
395
|
-
'',
|
|
396
|
-
];
|
|
397
|
-
const ns = this.namespaces();
|
|
398
|
-
const groups = {};
|
|
399
|
-
for (const namespace of ns) {
|
|
400
|
-
const base = namespace.split(':')[0];
|
|
401
|
-
if (!groups[base]) {
|
|
402
|
-
groups[base] = [];
|
|
403
|
-
}
|
|
404
|
-
groups[base].push(namespace);
|
|
405
|
-
}
|
|
406
|
-
for (const key of Object.keys(groups).sort()) {
|
|
407
|
-
const group = groups[key];
|
|
408
|
-
if (group.length > 0) {
|
|
409
|
-
out.push('', key.charAt(0).toUpperCase() + key.slice(1));
|
|
410
|
-
}
|
|
411
|
-
for (const ns of groups[key]) {
|
|
412
|
-
out.push(` ${ns}`);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
return out.join('\n').replace(/:binary:/g, name);
|
|
416
|
-
}
|
|
417
|
-
/**
|
|
418
|
-
* Registers a specific `generator` to this environment. This generator is stored under
|
|
419
|
-
* provided namespace, or a default namespace format if none if available.
|
|
420
|
-
*
|
|
421
|
-
* @param {String} name - Filepath to the a generator or a npm package name
|
|
422
|
-
* @param {String} namespace - Namespace under which register the generator (optional)
|
|
423
|
-
* @param {String} packagePath - PackagePath to the generator npm package (optional)
|
|
424
|
-
* @return {Object} environment - This environment
|
|
425
|
-
*/
|
|
426
|
-
register(name, namespace, packagePath) {
|
|
427
|
-
if (typeof name !== 'string') {
|
|
428
|
-
throw new TypeError('You must provide a generator name to register.');
|
|
429
|
-
}
|
|
430
|
-
const modulePath = this.resolveModulePath(name);
|
|
431
|
-
namespace = namespace || this.namespace(modulePath);
|
|
432
|
-
if (!namespace) {
|
|
433
|
-
throw new Error('Unable to determine namespace.');
|
|
434
|
-
}
|
|
435
|
-
// Generator is already registered and matches the current namespace.
|
|
436
|
-
if (this.store._meta[namespace] && this.store._meta[namespace].resolved === modulePath) {
|
|
437
|
-
return this;
|
|
438
|
-
}
|
|
439
|
-
this.store.add(namespace, modulePath, modulePath, packagePath);
|
|
440
|
-
const packageNS = Environment.namespaceToName(namespace);
|
|
441
|
-
this.store.addPackageNS(packageNS);
|
|
442
|
-
if (packagePath) {
|
|
443
|
-
this.store.addPackage(packageNS, packagePath);
|
|
444
|
-
}
|
|
445
|
-
debug('Registered %s (%s) on package %s (%s)', namespace, modulePath, packageNS, packagePath);
|
|
446
|
-
return this;
|
|
447
|
-
}
|
|
448
|
-
/**
|
|
449
|
-
* Register a stubbed generator to this environment. This method allow to register raw
|
|
450
|
-
* functions under the provided namespace. `registerStub` will enforce the function passed
|
|
451
|
-
* to extend the Base generator automatically.
|
|
452
|
-
*
|
|
453
|
-
* @param {Function} Generator - A Generator constructor or a simple function
|
|
454
|
-
* @param {String} namespace - Namespace under which register the generator
|
|
455
|
-
* @param {String} [resolved] - The file path to the generator
|
|
456
|
-
* @param {String} [packagePath] - The generator's package path
|
|
457
|
-
* @return {this}
|
|
458
|
-
*/
|
|
459
|
-
registerStub(Generator, namespace, resolved = Environment.UNKNOWN_RESOLVED, packagePath = undefined) {
|
|
460
|
-
if (typeof Generator !== 'function' && typeof Generator.createGenerator !== 'function') {
|
|
461
|
-
throw new TypeError('You must provide a stub function to register.');
|
|
462
|
-
}
|
|
463
|
-
if (typeof namespace !== 'string') {
|
|
464
|
-
throw new TypeError('You must provide a namespace to register.');
|
|
465
|
-
}
|
|
466
|
-
this.store.add(namespace, Generator, resolved, packagePath);
|
|
467
|
-
const packageNS = Environment.namespaceToName(namespace);
|
|
468
|
-
this.store.addPackageNS(packageNS);
|
|
469
|
-
if (packagePath) {
|
|
470
|
-
this.store.addPackage(packageNS, packagePath);
|
|
471
|
-
}
|
|
472
|
-
debug('Registered %s (%s) on package (%s)', namespace, resolved, packagePath);
|
|
473
|
-
return this;
|
|
474
|
-
}
|
|
475
|
-
/**
|
|
476
|
-
* Returns the list of registered namespace.
|
|
477
|
-
* @return {Array}
|
|
478
|
-
*/
|
|
479
|
-
namespaces() {
|
|
480
|
-
return this.store.namespaces();
|
|
481
|
-
}
|
|
482
|
-
/**
|
|
483
|
-
* Returns the environment or dependency version.
|
|
484
|
-
* @param {String} packageName - Module to get version.
|
|
485
|
-
* @return {String} Environment version.
|
|
486
|
-
*/
|
|
487
|
-
getVersion(packageName) {
|
|
488
|
-
if (packageName && packageName !== 'yeoman-environment') {
|
|
489
|
-
try {
|
|
490
|
-
return require(`${packageName}/package.json`).version;
|
|
491
|
-
}
|
|
492
|
-
catch {
|
|
493
|
-
return undefined;
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
return ENVIRONMENT_VERSION;
|
|
497
|
-
}
|
|
498
|
-
/**
|
|
499
|
-
* Returns stored generators meta
|
|
500
|
-
* @return {Object}
|
|
501
|
-
*/
|
|
502
|
-
getGeneratorsMeta() {
|
|
503
|
-
return this.store.getGeneratorsMeta();
|
|
504
|
-
}
|
|
505
|
-
/**
|
|
506
|
-
* Returns stored generators meta
|
|
507
|
-
* @param {string} namespace
|
|
508
|
-
* @return {any}
|
|
509
|
-
*/
|
|
510
|
-
getGeneratorMeta(namespace) {
|
|
511
|
-
const meta = this.store.getMeta(namespace) || this.store.getMeta(this.alias(namespace));
|
|
512
|
-
if (!meta) {
|
|
513
|
-
return;
|
|
514
|
-
}
|
|
515
|
-
const { importGenerator, resolved } = meta;
|
|
516
|
-
const importModule = async () => requireOrImport(resolved);
|
|
517
|
-
const importGeneratorClass = async () => this._findGeneratorClass(await importGenerator(), meta);
|
|
518
|
-
const instantiate = async (args, options) => this.instantiate(await importGeneratorClass(), args, options);
|
|
519
|
-
const instantiateHelp = async () => instantiate([], { help: true });
|
|
520
|
-
const newMeta = {
|
|
521
|
-
...meta,
|
|
522
|
-
importModule,
|
|
523
|
-
importGeneratorClass,
|
|
524
|
-
instantiate,
|
|
525
|
-
instantiateHelp,
|
|
526
|
-
};
|
|
527
|
-
return newMeta;
|
|
528
|
-
}
|
|
529
|
-
/**
|
|
530
|
-
* Get registered generators names
|
|
531
|
-
*
|
|
532
|
-
* @return {Array}
|
|
533
|
-
*/
|
|
534
|
-
getGeneratorNames() {
|
|
535
|
-
return uniq(Object.keys(this.getGeneratorsMeta()).map(namespace => Environment.namespaceToName(namespace)));
|
|
536
|
-
}
|
|
537
|
-
/**
|
|
538
|
-
* Verify if a package namespace already have been registered.
|
|
539
|
-
*
|
|
540
|
-
* @param {String} [packageNS] - namespace of the package.
|
|
541
|
-
* @return {boolean} - true if any generator of the package has been registered
|
|
542
|
-
*/
|
|
543
|
-
isPackageRegistered(packageNS) {
|
|
544
|
-
const registeredPackages = this.getRegisteredPackages();
|
|
545
|
-
return registeredPackages.includes(packageNS) || registeredPackages.includes(this.alias(packageNS).split(':', 2)[0]);
|
|
546
|
-
}
|
|
547
|
-
/**
|
|
548
|
-
* Get all registered packages namespaces.
|
|
549
|
-
*
|
|
550
|
-
* @return {Array} - array of namespaces.
|
|
551
|
-
*/
|
|
552
|
-
getRegisteredPackages() {
|
|
553
|
-
return this.store.getPackagesNS();
|
|
554
|
-
}
|
|
555
|
-
/**
|
|
556
|
-
* Get last added path for a namespace
|
|
557
|
-
*
|
|
558
|
-
* @param {String} - namespace
|
|
559
|
-
* @return {String} - path of the package
|
|
560
|
-
*/
|
|
561
|
-
async getPackagePath(namespace) {
|
|
562
|
-
if (namespace.includes(':')) {
|
|
563
|
-
const generator = (await this.get(namespace)) || {};
|
|
564
|
-
return generator.packagePath;
|
|
565
|
-
}
|
|
566
|
-
const packagePaths = this.getPackagePaths(namespace) || [];
|
|
567
|
-
return packagePaths[0];
|
|
568
|
-
}
|
|
569
|
-
/**
|
|
570
|
-
* Get paths for a namespace
|
|
571
|
-
*
|
|
572
|
-
* @param {String} - namespace
|
|
573
|
-
* @return {Array} - array of paths.
|
|
574
|
-
*/
|
|
575
|
-
getPackagePaths(namespace) {
|
|
576
|
-
return this.store.getPackagesPaths()[namespace] || this.store.getPackagesPaths()[Environment.namespaceToName(this.alias(namespace))];
|
|
577
|
-
}
|
|
578
|
-
/**
|
|
579
|
-
* Get a single generator from the registered list of generators. The lookup is
|
|
580
|
-
* based on generator's namespace, "walking up" the namespaces until a matching
|
|
581
|
-
* is found. Eg. if an `angular:common` namespace is registered, and we try to
|
|
582
|
-
* get `angular:common:all` then we get `angular:common` as a fallback (unless
|
|
583
|
-
* an `angular:common:all` generator is registered).
|
|
584
|
-
*
|
|
585
|
-
* @param {String} namespaceOrPath
|
|
586
|
-
* @return {import('@yeoman/api').BaseGenerator|Promise<import('@yeoman/api').BaseGenerator>|null} - the generator registered under the namespace
|
|
587
|
-
*/
|
|
588
|
-
async get(namespaceOrPath) {
|
|
589
|
-
// Stop the recursive search if nothing is left
|
|
590
|
-
if (!namespaceOrPath) {
|
|
591
|
-
return;
|
|
592
|
-
}
|
|
593
|
-
const parsed = toNamespace(namespaceOrPath);
|
|
594
|
-
if (parsed && this.getByNamespace) {
|
|
595
|
-
return this.getByNamespace(parsed);
|
|
596
|
-
}
|
|
597
|
-
let namespace = namespaceOrPath;
|
|
598
|
-
// Legacy yeoman-generator `#hookFor()` function is passing the generator path as part
|
|
599
|
-
// of the namespace. If we find a path delimiter in the namespace, then ignore the
|
|
600
|
-
// last part of the namespace.
|
|
601
|
-
const parts = namespaceOrPath.split(':');
|
|
602
|
-
const maybePath = last(parts);
|
|
603
|
-
if (parts.length > 1 && /[/\\]/.test(maybePath)) {
|
|
604
|
-
parts.pop();
|
|
605
|
-
// We also want to remove the drive letter on windows
|
|
606
|
-
if (maybePath.includes('\\') && last(parts).length === 1) {
|
|
607
|
-
parts.pop();
|
|
608
|
-
}
|
|
609
|
-
namespace = parts.join(':');
|
|
610
|
-
}
|
|
611
|
-
const generator = (await this.store.get(namespace)) ??
|
|
612
|
-
(await this.store.get(this.alias(namespace))) ??
|
|
613
|
-
// Namespace is empty if namespaceOrPath contains a win32 absolute path of the form 'C:\path\to\generator'.
|
|
614
|
-
// for this reason we pass namespaceOrPath to the getByPath function.
|
|
615
|
-
(await this.getByPath(namespaceOrPath));
|
|
616
|
-
return this._findGeneratorClass(generator);
|
|
617
|
-
}
|
|
618
|
-
/**
|
|
619
|
-
* Get a generator by path instead of namespace.
|
|
620
|
-
* @param {String} path
|
|
621
|
-
* @return {Generator|null} - the generator found at the location
|
|
622
|
-
*/
|
|
623
|
-
async getByPath(path) {
|
|
624
|
-
if (fs.existsSync(path)) {
|
|
625
|
-
const namespace = this.namespace(path);
|
|
626
|
-
this.register(path, namespace);
|
|
627
|
-
return this.get(namespace);
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
/**
|
|
631
|
-
* Find generator's class constructor.
|
|
632
|
-
* @private
|
|
633
|
-
* @param {Object} Generator - Object containing the class.
|
|
634
|
-
* @return {Function} Generator's constructor.
|
|
635
|
-
*/
|
|
636
|
-
async _findGeneratorClass(Generator, meta = Generator) {
|
|
637
|
-
if (!Generator) {
|
|
638
|
-
return Generator;
|
|
639
|
-
}
|
|
640
|
-
if (Array.isArray(Generator)) {
|
|
641
|
-
meta = Generator[1];
|
|
642
|
-
Generator = Generator[0];
|
|
643
|
-
}
|
|
644
|
-
if (typeof Generator.default === 'function') {
|
|
645
|
-
Generator.default.resolved = meta.resolved;
|
|
646
|
-
Generator.default.namespace = meta.namespace;
|
|
647
|
-
Generator.default.packagePath = meta.packagePath;
|
|
648
|
-
return Generator.default;
|
|
649
|
-
}
|
|
650
|
-
if (typeof Generator.createGenerator === 'function') {
|
|
651
|
-
const maybeGenerator = await Generator.createGenerator(this);
|
|
652
|
-
maybeGenerator.resolved = meta.resolved;
|
|
653
|
-
maybeGenerator.namespace = meta.namespace;
|
|
654
|
-
maybeGenerator.packagePath = meta.packagePath;
|
|
655
|
-
return maybeGenerator;
|
|
656
|
-
}
|
|
657
|
-
if (typeof Generator !== 'function') {
|
|
658
|
-
throw new TypeError("The generator doesn't provides a constructor.");
|
|
659
|
-
}
|
|
660
|
-
return Generator;
|
|
661
|
-
}
|
|
662
|
-
/**
|
|
663
|
-
* Create is the Generator factory. It takes a namespace to lookup and optional
|
|
664
|
-
* hash of options, that lets you define `arguments` and `options` to
|
|
665
|
-
* instantiate the generator with.
|
|
666
|
-
*
|
|
667
|
-
* An error is raised on invalid namespace.
|
|
668
|
-
*
|
|
669
|
-
* @param {String} namespaceOrPath
|
|
670
|
-
* @param {Array} [args]
|
|
671
|
-
* @param {Object} [options]
|
|
672
|
-
* @return {Generator} The instantiated generator
|
|
673
|
-
*/
|
|
674
|
-
async create(namespaceOrPath, args, options) {
|
|
675
|
-
if (!Array.isArray(args) && typeof args === 'object') {
|
|
676
|
-
options = args.options || args;
|
|
677
|
-
args = args.arguments || args.args || [];
|
|
678
|
-
}
|
|
679
|
-
else {
|
|
680
|
-
args = Array.isArray(args) ? args : splitArgsFromString(args);
|
|
681
|
-
options = options || {};
|
|
682
|
-
}
|
|
683
|
-
const namespace = toNamespace(namespaceOrPath);
|
|
684
|
-
let maybeGenerator;
|
|
685
|
-
if (namespace && this.getByNamespace) {
|
|
686
|
-
maybeGenerator = await this.getByNamespace(namespace);
|
|
687
|
-
if (!maybeGenerator) {
|
|
688
|
-
this.lookupLocalNamespaces(namespace);
|
|
689
|
-
maybeGenerator = await this.getByNamespace(namespace);
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
const checkGenerator = Generator => {
|
|
693
|
-
if (namespace &&
|
|
694
|
-
Generator &&
|
|
695
|
-
Generator.namespace &&
|
|
696
|
-
Generator.namespace !== namespace.namespace &&
|
|
697
|
-
Generator.namespace !== Environment.UNKNOWN_NAMESPACE) {
|
|
698
|
-
// Update namespace object in case of aliased namespace.
|
|
699
|
-
try {
|
|
700
|
-
namespace.namespace = Generator.namespace;
|
|
701
|
-
}
|
|
702
|
-
catch {
|
|
703
|
-
// Invalid namespace can be aliased to a valid one.
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
if (typeof Generator !== 'function') {
|
|
707
|
-
const generatorHint = namespace ? namespace.generatorHint : getGeneratorHint(namespaceOrPath);
|
|
708
|
-
throw new Error(chalk.red("You don't seem to have a generator with the name “" + namespaceOrPath + '” installed.') +
|
|
709
|
-
'\n' +
|
|
710
|
-
'But help is on the way:\n\n' +
|
|
711
|
-
'You can see available generators via ' +
|
|
712
|
-
chalk.yellow('npm search yeoman-generator') +
|
|
713
|
-
' or via ' +
|
|
714
|
-
chalk.yellow('http://yeoman.io/generators/') +
|
|
715
|
-
'. \n' +
|
|
716
|
-
'Install them with ' +
|
|
717
|
-
chalk.yellow(`npm install ${generatorHint}`) +
|
|
718
|
-
'.\n\n' +
|
|
719
|
-
'To see all your installed generators run ' +
|
|
720
|
-
chalk.yellow('yo --generators') +
|
|
721
|
-
'. ' +
|
|
722
|
-
'Adding the ' +
|
|
723
|
-
chalk.yellow('--help') +
|
|
724
|
-
' option will also show subgenerators. \n\n' +
|
|
725
|
-
'If ' +
|
|
726
|
-
chalk.yellow('yo') +
|
|
727
|
-
' cannot find the generator, run ' +
|
|
728
|
-
chalk.yellow('yo doctor') +
|
|
729
|
-
' to troubleshoot your system.');
|
|
730
|
-
}
|
|
731
|
-
return Generator;
|
|
732
|
-
};
|
|
733
|
-
maybeGenerator = maybeGenerator || this.get(namespaceOrPath);
|
|
734
|
-
return this.instantiate(checkGenerator(await maybeGenerator), args, options);
|
|
735
|
-
}
|
|
736
|
-
/**
|
|
737
|
-
* Instantiate a Generator with metadatas
|
|
738
|
-
*
|
|
739
|
-
* @param {Class<Generator>} generator Generator class
|
|
740
|
-
* @param {Array} [args] Arguments to pass the instance
|
|
741
|
-
* @param {Object} [options] Options to pass the instance
|
|
742
|
-
* @return {Generator} The instantiated generator
|
|
743
|
-
*/
|
|
744
|
-
async instantiate(Generator, args, options) {
|
|
745
|
-
if (!Array.isArray(args) && typeof args === 'object') {
|
|
746
|
-
options = args.options || args;
|
|
747
|
-
args = args.arguments || args.args || [];
|
|
748
|
-
}
|
|
749
|
-
else {
|
|
750
|
-
args = Array.isArray(args) ? args : splitArgsFromString(args);
|
|
751
|
-
options = options || {};
|
|
752
|
-
}
|
|
753
|
-
const { namespace = Environment.UNKNOWN_NAMESPACE } = Generator;
|
|
754
|
-
const environmentOptions = {
|
|
755
|
-
env: this,
|
|
756
|
-
resolved: Generator.resolved || Environment.UNKNOWN_RESOLVED,
|
|
757
|
-
namespace,
|
|
758
|
-
};
|
|
759
|
-
const generator = new Generator(args, {
|
|
760
|
-
...this.sharedOptions,
|
|
761
|
-
...options,
|
|
762
|
-
...environmentOptions,
|
|
763
|
-
});
|
|
764
|
-
generator._environmentOptions = {
|
|
765
|
-
...this.options,
|
|
766
|
-
...this.sharedOptions,
|
|
767
|
-
...environmentOptions,
|
|
768
|
-
};
|
|
769
|
-
if (!options.help && generator._postConstruct) {
|
|
770
|
-
await generator._postConstruct();
|
|
771
|
-
}
|
|
772
|
-
return generator;
|
|
773
|
-
}
|
|
774
|
-
/**
|
|
775
|
-
* Compose with the generator.
|
|
776
|
-
*
|
|
777
|
-
* @param {String} namespaceOrPath
|
|
778
|
-
* @param {Array} [args]
|
|
779
|
-
* @param {Object} [options]
|
|
780
|
-
* @param {Boolean} [schedule]
|
|
781
|
-
* @return {Generator} The instantiated generator or the singleton instance.
|
|
782
|
-
*/
|
|
783
|
-
async composeWith(generator, args, options, schedule = true) {
|
|
784
|
-
if (typeof args === 'boolean') {
|
|
785
|
-
schedule = args;
|
|
786
|
-
args = undefined;
|
|
787
|
-
options = undefined;
|
|
788
|
-
}
|
|
789
|
-
else if (typeof options === 'boolean') {
|
|
790
|
-
schedule = options;
|
|
791
|
-
options = undefined;
|
|
792
|
-
}
|
|
793
|
-
const generatorInstance = await this.create(generator, args, options);
|
|
794
|
-
return this.queueGenerator(generatorInstance, schedule);
|
|
795
|
-
}
|
|
796
|
-
/**
|
|
797
|
-
* @private
|
|
798
|
-
*/
|
|
799
|
-
getGeneratorsForPath(generatorRoot = this.cwd) {
|
|
800
|
-
this._generatorsForPath[generatorRoot] = this._generatorsForPath[generatorRoot] || {};
|
|
801
|
-
return this._generatorsForPath[generatorRoot];
|
|
802
|
-
}
|
|
803
|
-
/**
|
|
804
|
-
* @private
|
|
805
|
-
*/
|
|
806
|
-
getGenerator(uniqueBy, generatorRoot = this.cwd) {
|
|
807
|
-
if (this._generators[uniqueBy]) {
|
|
808
|
-
return this._generators[uniqueBy];
|
|
809
|
-
}
|
|
810
|
-
return this.getGeneratorsForPath(generatorRoot)[uniqueBy];
|
|
811
|
-
}
|
|
812
|
-
/**
|
|
813
|
-
* @private
|
|
814
|
-
*/
|
|
815
|
-
getAllGenerators() {
|
|
816
|
-
return Object.fromEntries([
|
|
817
|
-
...Object.entries(this._generators),
|
|
818
|
-
...Object.entries(this._generatorsForPath).flatMap(([root, generatorStore]) => Object.entries(generatorStore).map(([namespace, generator]) => [`${root}#${namespace}`, generator])),
|
|
819
|
-
]);
|
|
820
|
-
}
|
|
821
|
-
/**
|
|
822
|
-
* @private
|
|
823
|
-
*/
|
|
824
|
-
setGenerator(uniqueBy, generator) {
|
|
825
|
-
if (generator.features && generator.features.uniqueGlobally) {
|
|
826
|
-
this._generators[uniqueBy] = generator;
|
|
827
|
-
}
|
|
828
|
-
else {
|
|
829
|
-
this.getGeneratorsForPath(generator.destinationRoot())[uniqueBy] = generator;
|
|
830
|
-
}
|
|
831
|
-
return generator;
|
|
832
|
-
}
|
|
833
|
-
/**
|
|
834
|
-
* Queue generator run (queue itself tasks).
|
|
835
|
-
*
|
|
836
|
-
* @param {Generator} generator Generator instance
|
|
837
|
-
* @param {boolean} [schedule=false] Whether to schedule the generator run.
|
|
838
|
-
* @return {Generator} The generator or singleton instance.
|
|
839
|
-
*/
|
|
840
|
-
async queueGenerator(generator, schedule = false) {
|
|
841
|
-
const generatorFeatures = generator.getFeatures ? generator.getFeatures() : {};
|
|
842
|
-
let uniqueBy;
|
|
843
|
-
let rootUniqueBy;
|
|
844
|
-
let namespaceToEmit;
|
|
845
|
-
if (generatorFeatures) {
|
|
846
|
-
uniqueBy = generatorFeatures.uniqueBy;
|
|
847
|
-
namespaceToEmit = uniqueBy;
|
|
848
|
-
if (!generatorFeatures.uniqueGlobally) {
|
|
849
|
-
rootUniqueBy = generator.destinationRoot();
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
if (!uniqueBy) {
|
|
853
|
-
const { namespace } = generator.options;
|
|
854
|
-
const instanceId = crypto.randomBytes(20).toString('hex');
|
|
855
|
-
let namespaceDefinition = toNamespace(namespace);
|
|
856
|
-
if (namespaceDefinition) {
|
|
857
|
-
namespaceDefinition = namespaceDefinition.with({ instanceId });
|
|
858
|
-
uniqueBy = namespaceDefinition.id;
|
|
859
|
-
namespaceToEmit = namespaceDefinition.namespace;
|
|
860
|
-
}
|
|
861
|
-
else {
|
|
862
|
-
uniqueBy = `${namespace}#${instanceId}`;
|
|
863
|
-
namespaceToEmit = namespace;
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
const existing = this.getGenerator(uniqueBy, rootUniqueBy);
|
|
867
|
-
if (existing) {
|
|
868
|
-
debug(`Using existing generator for namespace ${uniqueBy}`);
|
|
869
|
-
return existing;
|
|
870
|
-
}
|
|
871
|
-
this.setGenerator(uniqueBy, generator);
|
|
872
|
-
this.emit('compose', namespaceToEmit, generator);
|
|
873
|
-
this.emit(`compose:${namespaceToEmit}`, generator);
|
|
874
|
-
const runGenerator = async () => {
|
|
875
|
-
if (generator.queueTasks) {
|
|
876
|
-
// Generator > 5
|
|
877
|
-
this.once('run', () => generator.emit('run'));
|
|
878
|
-
this.once('end', () => generator.emit('end'));
|
|
879
|
-
await generator.queueTasks();
|
|
880
|
-
return;
|
|
881
|
-
}
|
|
882
|
-
if (!generator.options.forwardErrorToEnvironment) {
|
|
883
|
-
generator.on('error', error => this.emit('error', error));
|
|
884
|
-
}
|
|
885
|
-
generator.promise = generator.run();
|
|
886
|
-
};
|
|
887
|
-
if (schedule) {
|
|
888
|
-
this.queueTask('environment:run', () => runGenerator());
|
|
889
|
-
}
|
|
890
|
-
else {
|
|
891
|
-
await runGenerator();
|
|
892
|
-
}
|
|
893
|
-
return generator;
|
|
894
|
-
}
|
|
895
|
-
/**
|
|
896
|
-
* Tries to locate and run a specific generator. The lookup is done depending
|
|
897
|
-
* on the provided arguments, options and the list of registered generators.
|
|
898
|
-
*
|
|
899
|
-
* When the environment was unable to resolve a generator, an error is raised.
|
|
900
|
-
*
|
|
901
|
-
* @param {String|Array} args
|
|
902
|
-
* @param {Object} [options]
|
|
903
|
-
*/
|
|
904
|
-
async run(args, options, done) {
|
|
905
|
-
if (done || typeof options === 'function' || typeof args === 'function') {
|
|
906
|
-
throw new Error('Callback support have been removed.');
|
|
907
|
-
}
|
|
908
|
-
args = Array.isArray(args) ? args : splitArgsFromString(args);
|
|
909
|
-
options = { ...options };
|
|
910
|
-
const name = args.shift();
|
|
911
|
-
if (!name) {
|
|
912
|
-
throw new Error('Must provide at least one argument, the generator namespace to invoke.');
|
|
913
|
-
}
|
|
914
|
-
this.loadEnvironmentOptions(options);
|
|
915
|
-
const instantiateAndRun = async () => {
|
|
916
|
-
const generator = await this.create(name, args, {
|
|
917
|
-
...options,
|
|
918
|
-
initialGenerator: true,
|
|
919
|
-
});
|
|
920
|
-
if (options.help) {
|
|
921
|
-
console.log(generator.help());
|
|
922
|
-
return undefined;
|
|
923
|
-
}
|
|
924
|
-
return this.runGenerator(generator);
|
|
925
|
-
};
|
|
926
|
-
if (this.options.experimental && !this.get(name)) {
|
|
927
|
-
debug(`Generator ${name} was not found, trying to install it`);
|
|
928
|
-
try {
|
|
929
|
-
await this.prepareEnvironment(name);
|
|
930
|
-
}
|
|
931
|
-
catch { }
|
|
932
|
-
}
|
|
933
|
-
return instantiateAndRun();
|
|
934
|
-
}
|
|
935
|
-
/**
|
|
936
|
-
* Start Environment queue
|
|
937
|
-
* @param {Object} options - Conflicter options.
|
|
938
|
-
*/
|
|
939
|
-
start(options) {
|
|
940
|
-
return new Promise((resolve, reject) => {
|
|
941
|
-
if (this.conflicter === undefined) {
|
|
942
|
-
const conflicterOptions = pick(defaults({}, this.options, options), [
|
|
943
|
-
'force',
|
|
944
|
-
'bail',
|
|
945
|
-
'ignoreWhitespace',
|
|
946
|
-
'dryRun',
|
|
947
|
-
'skipYoResolve',
|
|
948
|
-
'logCwd',
|
|
949
|
-
]);
|
|
950
|
-
conflicterOptions.cwd = conflicterOptions.logCwd;
|
|
951
|
-
this.conflicter = new Conflicter(this.adapter, conflicterOptions);
|
|
952
|
-
this.queueConflicter();
|
|
953
|
-
this.queuePackageManagerInstall();
|
|
954
|
-
}
|
|
955
|
-
/*
|
|
956
|
-
* Listen to errors and reject if emmited.
|
|
957
|
-
* Some cases the generator relied at the behavior that the running process
|
|
958
|
-
* would be killed if an error is thrown to environment.
|
|
959
|
-
* Make sure to not rely on that behavior.
|
|
960
|
-
*/
|
|
961
|
-
this.on('error', error => {
|
|
962
|
-
reject(error);
|
|
963
|
-
});
|
|
964
|
-
/*
|
|
965
|
-
* For backward compatibility
|
|
966
|
-
*/
|
|
967
|
-
this.on('generator:reject', error => {
|
|
968
|
-
reject(error);
|
|
969
|
-
});
|
|
970
|
-
this.on('generator:resolve', error => {
|
|
971
|
-
resolve(error);
|
|
972
|
-
});
|
|
973
|
-
this.runLoop.on('error', error => {
|
|
974
|
-
this.emit('error', error);
|
|
975
|
-
this.adapter.close();
|
|
976
|
-
});
|
|
977
|
-
this.runLoop.on('paused', () => {
|
|
978
|
-
this.emit('paused');
|
|
979
|
-
});
|
|
980
|
-
this.once('end', () => {
|
|
981
|
-
resolve();
|
|
982
|
-
});
|
|
983
|
-
/* If runLoop has ended, the environment has ended too. */
|
|
984
|
-
this.runLoop.once('end', () => {
|
|
985
|
-
this.emit('end');
|
|
986
|
-
});
|
|
987
|
-
this.emit('run');
|
|
988
|
-
this.runLoop.start();
|
|
989
|
-
});
|
|
990
|
-
}
|
|
991
|
-
/**
|
|
992
|
-
* Convenience method to run the generator with callbackWrapper.
|
|
993
|
-
* See https://github.com/yeoman/environment/pull/101
|
|
994
|
-
*
|
|
995
|
-
* @param {Object} generator
|
|
996
|
-
*/
|
|
997
|
-
async runGenerator(generator) {
|
|
998
|
-
generator = await generator;
|
|
999
|
-
generator = await this.queueGenerator(generator);
|
|
1000
|
-
this.compatibilityMode = generator.queueTasks ? false : 'v4';
|
|
1001
|
-
this._rootGenerator = this._rootGenerator || generator;
|
|
1002
|
-
return this.start(generator.options);
|
|
1003
|
-
}
|
|
1004
|
-
/**
|
|
1005
|
-
* Get the first generator that was queued to run in this environment.
|
|
1006
|
-
*
|
|
1007
|
-
* @return {Generator} generator queued to run in this environment.
|
|
1008
|
-
*/
|
|
1009
|
-
rootGenerator() {
|
|
1010
|
-
return this._rootGenerator;
|
|
1011
|
-
}
|
|
1012
|
-
/**
|
|
1013
|
-
* Given a String `filepath`, tries to figure out the relative namespace.
|
|
1014
|
-
*
|
|
1015
|
-
* ### Examples:
|
|
1016
|
-
*
|
|
1017
|
-
* this.namespace('backbone/all/index.js');
|
|
1018
|
-
* // => backbone:all
|
|
1019
|
-
*
|
|
1020
|
-
* this.namespace('generator-backbone/model');
|
|
1021
|
-
* // => backbone:model
|
|
1022
|
-
*
|
|
1023
|
-
* this.namespace('backbone.js');
|
|
1024
|
-
* // => backbone
|
|
1025
|
-
*
|
|
1026
|
-
* this.namespace('generator-mocha/backbone/model/index.js');
|
|
1027
|
-
* // => mocha:backbone:model
|
|
1028
|
-
*
|
|
1029
|
-
* @param {String} filepath
|
|
1030
|
-
* @param {Array} lookups paths
|
|
1031
|
-
*/
|
|
1032
|
-
namespace(filepath, lookups = this.lookups) {
|
|
1033
|
-
if (!filepath) {
|
|
1034
|
-
throw new Error('Missing namespace');
|
|
1035
|
-
}
|
|
1036
|
-
// Normalize path
|
|
1037
|
-
let ns = slash(filepath);
|
|
1038
|
-
// Ignore path before latest node_modules
|
|
1039
|
-
const REPOSITORY_PATH = '/node_modules/';
|
|
1040
|
-
if (ns.includes(REPOSITORY_PATH)) {
|
|
1041
|
-
ns = ns.slice(ns.lastIndexOf(REPOSITORY_PATH) + REPOSITORY_PATH.length, ns.length);
|
|
1042
|
-
}
|
|
1043
|
-
// Cleanup extension and normalize path for differents OS
|
|
1044
|
-
const parsed = path.parse(ns);
|
|
1045
|
-
ns = parsed.dir ? `${parsed.dir}/${parsed.name}` : parsed.name;
|
|
1046
|
-
// Sort lookups by length so biggest are removed first
|
|
1047
|
-
const nsLookups = _([...lookups, '..'])
|
|
1048
|
-
.map(found => slash(found))
|
|
1049
|
-
.sortBy('length')
|
|
1050
|
-
.value()
|
|
1051
|
-
.reverse();
|
|
1052
|
-
// If `ns` contains a lookup dir in its path, remove it.
|
|
1053
|
-
for (let lookup of nsLookups) {
|
|
1054
|
-
// Only match full directory (begin with leading slash or start of input, end with trailing slash)
|
|
1055
|
-
lookup = new RegExp(`(?:/|^)${escapeStrRe(lookup)}(?=/)`, 'g');
|
|
1056
|
-
ns = ns.replace(lookup, '');
|
|
1057
|
-
}
|
|
1058
|
-
const folders = ns.split('/');
|
|
1059
|
-
const scope = findLast(folders, folder => folder.indexOf('@') === 0);
|
|
1060
|
-
// Cleanup `ns` from unwanted parts and then normalize slashes to `:`
|
|
1061
|
-
ns = ns
|
|
1062
|
-
.replace(/\/\//g, '') // Remove double `/`
|
|
1063
|
-
.replace(/(.*generator-)/, '') // Remove before `generator-`
|
|
1064
|
-
.replace(/\/(index|main)$/, '') // Remove `/index` or `/main`
|
|
1065
|
-
.replace(/^\//, '') // Remove leading `/`
|
|
1066
|
-
.replace(/\/+/g, ':'); // Replace slashes by `:`
|
|
1067
|
-
if (scope) {
|
|
1068
|
-
ns = `${scope}/${ns}`;
|
|
1069
|
-
}
|
|
1070
|
-
debug('Resolve namespaces for %s: %s', filepath, ns);
|
|
1071
|
-
return ns;
|
|
1072
|
-
}
|
|
1073
|
-
/**
|
|
1074
|
-
* Resolve a module path
|
|
1075
|
-
* @param {String} moduleId - Filepath or module name
|
|
1076
|
-
* @return {String} - The resolved path leading to the module
|
|
1077
|
-
*/
|
|
1078
|
-
resolveModulePath(moduleId) {
|
|
1079
|
-
if (moduleId[0] === '.') {
|
|
1080
|
-
moduleId = path.resolve(moduleId);
|
|
1081
|
-
}
|
|
1082
|
-
moduleId = untildify(moduleId);
|
|
1083
|
-
moduleId = path.normalize(moduleId);
|
|
1084
|
-
if (path.extname(moduleId) === '') {
|
|
1085
|
-
moduleId += path.sep;
|
|
1086
|
-
}
|
|
1087
|
-
let resolved;
|
|
1088
|
-
// Win32: moduleId is resolving as moduleId.js or moduleId.json instead of moduleId/index.js, workaround it.
|
|
1089
|
-
if (process.platform === 'win32' && path.extname(moduleId) === '') {
|
|
1090
|
-
try {
|
|
1091
|
-
resolved = require.resolve(path.join(moduleId, 'index'));
|
|
1092
|
-
}
|
|
1093
|
-
catch { }
|
|
1094
|
-
}
|
|
1095
|
-
return resolved || require.resolve(moduleId);
|
|
1096
|
-
}
|
|
1097
|
-
/**
|
|
1098
|
-
* Apply transform streams to file in MemFs.
|
|
1099
|
-
* @param {Transform[]} transformStreams - transform streams to be applied.
|
|
1100
|
-
* @param {Stream} [stream] - files stream, defaults to this.sharedFs.stream().
|
|
1101
|
-
* @return {Promise<void>}
|
|
1102
|
-
*/
|
|
1103
|
-
async applyTransforms(transformStreams, options = {}) {
|
|
1104
|
-
const { streamOptions = { filter: file => isFilePending(file) }, stream = this.sharedFs.stream(streamOptions), name = 'Transforming', } = options;
|
|
1105
|
-
let { log = true } = options;
|
|
1106
|
-
if (log) {
|
|
1107
|
-
npmlog.tracker = new TrackerGroup();
|
|
1108
|
-
npmlog.enableProgress();
|
|
1109
|
-
log = npmlog.newItem(name);
|
|
1110
|
-
}
|
|
1111
|
-
if (!Array.isArray(transformStreams)) {
|
|
1112
|
-
transformStreams = [transformStreams];
|
|
1113
|
-
}
|
|
1114
|
-
await pipeline(stream, ...transformStreams, transform(file => {
|
|
1115
|
-
if (log) {
|
|
1116
|
-
log.completeWork(10);
|
|
1117
|
-
npmlog.info('Completed', path.relative(this.logCwd, file.path));
|
|
1118
|
-
}
|
|
1119
|
-
}, 'environment:log'));
|
|
1120
|
-
if (log) {
|
|
1121
|
-
log.finish();
|
|
1122
|
-
npmlog.disableProgress();
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
/**
|
|
1126
|
-
* Commits the MemFs to the disc.
|
|
1127
|
-
* @param {Stream} [stream] - files stream, defaults to this.sharedFs.stream().
|
|
1128
|
-
* @return {Promise}
|
|
1129
|
-
*/
|
|
1130
|
-
commitSharedFs(stream = this.sharedFs.stream({ filter: file => isFilePending(file) })) {
|
|
1131
|
-
debug('committing files');
|
|
1132
|
-
const conflicterStatus = {};
|
|
1133
|
-
if (this.enableConflicterIgnore) {
|
|
1134
|
-
conflicterStatus.fs = this.fs;
|
|
1135
|
-
}
|
|
1136
|
-
return this.fs.commit([
|
|
1137
|
-
createYoResolveTransform(this.conflicter),
|
|
1138
|
-
createYoRcTransform(),
|
|
1139
|
-
createConflicterCheckTransform(this.conflicter, conflicterStatus),
|
|
1140
|
-
createConflicterStatusTransform(),
|
|
1141
|
-
// Use custom commit transform due to out of order transform.
|
|
1142
|
-
createCommitTransform(this.fs),
|
|
1143
|
-
], stream);
|
|
1144
|
-
}
|
|
1145
|
-
/**
|
|
1146
|
-
* Queue environment's commit task.
|
|
1147
|
-
*/
|
|
1148
|
-
queueConflicter() {
|
|
1149
|
-
const queueCommit = () => {
|
|
1150
|
-
debug('Queueing conflicts task');
|
|
1151
|
-
this.queueTask('environment:conflicts', async () => {
|
|
1152
|
-
let customCommitTask = this.findGeneratorCustomCommitTask();
|
|
1153
|
-
if (customCommitTask !== undefined && customCommitTask) {
|
|
1154
|
-
if (typeof customCommitTask !== 'function') {
|
|
1155
|
-
// There is a custom commit task or just disabled
|
|
1156
|
-
return;
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
else {
|
|
1160
|
-
// Using default commit task
|
|
1161
|
-
customCommitTask = this.commitSharedFs.bind(this);
|
|
1162
|
-
}
|
|
1163
|
-
await customCommitTask();
|
|
1164
|
-
if (this.enableConflicterIgnore) {
|
|
1165
|
-
debug('Adding queueCommit event listener');
|
|
1166
|
-
this.sharedFs.once('change', queueCommit);
|
|
1167
|
-
}
|
|
1168
|
-
}, {
|
|
1169
|
-
once: 'write memory fs to disk',
|
|
1170
|
-
});
|
|
1171
|
-
};
|
|
1172
|
-
queueCommit();
|
|
1173
|
-
}
|
|
1174
|
-
/**
|
|
1175
|
-
* Queue environment's package manager install task.
|
|
1176
|
-
*/
|
|
1177
|
-
queuePackageManagerInstall() {
|
|
1178
|
-
this.queueTask('install', () => this.packageManagerInstallTask(), { once: 'package manager install' });
|
|
1179
|
-
}
|
|
1180
|
-
/**
|
|
1181
|
-
* Queue tasks
|
|
1182
|
-
* @param {string} priority
|
|
1183
|
-
* @param {(...args: any[]) => void | Promise<void>} task
|
|
1184
|
-
* @param {{ once?: string, startQueue?: boolean }} [options]
|
|
1185
|
-
*/
|
|
1186
|
-
queueTask(priority, task, options) {
|
|
1187
|
-
return new Promise((resolve, reject) => {
|
|
1188
|
-
this.runLoop.add(priority, async (done, stop) => {
|
|
1189
|
-
try {
|
|
1190
|
-
const result = await task();
|
|
1191
|
-
done(result);
|
|
1192
|
-
resolve(result);
|
|
1193
|
-
}
|
|
1194
|
-
catch (error) {
|
|
1195
|
-
stop(error);
|
|
1196
|
-
reject(error);
|
|
1197
|
-
}
|
|
1198
|
-
}, {
|
|
1199
|
-
once: options.once,
|
|
1200
|
-
run: options.startQueue ?? false,
|
|
1201
|
-
});
|
|
1202
|
-
});
|
|
1203
|
-
}
|
|
1204
|
-
/**
|
|
1205
|
-
* Add priority
|
|
1206
|
-
* @param {string} priority
|
|
1207
|
-
* @param {string} [before]
|
|
1208
|
-
*/
|
|
1209
|
-
addPriority(priority, before) {
|
|
1210
|
-
if (this.runLoop.queueNames.includes(priority)) {
|
|
1211
|
-
return;
|
|
1212
|
-
}
|
|
1213
|
-
this.runLoop.addSubQueue(priority, before);
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
Object.assign(Environment.prototype, resolver);
|
|
1217
|
-
Object.assign(Environment.prototype, composability);
|
|
1218
|
-
Object.assign(Environment.prototype, spawnCommandMixin);
|
|
1219
|
-
Object.assign(Environment.prototype, namespaceCompasibilityMixin);
|
|
1220
|
-
export default Environment;
|
|
1221
|
-
//# sourceMappingURL=environment.js.map
|