yeoman-environment 4.0.0-alpha.5 → 4.0.0-alpha.6
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 +638 -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
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
import EventEmitter from 'node:events';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { basename, isAbsolute, join, relative, resolve } from 'node:path';
|
|
4
|
+
import process from 'node:process';
|
|
5
|
+
import { realpathSync } from 'node:fs';
|
|
6
|
+
import { QueuedAdapter } from '@yeoman/adapter';
|
|
7
|
+
import { create as createMemFs } from 'mem-fs';
|
|
8
|
+
import { create as createMemFsEditor } from 'mem-fs-editor';
|
|
9
|
+
import { FlyRepository } from 'fly-import';
|
|
10
|
+
import createdLogger from 'debug';
|
|
11
|
+
// @ts-expect-error grouped-queue don't have types
|
|
12
|
+
import GroupedQueue from 'grouped-queue';
|
|
13
|
+
// eslint-disable-next-line n/file-extension-in-import
|
|
14
|
+
import { isFilePending } from 'mem-fs-editor/state';
|
|
15
|
+
import { passthrough, pipeline } from '@yeoman/transform';
|
|
16
|
+
import { toNamespace } from '@yeoman/namespace';
|
|
17
|
+
import chalk from 'chalk';
|
|
18
|
+
import { defaults, pick } from 'lodash-es';
|
|
19
|
+
import { ComposedStore } from './composed-store.js';
|
|
20
|
+
import Store from './store.js';
|
|
21
|
+
import { asNamespace, defaultLookups } from './util/namespace.js';
|
|
22
|
+
import { lookupGenerators } from './generator-lookup.js';
|
|
23
|
+
import { UNKNOWN_NAMESPACE, UNKNOWN_RESOLVED, defaultQueues } from './constants.js';
|
|
24
|
+
import { resolveModulePath } from './util/resolve.js';
|
|
25
|
+
import { commitSharedFsTask } from './commit.js';
|
|
26
|
+
import { packageManagerInstallTask } from './package-manager.js';
|
|
27
|
+
// eslint-disable-next-line import/order
|
|
28
|
+
import { splitArgsFromString } from './util/util.js';
|
|
29
|
+
const require = createRequire(import.meta.url);
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
31
|
+
const ENVIRONMENT_VERSION = require('../package.json').version;
|
|
32
|
+
const debug = createdLogger('yeoman:environment');
|
|
33
|
+
const getInstantiateOptions = (args, options) => {
|
|
34
|
+
if (Array.isArray(args) || typeof args === 'string') {
|
|
35
|
+
return { generatorArgs: splitArgsFromString(args), generatorOptions: options };
|
|
36
|
+
}
|
|
37
|
+
if (args !== undefined) {
|
|
38
|
+
if ('generatorOptions' in args || 'generatorArgs' in args) {
|
|
39
|
+
return args;
|
|
40
|
+
}
|
|
41
|
+
if ('options' in args || 'arguments' in args || 'args' in args) {
|
|
42
|
+
const { args: insideArgs, arguments: generatorArgs = insideArgs, options: generatorOptions, ...remainingOptions } = args;
|
|
43
|
+
return { generatorArgs: splitArgsFromString(generatorArgs), generatorOptions: generatorOptions ?? remainingOptions };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return { generatorOptions: options };
|
|
47
|
+
};
|
|
48
|
+
const getComposeOptions = (...varargs) => {
|
|
49
|
+
if (varargs.filter(Boolean).length === 0)
|
|
50
|
+
return {};
|
|
51
|
+
const [args, options, composeOptions] = varargs;
|
|
52
|
+
if (typeof args === 'boolean') {
|
|
53
|
+
return { schedule: args };
|
|
54
|
+
}
|
|
55
|
+
let generatorArgs;
|
|
56
|
+
let generatorOptions;
|
|
57
|
+
if (args !== undefined) {
|
|
58
|
+
if (typeof args === 'object') {
|
|
59
|
+
if ('generatorOptions' in args || 'generatorArgs' in args || 'schedule' in args) {
|
|
60
|
+
return args;
|
|
61
|
+
}
|
|
62
|
+
generatorOptions = args;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
generatorArgs = Array.isArray(args) ? args : splitArgsFromString(String(args));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (typeof options === 'boolean') {
|
|
69
|
+
return { generatorArgs, generatorOptions, schedule: options };
|
|
70
|
+
}
|
|
71
|
+
generatorOptions = generatorOptions ?? options;
|
|
72
|
+
if (typeof composeOptions === 'boolean') {
|
|
73
|
+
return { generatorArgs, generatorOptions, schedule: composeOptions };
|
|
74
|
+
}
|
|
75
|
+
return composeOptions;
|
|
76
|
+
};
|
|
77
|
+
export default class EnvironmentBase extends EventEmitter {
|
|
78
|
+
cwd;
|
|
79
|
+
adapter;
|
|
80
|
+
sharedFs;
|
|
81
|
+
conflicterOptions;
|
|
82
|
+
logCwd;
|
|
83
|
+
options;
|
|
84
|
+
aliases = [];
|
|
85
|
+
store;
|
|
86
|
+
command;
|
|
87
|
+
runLoop;
|
|
88
|
+
composedStore;
|
|
89
|
+
fs;
|
|
90
|
+
lookups;
|
|
91
|
+
sharedOptions;
|
|
92
|
+
repository;
|
|
93
|
+
experimental;
|
|
94
|
+
_rootGenerator;
|
|
95
|
+
compatibilityMode;
|
|
96
|
+
constructor(options = {}) {
|
|
97
|
+
super();
|
|
98
|
+
this.setMaxListeners(100);
|
|
99
|
+
const { cwd = process.cwd(), logCwd = cwd, sharedFs = createMemFs(), command, yeomanRepository, arboristRegistry, sharedOptions = {}, experimental, console, stdin, stderr, stdout, adapter = new QueuedAdapter({ console, stdin, stdout, stderr }), ...remainingOptions } = options;
|
|
100
|
+
this.options = remainingOptions;
|
|
101
|
+
this.adapter = adapter;
|
|
102
|
+
this.cwd = resolve(cwd);
|
|
103
|
+
this.logCwd = logCwd;
|
|
104
|
+
this.store = new Store(this);
|
|
105
|
+
this.command = command;
|
|
106
|
+
this.runLoop = new GroupedQueue(defaultQueues, false);
|
|
107
|
+
this.composedStore = new ComposedStore({ log: this.adapter.log });
|
|
108
|
+
this.sharedFs = sharedFs;
|
|
109
|
+
// Each composed generator might set listeners on these shared resources. Let's make sure
|
|
110
|
+
// Node won't complain about event listeners leaks.
|
|
111
|
+
this.runLoop.setMaxListeners(0);
|
|
112
|
+
this.sharedFs.setMaxListeners(0);
|
|
113
|
+
this.fs = createMemFsEditor(sharedFs);
|
|
114
|
+
this.lookups = defaultLookups;
|
|
115
|
+
this.sharedOptions = sharedOptions;
|
|
116
|
+
// Create a default sharedData.
|
|
117
|
+
this.sharedOptions.sharedData = this.sharedOptions.sharedData ?? {};
|
|
118
|
+
// Pass forwardErrorToEnvironment to generators.
|
|
119
|
+
this.sharedOptions.forwardErrorToEnvironment = false;
|
|
120
|
+
this.repository = new FlyRepository({
|
|
121
|
+
repositoryPath: yeomanRepository ?? `${this.cwd}/.yo-repository`,
|
|
122
|
+
arboristConfig: {
|
|
123
|
+
registry: arboristRegistry,
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
127
|
+
this.experimental = experimental || process.argv.includes('--experimental');
|
|
128
|
+
this.alias(/^([^:]+)$/, '$1:app');
|
|
129
|
+
}
|
|
130
|
+
async applyTransforms(transformStreams, options = {}) {
|
|
131
|
+
const { streamOptions = { filter: file => isFilePending(file) }, stream = this.sharedFs.stream(streamOptions), name = 'Transforming', } = options;
|
|
132
|
+
if (!Array.isArray(transformStreams)) {
|
|
133
|
+
transformStreams = [transformStreams];
|
|
134
|
+
}
|
|
135
|
+
await this.adapter.progress(async ({ step }) => {
|
|
136
|
+
await pipeline(stream, ...transformStreams, passthrough(file => {
|
|
137
|
+
step('Completed', relative(this.logCwd, file.path));
|
|
138
|
+
}));
|
|
139
|
+
}, { name, disabled: !(options?.log ?? true) });
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get a single generator from the registered list of generators. The lookup is
|
|
143
|
+
* based on generator's namespace, "walking up" the namespaces until a matching
|
|
144
|
+
* is found. Eg. if an `angular:common` namespace is registered, and we try to
|
|
145
|
+
* get `angular:common:all` then we get `angular:common` as a fallback (unless
|
|
146
|
+
* an `angular:common:all` generator is registered).
|
|
147
|
+
*
|
|
148
|
+
* @param namespaceOrPath
|
|
149
|
+
* @return the generator registered under the namespace
|
|
150
|
+
*/
|
|
151
|
+
async get(namespaceOrPath) {
|
|
152
|
+
// Stop the recursive search if nothing is left
|
|
153
|
+
if (!namespaceOrPath) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const parsed = toNamespace(namespaceOrPath);
|
|
157
|
+
if (typeof namespaceOrPath !== 'string' || parsed) {
|
|
158
|
+
const ns = parsed.namespace;
|
|
159
|
+
const maybeGenerator = (await this.store.get(ns)) ?? this.store.get(this.alias(ns));
|
|
160
|
+
return maybeGenerator;
|
|
161
|
+
}
|
|
162
|
+
const maybeGenerator = (await this.store.get(namespaceOrPath)) ?? (await this.store.get(this.alias(namespaceOrPath)));
|
|
163
|
+
if (maybeGenerator) {
|
|
164
|
+
return maybeGenerator;
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
const resolved = await resolveModulePath(namespaceOrPath);
|
|
168
|
+
if (resolved) {
|
|
169
|
+
const namespace = this.namespace(resolved);
|
|
170
|
+
this.store.add({ resolved, namespace });
|
|
171
|
+
return (await this.store.get(namespace));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch { }
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
async create(namespaceOrPath, ...args) {
|
|
178
|
+
let constructor;
|
|
179
|
+
const namespace = typeof namespaceOrPath === 'string' ? toNamespace(namespaceOrPath) : undefined;
|
|
180
|
+
if (typeof namespaceOrPath === 'string') {
|
|
181
|
+
constructor = await this.get(namespaceOrPath);
|
|
182
|
+
if (namespace && !constructor) {
|
|
183
|
+
// Await this.lookupLocalNamespaces(namespace);
|
|
184
|
+
// constructor = await this.get(namespace);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
constructor = namespaceOrPath;
|
|
189
|
+
}
|
|
190
|
+
const checkGenerator = (Generator) => {
|
|
191
|
+
const generatorNamespace = Generator?.namespace;
|
|
192
|
+
if (namespace && generatorNamespace !== namespace.namespace && generatorNamespace !== UNKNOWN_NAMESPACE) {
|
|
193
|
+
// Update namespace object in case of aliased namespace.
|
|
194
|
+
try {
|
|
195
|
+
namespace.namespace = Generator.namespace;
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
// Invalid namespace can be aliased to a valid one.
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (typeof Generator !== 'function') {
|
|
202
|
+
throw new TypeError(chalk.red(`You don't seem to have a generator with the name “${namespace?.generatorHint}” installed.`) +
|
|
203
|
+
'\n' +
|
|
204
|
+
'But help is on the way:\n\n' +
|
|
205
|
+
'You can see available generators via ' +
|
|
206
|
+
chalk.yellow('npm search yeoman-generator') +
|
|
207
|
+
' or via ' +
|
|
208
|
+
chalk.yellow('http://yeoman.io/generators/') +
|
|
209
|
+
'. \n' +
|
|
210
|
+
'Install them with ' +
|
|
211
|
+
chalk.yellow(`npm install ${namespace?.generatorHint}`) +
|
|
212
|
+
'.\n\n' +
|
|
213
|
+
'To see all your installed generators run ' +
|
|
214
|
+
chalk.yellow('yo --generators') +
|
|
215
|
+
'. ' +
|
|
216
|
+
'Adding the ' +
|
|
217
|
+
chalk.yellow('--help') +
|
|
218
|
+
' option will also show subgenerators. \n\n' +
|
|
219
|
+
'If ' +
|
|
220
|
+
chalk.yellow('yo') +
|
|
221
|
+
' cannot find the generator, run ' +
|
|
222
|
+
chalk.yellow('yo doctor') +
|
|
223
|
+
' to troubleshoot your system.');
|
|
224
|
+
}
|
|
225
|
+
return Generator;
|
|
226
|
+
};
|
|
227
|
+
return this.instantiate(checkGenerator(constructor), ...args);
|
|
228
|
+
}
|
|
229
|
+
async instantiate(constructor, ...args) {
|
|
230
|
+
const composeOptions = args.length > 0 ? getInstantiateOptions(...args) : {};
|
|
231
|
+
const { namespace = UNKNOWN_NAMESPACE, resolved = UNKNOWN_RESOLVED } = constructor;
|
|
232
|
+
const environmentOptions = { env: this, resolved, namespace };
|
|
233
|
+
const generator = new constructor(composeOptions.generatorArgs ?? [], {
|
|
234
|
+
...this.sharedOptions,
|
|
235
|
+
...composeOptions.generatorOptions,
|
|
236
|
+
...environmentOptions,
|
|
237
|
+
});
|
|
238
|
+
generator._environmentOptions = {
|
|
239
|
+
...this.options,
|
|
240
|
+
...this.sharedOptions,
|
|
241
|
+
...environmentOptions,
|
|
242
|
+
};
|
|
243
|
+
if (!composeOptions.generatorOptions?.help && generator._postConstruct) {
|
|
244
|
+
await generator._postConstruct();
|
|
245
|
+
}
|
|
246
|
+
return generator;
|
|
247
|
+
}
|
|
248
|
+
async composeWith(generator, ...args) {
|
|
249
|
+
const options = getComposeOptions(...args);
|
|
250
|
+
const { schedule = true, ...instantiateOptions } = options;
|
|
251
|
+
const generatorInstance = await this.create(generator, instantiateOptions);
|
|
252
|
+
return this.queueGenerator(generatorInstance, { schedule });
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Given a String `filepath`, tries to figure out the relative namespace.
|
|
256
|
+
*
|
|
257
|
+
* ### Examples:
|
|
258
|
+
*
|
|
259
|
+
* this.namespace('backbone/all/index.js');
|
|
260
|
+
* // => backbone:all
|
|
261
|
+
*
|
|
262
|
+
* this.namespace('generator-backbone/model');
|
|
263
|
+
* // => backbone:model
|
|
264
|
+
*
|
|
265
|
+
* this.namespace('backbone.js');
|
|
266
|
+
* // => backbone
|
|
267
|
+
*
|
|
268
|
+
* this.namespace('generator-mocha/backbone/model/index.js');
|
|
269
|
+
* // => mocha:backbone:model
|
|
270
|
+
*
|
|
271
|
+
* @param {String} filepath
|
|
272
|
+
* @param {Array} lookups paths
|
|
273
|
+
*/
|
|
274
|
+
namespace(filepath, lookups = this.lookups) {
|
|
275
|
+
return asNamespace(filepath, { lookups });
|
|
276
|
+
}
|
|
277
|
+
getVersion(packageName) {
|
|
278
|
+
if (packageName && packageName !== 'yeoman-environment') {
|
|
279
|
+
try {
|
|
280
|
+
return require(`${packageName}/package.json`).version;
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
return undefined;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return ENVIRONMENT_VERSION;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Queue generator run (queue itself tasks).
|
|
290
|
+
*
|
|
291
|
+
* @param {Generator} generator Generator instance
|
|
292
|
+
* @param {boolean} [schedule=false] Whether to schedule the generator run.
|
|
293
|
+
* @return {Generator} The generator or singleton instance.
|
|
294
|
+
*/
|
|
295
|
+
async queueGenerator(generator, queueOptions) {
|
|
296
|
+
const schedule = typeof queueOptions === 'boolean' ? queueOptions : queueOptions?.schedule ?? false;
|
|
297
|
+
const { added, identifier, generator: composedGenerator } = this.composedStore.addGenerator(generator);
|
|
298
|
+
if (!added) {
|
|
299
|
+
debug(`Using existing generator for namespace ${identifier}`);
|
|
300
|
+
return composedGenerator;
|
|
301
|
+
}
|
|
302
|
+
this.emit('compose', identifier, generator);
|
|
303
|
+
this.emit(`compose:${identifier}`, generator);
|
|
304
|
+
const runGenerator = async () => {
|
|
305
|
+
if (generator.queueTasks) {
|
|
306
|
+
// Generator > 5
|
|
307
|
+
this.once('run', () => generator.emit('run'));
|
|
308
|
+
this.once('end', () => generator.emit('end'));
|
|
309
|
+
await generator.queueTasks();
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (!generator.options.forwardErrorToEnvironment) {
|
|
313
|
+
generator.on('error', (error) => this.emit('error', error));
|
|
314
|
+
}
|
|
315
|
+
generator.promise = generator.run();
|
|
316
|
+
};
|
|
317
|
+
if (schedule) {
|
|
318
|
+
this.queueTask('environment:run', async () => runGenerator());
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
await runGenerator();
|
|
322
|
+
}
|
|
323
|
+
return generator;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Get the first generator that was queued to run in this environment.
|
|
327
|
+
*
|
|
328
|
+
* @return {Generator} generator queued to run in this environment.
|
|
329
|
+
*/
|
|
330
|
+
rootGenerator() {
|
|
331
|
+
return this._rootGenerator;
|
|
332
|
+
}
|
|
333
|
+
async runGenerator(generator) {
|
|
334
|
+
generator = await this.queueGenerator(generator);
|
|
335
|
+
this.compatibilityMode = generator.queueTasks ? false : 'v4';
|
|
336
|
+
this._rootGenerator = this._rootGenerator ?? generator;
|
|
337
|
+
return this.start(generator.options);
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Start Environment queue
|
|
341
|
+
* @param {Object} options - Conflicter options.
|
|
342
|
+
*/
|
|
343
|
+
async start(options) {
|
|
344
|
+
return new Promise((resolve, reject) => {
|
|
345
|
+
this.conflicterOptions = pick(defaults({}, this.options, options), ['force', 'bail', 'ignoreWhitespace', 'dryRun', 'skipYoResolve']);
|
|
346
|
+
this.conflicterOptions.cwd = this.logCwd;
|
|
347
|
+
this.queueCommit();
|
|
348
|
+
this.queuePackageManagerInstall();
|
|
349
|
+
/*
|
|
350
|
+
* Listen to errors and reject if emmited.
|
|
351
|
+
* Some cases the generator relied at the behavior that the running process
|
|
352
|
+
* would be killed if an error is thrown to environment.
|
|
353
|
+
* Make sure to not rely on that behavior.
|
|
354
|
+
*/
|
|
355
|
+
this.on('error', error => {
|
|
356
|
+
reject(error);
|
|
357
|
+
this.adapter.close();
|
|
358
|
+
});
|
|
359
|
+
this.once('end', () => {
|
|
360
|
+
resolve();
|
|
361
|
+
this.adapter.close();
|
|
362
|
+
});
|
|
363
|
+
/*
|
|
364
|
+
* For backward compatibility
|
|
365
|
+
*/
|
|
366
|
+
this.on('generator:reject', error => {
|
|
367
|
+
this.emit('error', error);
|
|
368
|
+
});
|
|
369
|
+
/*
|
|
370
|
+
* For backward compatibility
|
|
371
|
+
*/
|
|
372
|
+
this.on('generator:resolve', () => {
|
|
373
|
+
this.emit('end');
|
|
374
|
+
});
|
|
375
|
+
this.runLoop.on('error', (error) => {
|
|
376
|
+
this.emit('error', error);
|
|
377
|
+
});
|
|
378
|
+
this.runLoop.on('paused', () => {
|
|
379
|
+
this.emit('paused');
|
|
380
|
+
});
|
|
381
|
+
/* If runLoop has ended, the environment has ended too. */
|
|
382
|
+
this.runLoop.once('end', () => {
|
|
383
|
+
this.emit('end');
|
|
384
|
+
});
|
|
385
|
+
this.emit('run');
|
|
386
|
+
this.runLoop.start();
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
register(pathOrStub, meta, ...args) {
|
|
390
|
+
if (typeof pathOrStub === 'string') {
|
|
391
|
+
if (typeof meta === 'object') {
|
|
392
|
+
return this.registerGeneratorPath(pathOrStub, meta.namespace, meta.packagePath);
|
|
393
|
+
}
|
|
394
|
+
// Backward compatibility
|
|
395
|
+
return this.registerGeneratorPath(pathOrStub, meta, ...args);
|
|
396
|
+
}
|
|
397
|
+
if (pathOrStub) {
|
|
398
|
+
if (typeof meta === 'object') {
|
|
399
|
+
return this.registerStub(pathOrStub, meta.namespace, meta.resolved, meta.packagePath);
|
|
400
|
+
}
|
|
401
|
+
// Backward compatibility
|
|
402
|
+
return this.registerStub(pathOrStub, meta, ...args);
|
|
403
|
+
}
|
|
404
|
+
throw new TypeError('You must provide a generator name to register.');
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Queue tasks
|
|
408
|
+
* @param {string} priority
|
|
409
|
+
* @param {(...args: any[]) => void | Promise<void>} task
|
|
410
|
+
* @param {{ once?: string, startQueue?: boolean }} [options]
|
|
411
|
+
*/
|
|
412
|
+
queueTask(priority, task, options) {
|
|
413
|
+
this.runLoop.add(priority, async (done, stop) => {
|
|
414
|
+
try {
|
|
415
|
+
await task();
|
|
416
|
+
done();
|
|
417
|
+
}
|
|
418
|
+
catch (error) {
|
|
419
|
+
stop(error);
|
|
420
|
+
}
|
|
421
|
+
}, {
|
|
422
|
+
once: options?.once,
|
|
423
|
+
run: options?.startQueue ?? false,
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Add priority
|
|
428
|
+
* @param {string} priority
|
|
429
|
+
* @param {string} [before]
|
|
430
|
+
*/
|
|
431
|
+
addPriority(priority, before) {
|
|
432
|
+
if (this.runLoop.queueNames.includes(priority)) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
this.runLoop.addSubQueue(priority, before);
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Search for generators and their sub generators.
|
|
439
|
+
*
|
|
440
|
+
* A generator is a `:lookup/:name/index.js` file placed inside an npm package.
|
|
441
|
+
*
|
|
442
|
+
* Defaults lookups are:
|
|
443
|
+
* - ./
|
|
444
|
+
* - generators/
|
|
445
|
+
* - lib/generators/
|
|
446
|
+
*
|
|
447
|
+
* So this index file `node_modules/generator-dummy/lib/generators/yo/index.js` would be
|
|
448
|
+
* registered as `dummy:yo` generator.
|
|
449
|
+
*/
|
|
450
|
+
async lookup(options) {
|
|
451
|
+
const { registerToScope, lookups = this.lookups, ...remainingOptions } = options ?? { localOnly: false };
|
|
452
|
+
options = {
|
|
453
|
+
...remainingOptions,
|
|
454
|
+
lookups,
|
|
455
|
+
};
|
|
456
|
+
const generators = [];
|
|
457
|
+
await lookupGenerators(options, ({ packagePath, filePath, lookups }) => {
|
|
458
|
+
try {
|
|
459
|
+
let repositoryPath = join(packagePath, '..');
|
|
460
|
+
if (basename(repositoryPath).startsWith('@')) {
|
|
461
|
+
// Scoped package
|
|
462
|
+
repositoryPath = join(repositoryPath, '..');
|
|
463
|
+
}
|
|
464
|
+
let namespace = asNamespace(relative(repositoryPath, filePath), { lookups });
|
|
465
|
+
const resolved = realpathSync(filePath);
|
|
466
|
+
if (!namespace) {
|
|
467
|
+
namespace = asNamespace(resolved, { lookups });
|
|
468
|
+
}
|
|
469
|
+
if (registerToScope && !namespace.startsWith('@')) {
|
|
470
|
+
namespace = `@${registerToScope}/${namespace}`;
|
|
471
|
+
}
|
|
472
|
+
this.store.add({ namespace, packagePath, resolved });
|
|
473
|
+
const meta = this.getGeneratorMeta(namespace);
|
|
474
|
+
if (meta) {
|
|
475
|
+
generators.push({
|
|
476
|
+
...meta,
|
|
477
|
+
generatorPath: meta.resolved,
|
|
478
|
+
registered: true,
|
|
479
|
+
});
|
|
480
|
+
return Boolean(options?.singleResult);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
catch (error) {
|
|
484
|
+
console.error('Unable to register %s (Error: %s)', filePath, error);
|
|
485
|
+
}
|
|
486
|
+
generators.push({
|
|
487
|
+
generatorPath: filePath,
|
|
488
|
+
resolved: filePath,
|
|
489
|
+
packagePath,
|
|
490
|
+
registered: false,
|
|
491
|
+
});
|
|
492
|
+
return false;
|
|
493
|
+
});
|
|
494
|
+
return generators;
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Verify if a package namespace already have been registered.
|
|
498
|
+
*
|
|
499
|
+
* @param packageNS - namespace of the package.
|
|
500
|
+
* @return true if any generator of the package has been registered
|
|
501
|
+
*/
|
|
502
|
+
isPackageRegistered(packageNamespace) {
|
|
503
|
+
const registeredPackages = this.getRegisteredPackages();
|
|
504
|
+
return registeredPackages.includes(packageNamespace) || registeredPackages.includes(this.alias(packageNamespace).split(':', 2)[0]);
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Get all registered packages namespaces.
|
|
508
|
+
*
|
|
509
|
+
* @return array of namespaces.
|
|
510
|
+
*/
|
|
511
|
+
getRegisteredPackages() {
|
|
512
|
+
return this.store.getPackagesNS();
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Returns stored generators meta
|
|
516
|
+
* @param namespace
|
|
517
|
+
*/
|
|
518
|
+
getGeneratorMeta(namespace) {
|
|
519
|
+
const meta = this.store.getMeta(namespace) ?? this.store.getMeta(this.alias(namespace));
|
|
520
|
+
if (!meta) {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
524
|
+
return { ...meta };
|
|
525
|
+
}
|
|
526
|
+
alias(match, value) {
|
|
527
|
+
if (match && value) {
|
|
528
|
+
this.aliases.push({
|
|
529
|
+
match: match instanceof RegExp ? match : new RegExp(`^${match}$`),
|
|
530
|
+
value,
|
|
531
|
+
});
|
|
532
|
+
return this;
|
|
533
|
+
}
|
|
534
|
+
if (typeof match !== 'string') {
|
|
535
|
+
throw new TypeError('string is required');
|
|
536
|
+
}
|
|
537
|
+
const aliases = [...this.aliases].reverse();
|
|
538
|
+
return aliases.reduce((resolved, alias) => {
|
|
539
|
+
if (!alias.match.test(resolved)) {
|
|
540
|
+
return resolved;
|
|
541
|
+
}
|
|
542
|
+
return resolved.replace(alias.match, alias.value);
|
|
543
|
+
}, match);
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Queue environment's commit task.
|
|
547
|
+
*/
|
|
548
|
+
queueCommit() {
|
|
549
|
+
const queueCommit = () => {
|
|
550
|
+
debug('Queueing conflicts task');
|
|
551
|
+
this.queueTask('environment:conflicts', async () => {
|
|
552
|
+
const { customCommitTask = () => commitSharedFsTask(this) } = this.composedStore;
|
|
553
|
+
if (typeof customCommitTask !== 'function') {
|
|
554
|
+
// There is a custom commit task or just disabled
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
await customCommitTask();
|
|
558
|
+
debug('Adding queueCommit event listener');
|
|
559
|
+
this.sharedFs.once('change', queueCommit);
|
|
560
|
+
}, {
|
|
561
|
+
once: 'write memory fs to disk',
|
|
562
|
+
});
|
|
563
|
+
};
|
|
564
|
+
queueCommit();
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Queue environment's package manager install task.
|
|
568
|
+
*/
|
|
569
|
+
queuePackageManagerInstall() {
|
|
570
|
+
const { adapter, sharedFs: memFs } = this;
|
|
571
|
+
const { skipInstall, nodePackageManager } = this.options;
|
|
572
|
+
const { customInstallTask } = this.composedStore;
|
|
573
|
+
this.queueTask('install', async () => {
|
|
574
|
+
if (this.compatibilityMode === 'v4') {
|
|
575
|
+
debug('Running in generator < 5 compatibility. Package manager install is done by the generator.');
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
await packageManagerInstallTask({
|
|
579
|
+
adapter,
|
|
580
|
+
memFs,
|
|
581
|
+
packageJsonLocation: this.cwd,
|
|
582
|
+
skipInstall,
|
|
583
|
+
nodePackageManager,
|
|
584
|
+
customInstallTask,
|
|
585
|
+
});
|
|
586
|
+
}, { once: 'package manager install' });
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Registers a specific `generator` to this environment. This generator is stored under
|
|
590
|
+
* provided namespace, or a default namespace format if none if available.
|
|
591
|
+
*
|
|
592
|
+
* @param name - Filepath to the a generator or a npm package name
|
|
593
|
+
* @param namespace - Namespace under which register the generator (optional)
|
|
594
|
+
* @param packagePath - PackagePath to the generator npm package (optional)
|
|
595
|
+
* @return environment - This environment
|
|
596
|
+
*/
|
|
597
|
+
registerGeneratorPath(generatorPath, namespace, packagePath) {
|
|
598
|
+
if (typeof generatorPath !== 'string') {
|
|
599
|
+
throw new TypeError('You must provide a generator name to register.');
|
|
600
|
+
}
|
|
601
|
+
if (!isAbsolute(generatorPath)) {
|
|
602
|
+
throw new Error(`An absolute path is required to register`);
|
|
603
|
+
}
|
|
604
|
+
namespace = namespace ?? this.namespace(generatorPath);
|
|
605
|
+
if (!namespace) {
|
|
606
|
+
throw new Error('Unable to determine namespace.');
|
|
607
|
+
}
|
|
608
|
+
// Generator is already registered and matches the current namespace.
|
|
609
|
+
const generatorMeta = this.store.getMeta(namespace);
|
|
610
|
+
if (generatorMeta && generatorMeta.resolved === generatorPath) {
|
|
611
|
+
return generatorMeta;
|
|
612
|
+
}
|
|
613
|
+
const meta = this.store.add({ namespace, resolved: generatorPath, packagePath });
|
|
614
|
+
debug('Registered %s (%s) on package %s (%s)', namespace, generatorPath, meta.packageNamespace, packagePath);
|
|
615
|
+
return meta;
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Register a stubbed generator to this environment. This method allow to register raw
|
|
619
|
+
* functions under the provided namespace. `registerStub` will enforce the function passed
|
|
620
|
+
* to extend the Base generator automatically.
|
|
621
|
+
*
|
|
622
|
+
* @param Generator - A Generator constructor or a simple function
|
|
623
|
+
* @param namespace - Namespace under which register the generator
|
|
624
|
+
* @param resolved - The file path to the generator
|
|
625
|
+
* @param packagePath - The generator's package path
|
|
626
|
+
*/
|
|
627
|
+
registerStub(Generator, namespace, resolved = UNKNOWN_RESOLVED, packagePath) {
|
|
628
|
+
if (typeof Generator !== 'function' && typeof Generator.createGenerator !== 'function') {
|
|
629
|
+
throw new TypeError('You must provide a stub function to register.');
|
|
630
|
+
}
|
|
631
|
+
if (typeof namespace !== 'string') {
|
|
632
|
+
throw new TypeError('You must provide a namespace to register.');
|
|
633
|
+
}
|
|
634
|
+
const meta = this.store.add({ namespace, resolved, packagePath }, Generator);
|
|
635
|
+
debug('Registered %s (%s) on package (%s)', namespace, resolved, packagePath);
|
|
636
|
+
return meta;
|
|
637
|
+
}
|
|
638
|
+
}
|