yeoman-environment 4.0.0-alpha.6 → 4.0.0-alpha.8

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/dist/cli/index.js CHANGED
@@ -22,8 +22,7 @@ program
22
22
  .description('Find installed generators')
23
23
  .action(async () => {
24
24
  const env = createEnv();
25
- const generators = await env.lookup();
26
- printGroupedGenerator(generators);
25
+ printGroupedGenerator(await env.lookup());
27
26
  });
28
27
  program
29
28
  .command('list')
@@ -1,8 +1,10 @@
1
- export declare const printGroupedGenerator: (generators: any) => void;
1
+ import type { BaseGeneratorMeta } from '@yeoman/types';
2
+ import type YeomanCommand from '../util/command.js';
3
+ export declare const printGroupedGenerator: (generators: BaseGeneratorMeta[]) => void;
2
4
  /**
3
5
  * @param {string} generatorNamespace
4
6
  * @param {*} options
5
7
  * @param {*} command
6
8
  * @returns
7
9
  */
8
- export declare const environmentAction: (this: any, generatorNamespace: string, options: any, command: any) => Promise<any>;
10
+ export declare const environmentAction: (this: YeomanCommand, generatorNamespace: string, options: any, command: any) => Promise<void>;
package/dist/cli/utils.js CHANGED
@@ -27,7 +27,8 @@ export const environmentAction = async function (generatorNamespace, options, co
27
27
  if (!generatorNamespace) {
28
28
  return;
29
29
  }
30
- this.env = createEnv({ ...options, command: this });
31
- await this.env.lookupLocalPackages();
32
- return this.env.execute(generatorNamespace, command.args.splice(1));
30
+ const env = createEnv({ ...options, command: this });
31
+ this.env = env;
32
+ await env.lookupLocalPackages();
33
+ return env.execute(generatorNamespace, command.args.splice(1));
33
34
  };
package/dist/commit.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
- import type { PassThrough } from 'node:stream';
2
+ import type { PipelineSource } from 'node:stream';
3
3
  import type { InputOutputAdapter } from '@yeoman/types';
4
4
  import { type ConflicterOptions } from '@yeoman/conflicter';
5
5
  import type { Store } from 'mem-fs';
@@ -13,5 +13,5 @@ export declare const commitSharedFsTask: ({ adapter, conflicterOptions, sharedFs
13
13
  adapter: InputOutputAdapter;
14
14
  conflicterOptions?: ConflicterOptions | undefined;
15
15
  sharedFs: Store<MemFsEditorFile>;
16
- stream?: PassThrough | undefined;
16
+ stream?: PipelineSource<any> | undefined;
17
17
  }) => any;
@@ -1,13 +1,12 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
- /// <reference types="node" resolution-mode="require"/>
3
2
  import EventEmitter from 'node:events';
4
- import { type Transform } from 'node:stream';
5
3
  import { QueuedAdapter, type TerminalAdapterOptions } from '@yeoman/adapter';
6
4
  import type { ApplyTransformsOptions, BaseEnvironment, BaseEnvironmentOptions, BaseGenerator, BaseGeneratorConstructor, BaseGeneratorMeta, ComposeOptions, GeneratorMeta, GetGeneratorConstructor, InputOutputAdapter, InstantiateOptions, LookupGeneratorMeta } from '@yeoman/types';
7
5
  import { type Store as MemFs } from 'mem-fs';
8
6
  import { type MemFsEditor, type MemFsEditorFile } from 'mem-fs-editor';
9
7
  import { FlyRepository } from 'fly-import';
10
8
  import GroupedQueue from 'grouped-queue';
9
+ import { type FilePipelineTransform } from '@yeoman/transform';
11
10
  import { type YeomanNamespace } from '@yeoman/namespace';
12
11
  import { type ConflicterOptions } from '@yeoman/conflicter';
13
12
  import { ComposedStore } from './composed-store.js';
@@ -22,6 +21,12 @@ export type EnvironmentOptions = BaseEnvironmentOptions & Omit<TerminalAdapterOp
22
21
  arboristRegistry?: string;
23
22
  nodePackageManager?: string;
24
23
  };
24
+ /**
25
+ * Copy and remove null and undefined values
26
+ * @param object
27
+ * @returns
28
+ */
29
+ export declare function removePropertiesWithNullishValues(object: Record<string, any>): Record<string, any>;
25
30
  export default class EnvironmentBase extends EventEmitter implements BaseEnvironment {
26
31
  cwd: string;
27
32
  adapter: QueuedAdapter;
@@ -45,7 +50,7 @@ export default class EnvironmentBase extends EventEmitter implements BaseEnviron
45
50
  protected _rootGenerator?: BaseGenerator;
46
51
  protected compatibilityMode?: false | 'v4';
47
52
  constructor(options?: EnvironmentOptions);
48
- applyTransforms(transformStreams: Transform[], options?: ApplyTransformsOptions): Promise<void>;
53
+ applyTransforms(transformStreams: FilePipelineTransform[], options?: ApplyTransformsOptions): Promise<void>;
49
54
  /**
50
55
  * Get a single generator from the registered list of generators. The lookup is
51
56
  * based on generator's namespace, "walking up" the namespaces until a matching
@@ -130,11 +135,6 @@ export default class EnvironmentBase extends EventEmitter implements BaseEnviron
130
135
  */
131
136
  rootGenerator<G extends BaseGenerator = BaseGenerator>(): G;
132
137
  runGenerator(generator: BaseGenerator): Promise<void>;
133
- /**
134
- * Start Environment queue
135
- * @param {Object} options - Conflicter options.
136
- */
137
- start(options: any): Promise<void>;
138
138
  /**
139
139
  * Registers a specific `generator` to this environment. This generator is stored under
140
140
  * provided namespace, or a default namespace format if none if available.
@@ -227,6 +227,11 @@ export default class EnvironmentBase extends EventEmitter implements BaseEnviron
227
227
  */
228
228
  alias(match: string | RegExp, value: string): this;
229
229
  alias(value: string): string;
230
+ /**
231
+ * Start Environment queue
232
+ * @param {Object} options - Conflicter options.
233
+ */
234
+ protected start(options: any): Promise<void>;
230
235
  /**
231
236
  * Queue environment's commit task.
232
237
  */
@@ -12,7 +12,7 @@ import createdLogger from 'debug';
12
12
  import GroupedQueue from 'grouped-queue';
13
13
  // eslint-disable-next-line n/file-extension-in-import
14
14
  import { isFilePending } from 'mem-fs-editor/state';
15
- import { passthrough, pipeline } from '@yeoman/transform';
15
+ import { transform, filePipeline } from '@yeoman/transform';
16
16
  import { toNamespace } from '@yeoman/namespace';
17
17
  import chalk from 'chalk';
18
18
  import { defaults, pick } from 'lodash-es';
@@ -55,15 +55,18 @@ const getComposeOptions = (...varargs) => {
55
55
  let generatorArgs;
56
56
  let generatorOptions;
57
57
  if (args !== undefined) {
58
- if (typeof args === 'object') {
58
+ if (Array.isArray(args)) {
59
+ generatorArgs = args;
60
+ }
61
+ else if (typeof args === 'string') {
62
+ generatorArgs = splitArgsFromString(String(args));
63
+ }
64
+ else if (typeof args === 'object') {
59
65
  if ('generatorOptions' in args || 'generatorArgs' in args || 'schedule' in args) {
60
66
  return args;
61
67
  }
62
68
  generatorOptions = args;
63
69
  }
64
- else {
65
- generatorArgs = Array.isArray(args) ? args : splitArgsFromString(String(args));
66
- }
67
70
  }
68
71
  if (typeof options === 'boolean') {
69
72
  return { generatorArgs, generatorOptions, schedule: options };
@@ -72,8 +75,16 @@ const getComposeOptions = (...varargs) => {
72
75
  if (typeof composeOptions === 'boolean') {
73
76
  return { generatorArgs, generatorOptions, schedule: composeOptions };
74
77
  }
75
- return composeOptions;
78
+ return {};
76
79
  };
80
+ /**
81
+ * Copy and remove null and undefined values
82
+ * @param object
83
+ * @returns
84
+ */
85
+ export function removePropertiesWithNullishValues(object) {
86
+ return Object.fromEntries(Object.entries(object).filter(([_key, value]) => value !== undefined && value !== null));
87
+ }
77
88
  export default class EnvironmentBase extends EventEmitter {
78
89
  cwd;
79
90
  adapter;
@@ -96,7 +107,7 @@ export default class EnvironmentBase extends EventEmitter {
96
107
  constructor(options = {}) {
97
108
  super();
98
109
  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;
110
+ const { cwd = process.cwd(), logCwd = cwd, sharedFs = createMemFs(), command, yeomanRepository, arboristRegistry, sharedOptions = {}, experimental, console: adapterConsole, stdin, stderr, stdout, adapter = new QueuedAdapter({ console: adapterConsole, stdin, stdout, stderr }), ...remainingOptions } = options;
100
111
  this.options = remainingOptions;
101
112
  this.adapter = adapter;
102
113
  this.cwd = resolve(cwd);
@@ -133,9 +144,13 @@ export default class EnvironmentBase extends EventEmitter {
133
144
  transformStreams = [transformStreams];
134
145
  }
135
146
  await this.adapter.progress(async ({ step }) => {
136
- await pipeline(stream, ...transformStreams, passthrough(file => {
137
- step('Completed', relative(this.logCwd, file.path));
138
- }));
147
+ await filePipeline(stream, [
148
+ ...transformStreams,
149
+ transform(file => {
150
+ step('Completed', relative(this.logCwd, file.path));
151
+ return undefined;
152
+ }),
153
+ ]);
139
154
  }, { name, disabled: !(options?.log ?? true) });
140
155
  }
141
156
  /**
@@ -177,16 +192,6 @@ export default class EnvironmentBase extends EventEmitter {
177
192
  async create(namespaceOrPath, ...args) {
178
193
  let constructor;
179
194
  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
195
  const checkGenerator = (Generator) => {
191
196
  const generatorNamespace = Generator?.namespace;
192
197
  if (namespace && generatorNamespace !== namespace.namespace && generatorNamespace !== UNKNOWN_NAMESPACE) {
@@ -224,6 +229,19 @@ export default class EnvironmentBase extends EventEmitter {
224
229
  }
225
230
  return Generator;
226
231
  };
232
+ if (typeof namespaceOrPath !== 'string') {
233
+ return this.instantiate(checkGenerator(namespaceOrPath), ...args);
234
+ }
235
+ if (typeof namespaceOrPath === 'string') {
236
+ constructor = await this.get(namespaceOrPath);
237
+ if (namespace && !constructor) {
238
+ // Await this.lookupLocalNamespaces(namespace);
239
+ // constructor = await this.get(namespace);
240
+ }
241
+ }
242
+ else {
243
+ constructor = namespaceOrPath;
244
+ }
227
245
  return this.instantiate(checkGenerator(constructor), ...args);
228
246
  }
229
247
  async instantiate(constructor, ...args) {
@@ -336,56 +354,6 @@ export default class EnvironmentBase extends EventEmitter {
336
354
  this._rootGenerator = this._rootGenerator ?? generator;
337
355
  return this.start(generator.options);
338
356
  }
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
357
  register(pathOrStub, meta, ...args) {
390
358
  if (typeof pathOrStub === 'string') {
391
359
  if (typeof meta === 'object') {
@@ -455,13 +423,13 @@ export default class EnvironmentBase extends EventEmitter {
455
423
  };
456
424
  const generators = [];
457
425
  await lookupGenerators(options, ({ packagePath, filePath, lookups }) => {
426
+ let repositoryPath = join(packagePath, '..');
427
+ if (basename(repositoryPath).startsWith('@')) {
428
+ // Scoped package
429
+ repositoryPath = join(repositoryPath, '..');
430
+ }
431
+ let namespace = asNamespace(relative(repositoryPath, filePath), { lookups });
458
432
  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
433
  const resolved = realpathSync(filePath);
466
434
  if (!namespace) {
467
435
  namespace = asNamespace(resolved, { lookups });
@@ -469,12 +437,10 @@ export default class EnvironmentBase extends EventEmitter {
469
437
  if (registerToScope && !namespace.startsWith('@')) {
470
438
  namespace = `@${registerToScope}/${namespace}`;
471
439
  }
472
- this.store.add({ namespace, packagePath, resolved });
473
- const meta = this.getGeneratorMeta(namespace);
440
+ const meta = this.store.add({ namespace, packagePath, resolved });
474
441
  if (meta) {
475
442
  generators.push({
476
443
  ...meta,
477
- generatorPath: meta.resolved,
478
444
  registered: true,
479
445
  });
480
446
  return Boolean(options?.singleResult);
@@ -484,8 +450,8 @@ export default class EnvironmentBase extends EventEmitter {
484
450
  console.error('Unable to register %s (Error: %s)', filePath, error);
485
451
  }
486
452
  generators.push({
487
- generatorPath: filePath,
488
453
  resolved: filePath,
454
+ namespace,
489
455
  packagePath,
490
456
  registered: false,
491
457
  });
@@ -542,6 +508,58 @@ export default class EnvironmentBase extends EventEmitter {
542
508
  return resolved.replace(alias.match, alias.value);
543
509
  }, match);
544
510
  }
511
+ /**
512
+ * Start Environment queue
513
+ * @param {Object} options - Conflicter options.
514
+ */
515
+ async start(options) {
516
+ return new Promise((resolve, reject) => {
517
+ Object.assign(this.options, removePropertiesWithNullishValues(pick(options, ['skipInstall', 'nodePackageManager'])));
518
+ this.logCwd = options.logCwd ?? this.logCwd;
519
+ this.conflicterOptions = pick(defaults({}, this.options, options), ['force', 'bail', 'ignoreWhitespace', 'dryRun', 'skipYoResolve']);
520
+ this.conflicterOptions.cwd = this.logCwd;
521
+ this.queueCommit();
522
+ this.queuePackageManagerInstall();
523
+ /*
524
+ * Listen to errors and reject if emmited.
525
+ * Some cases the generator relied at the behavior that the running process
526
+ * would be killed if an error is thrown to environment.
527
+ * Make sure to not rely on that behavior.
528
+ */
529
+ this.on('error', error => {
530
+ reject(error);
531
+ this.adapter.close();
532
+ });
533
+ this.once('end', () => {
534
+ resolve();
535
+ this.adapter.close();
536
+ });
537
+ /*
538
+ * For backward compatibility
539
+ */
540
+ this.on('generator:reject', error => {
541
+ this.emit('error', error);
542
+ });
543
+ /*
544
+ * For backward compatibility
545
+ */
546
+ this.on('generator:resolve', () => {
547
+ this.emit('end');
548
+ });
549
+ this.runLoop.on('error', (error) => {
550
+ this.emit('error', error);
551
+ });
552
+ this.runLoop.on('paused', () => {
553
+ this.emit('paused');
554
+ });
555
+ /* If runLoop has ended, the environment has ended too. */
556
+ this.runLoop.once('end', () => {
557
+ this.emit('end');
558
+ });
559
+ this.emit('run');
560
+ this.runLoop.start();
561
+ });
562
+ }
545
563
  /**
546
564
  * Queue environment's commit task.
547
565
  */
package/dist/store.d.ts CHANGED
@@ -17,7 +17,7 @@ export default class Store {
17
17
  * @param meta
18
18
  * @param generator - A generator module or a module path
19
19
  */
20
- add(meta: BaseGeneratorMeta, Generator?: unknown): GeneratorMeta;
20
+ add<M extends BaseGeneratorMeta>(meta: M, Generator?: unknown): GeneratorMeta & M;
21
21
  /**
22
22
  * Get the module registered under the given namespace
23
23
  * @param {String} namespace
package/dist/store.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import { pathToFileURL } from 'node:url';
2
2
  import { extname, join } from 'node:path';
3
+ import { createRequire } from 'node:module';
3
4
  import { toNamespace } from '@yeoman/namespace';
4
5
  import createDebug from 'debug';
5
6
  const debug = createDebug('yeoman:environment:store');
7
+ const require = createRequire(import.meta.url);
6
8
  /**
7
9
  * The Generator store
8
10
  * This is used to store generator (npm packages) reference and instantiate them when
@@ -28,7 +30,19 @@ export default class Store {
28
30
  */
29
31
  add(meta, Generator) {
30
32
  if (typeof meta.resolved === 'string') {
31
- meta.resolved = extname(meta.resolved) ? join(meta.resolved) : join(meta.resolved, 'index.js');
33
+ if (extname(meta.resolved)) {
34
+ meta.resolved = join(meta.resolved);
35
+ }
36
+ else {
37
+ try {
38
+ // Resolve if meta.resolved is a package path.
39
+ meta.resolved = require.resolve(meta.resolved);
40
+ }
41
+ catch {
42
+ // Import must be a file, append index.js to directories
43
+ meta.resolved = join(meta.resolved, 'index.js');
44
+ }
45
+ }
32
46
  }
33
47
  if (meta.packagePath) {
34
48
  meta.packagePath = join(meta.packagePath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yeoman-environment",
3
- "version": "4.0.0-alpha.6",
3
+ "version": "4.0.0-alpha.8",
4
4
  "description": "Handles the lifecyle and bootstrapping of generators in a specific environment",
5
5
  "keywords": [
6
6
  "development",
@@ -57,12 +57,10 @@
57
57
  "doc_path": "./yeoman-environment-doc"
58
58
  },
59
59
  "dependencies": {
60
- "@npmcli/arborist": "^6.2.9",
61
- "@yeoman/adapter": "^0.3.0",
62
- "@yeoman/conflicter": "^0.6.0",
60
+ "@yeoman/adapter": "^1.1.0",
61
+ "@yeoman/conflicter": "^1.0.7",
63
62
  "@yeoman/namespace": "^1.0.0",
64
- "@yeoman/transform": "^1.0.0",
65
- "@yeoman/types": "^0.5.0",
63
+ "@yeoman/transform": "^1.2.0",
66
64
  "arrify": "^3.0.0",
67
65
  "chalk": "^5.2.0",
68
66
  "commander": "^10.0.1",
@@ -81,17 +79,17 @@
81
79
  "preferred-pm": "^3.0.3",
82
80
  "semver": "^7.5.0",
83
81
  "slash": "^5.0.1",
84
- "untildify": "^4.0.0",
85
- "yeoman-generator": "^5.8.0"
82
+ "untildify": "^4.0.0"
86
83
  },
87
84
  "devDependencies": {
85
+ "@types/debug": "^4.1.8",
88
86
  "@types/lodash-es": "^4.17.7",
89
87
  "@types/semver": "^7.5.0",
90
88
  "c8": "^7.13.0",
91
89
  "cpy-cli": "^4.2.0",
92
90
  "esmocha": "^1.0.1",
93
91
  "fs-extra": "^11.1.1",
94
- "inquirer": "^9.2.1",
92
+ "inquirer": "^9.2.6",
95
93
  "jsdoc": "^4.0.2",
96
94
  "prettier": "^2.8.8",
97
95
  "prettier-plugin-packagejson": "^2.4.3",
@@ -103,10 +101,11 @@
103
101
  "typescript": "^5.0.4",
104
102
  "xo": "0.54.2",
105
103
  "yeoman-assert": "^3.1.1",
106
- "yeoman-generator": "^5.8.0",
107
- "yeoman-test": "^8.0.0-beta.5"
104
+ "yeoman-generator": "^5.9.0",
105
+ "yeoman-test": "^8.0.0-rc.0"
108
106
  },
109
107
  "peerDependencies": {
108
+ "@yeoman/types": "^1.0.1",
110
109
  "mem-fs": "^3.0.0"
111
110
  },
112
111
  "engines": {