yeoman-environment 4.0.0-alpha.7 → 4.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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';
@@ -75,8 +75,16 @@ const getComposeOptions = (...varargs) => {
75
75
  if (typeof composeOptions === 'boolean') {
76
76
  return { generatorArgs, generatorOptions, schedule: composeOptions };
77
77
  }
78
- return composeOptions;
78
+ return {};
79
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
+ }
80
88
  export default class EnvironmentBase extends EventEmitter {
81
89
  cwd;
82
90
  adapter;
@@ -99,7 +107,7 @@ export default class EnvironmentBase extends EventEmitter {
99
107
  constructor(options = {}) {
100
108
  super();
101
109
  this.setMaxListeners(100);
102
- 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;
103
111
  this.options = remainingOptions;
104
112
  this.adapter = adapter;
105
113
  this.cwd = resolve(cwd);
@@ -136,9 +144,13 @@ export default class EnvironmentBase extends EventEmitter {
136
144
  transformStreams = [transformStreams];
137
145
  }
138
146
  await this.adapter.progress(async ({ step }) => {
139
- await pipeline(stream, ...transformStreams, passthrough(file => {
140
- step('Completed', relative(this.logCwd, file.path));
141
- }));
147
+ await filePipeline(stream, [
148
+ ...transformStreams,
149
+ transform(file => {
150
+ step('Completed', relative(this.logCwd, file.path));
151
+ return undefined;
152
+ }),
153
+ ]);
142
154
  }, { name, disabled: !(options?.log ?? true) });
143
155
  }
144
156
  /**
@@ -180,16 +192,6 @@ export default class EnvironmentBase extends EventEmitter {
180
192
  async create(namespaceOrPath, ...args) {
181
193
  let constructor;
182
194
  const namespace = typeof namespaceOrPath === 'string' ? toNamespace(namespaceOrPath) : undefined;
183
- if (typeof namespaceOrPath === 'string') {
184
- constructor = await this.get(namespaceOrPath);
185
- if (namespace && !constructor) {
186
- // Await this.lookupLocalNamespaces(namespace);
187
- // constructor = await this.get(namespace);
188
- }
189
- }
190
- else {
191
- constructor = namespaceOrPath;
192
- }
193
195
  const checkGenerator = (Generator) => {
194
196
  const generatorNamespace = Generator?.namespace;
195
197
  if (namespace && generatorNamespace !== namespace.namespace && generatorNamespace !== UNKNOWN_NAMESPACE) {
@@ -227,6 +229,19 @@ export default class EnvironmentBase extends EventEmitter {
227
229
  }
228
230
  return Generator;
229
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
+ }
230
245
  return this.instantiate(checkGenerator(constructor), ...args);
231
246
  }
232
247
  async instantiate(constructor, ...args) {
@@ -339,56 +354,6 @@ export default class EnvironmentBase extends EventEmitter {
339
354
  this._rootGenerator = this._rootGenerator ?? generator;
340
355
  return this.start(generator.options);
341
356
  }
342
- /**
343
- * Start Environment queue
344
- * @param {Object} options - Conflicter options.
345
- */
346
- async start(options) {
347
- return new Promise((resolve, reject) => {
348
- this.conflicterOptions = pick(defaults({}, this.options, options), ['force', 'bail', 'ignoreWhitespace', 'dryRun', 'skipYoResolve']);
349
- this.conflicterOptions.cwd = this.logCwd;
350
- this.queueCommit();
351
- this.queuePackageManagerInstall();
352
- /*
353
- * Listen to errors and reject if emmited.
354
- * Some cases the generator relied at the behavior that the running process
355
- * would be killed if an error is thrown to environment.
356
- * Make sure to not rely on that behavior.
357
- */
358
- this.on('error', error => {
359
- reject(error);
360
- this.adapter.close();
361
- });
362
- this.once('end', () => {
363
- resolve();
364
- this.adapter.close();
365
- });
366
- /*
367
- * For backward compatibility
368
- */
369
- this.on('generator:reject', error => {
370
- this.emit('error', error);
371
- });
372
- /*
373
- * For backward compatibility
374
- */
375
- this.on('generator:resolve', () => {
376
- this.emit('end');
377
- });
378
- this.runLoop.on('error', (error) => {
379
- this.emit('error', error);
380
- });
381
- this.runLoop.on('paused', () => {
382
- this.emit('paused');
383
- });
384
- /* If runLoop has ended, the environment has ended too. */
385
- this.runLoop.once('end', () => {
386
- this.emit('end');
387
- });
388
- this.emit('run');
389
- this.runLoop.start();
390
- });
391
- }
392
357
  register(pathOrStub, meta, ...args) {
393
358
  if (typeof pathOrStub === 'string') {
394
359
  if (typeof meta === 'object') {
@@ -458,13 +423,13 @@ export default class EnvironmentBase extends EventEmitter {
458
423
  };
459
424
  const generators = [];
460
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 });
461
432
  try {
462
- let repositoryPath = join(packagePath, '..');
463
- if (basename(repositoryPath).startsWith('@')) {
464
- // Scoped package
465
- repositoryPath = join(repositoryPath, '..');
466
- }
467
- let namespace = asNamespace(relative(repositoryPath, filePath), { lookups });
468
433
  const resolved = realpathSync(filePath);
469
434
  if (!namespace) {
470
435
  namespace = asNamespace(resolved, { lookups });
@@ -472,12 +437,10 @@ export default class EnvironmentBase extends EventEmitter {
472
437
  if (registerToScope && !namespace.startsWith('@')) {
473
438
  namespace = `@${registerToScope}/${namespace}`;
474
439
  }
475
- this.store.add({ namespace, packagePath, resolved });
476
- const meta = this.getGeneratorMeta(namespace);
440
+ const meta = this.store.add({ namespace, packagePath, resolved });
477
441
  if (meta) {
478
442
  generators.push({
479
443
  ...meta,
480
- generatorPath: meta.resolved,
481
444
  registered: true,
482
445
  });
483
446
  return Boolean(options?.singleResult);
@@ -487,8 +450,8 @@ export default class EnvironmentBase extends EventEmitter {
487
450
  console.error('Unable to register %s (Error: %s)', filePath, error);
488
451
  }
489
452
  generators.push({
490
- generatorPath: filePath,
491
453
  resolved: filePath,
454
+ namespace,
492
455
  packagePath,
493
456
  registered: false,
494
457
  });
@@ -545,6 +508,59 @@ export default class EnvironmentBase extends EventEmitter {
545
508
  return resolved.replace(alias.match, alias.value);
546
509
  }, match);
547
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.runLoop.pause();
532
+ this.adapter.close();
533
+ });
534
+ this.once('end', () => {
535
+ resolve();
536
+ this.adapter.close();
537
+ });
538
+ /*
539
+ * For backward compatibility
540
+ */
541
+ this.on('generator:reject', error => {
542
+ this.emit('error', error);
543
+ });
544
+ /*
545
+ * For backward compatibility
546
+ */
547
+ this.on('generator:resolve', () => {
548
+ this.emit('end');
549
+ });
550
+ this.runLoop.on('error', (error) => {
551
+ this.emit('error', error);
552
+ });
553
+ this.runLoop.on('paused', () => {
554
+ this.emit('paused');
555
+ });
556
+ /* If runLoop has ended, the environment has ended too. */
557
+ this.runLoop.once('end', () => {
558
+ this.emit('end');
559
+ });
560
+ this.emit('run');
561
+ this.runLoop.start();
562
+ });
563
+ }
548
564
  /**
549
565
  * Queue environment's commit task.
550
566
  */
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);
@@ -1,7 +1,6 @@
1
1
  import { parse } from 'node:path';
2
2
  import slash from 'slash';
3
- import { findLast } from 'lodash-es';
4
- import escapeStringRegexp from 'escape-string-regexp';
3
+ import { findLast, escapeRegExp } from 'lodash-es';
5
4
  export const defaultLookups = ['.', 'generators', 'lib/generators', 'dist/generators'];
6
5
  /**
7
6
  * Given a String `filepath`, tries to figure out the relative namespace.
@@ -45,7 +44,7 @@ export const asNamespace = (filepath, { lookups = defaultLookups }) => {
45
44
  // If `ns` contains a lookup dir in its path, remove it.
46
45
  for (const lookup of nsLookups) {
47
46
  // Only match full directory (begin with leading slash or start of input, end with trailing slash)
48
- ns = ns.replace(new RegExp(`(?:/|^)${escapeStringRegexp(lookup)}(?=/)`, 'g'), '');
47
+ ns = ns.replace(new RegExp(`(?:/|^)${escapeRegExp(lookup)}(?=/)`, 'g'), '');
49
48
  }
50
49
  const folders = ns.split('/');
51
50
  const scope = findLast(folders, folder => folder.startsWith('@'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yeoman-environment",
3
- "version": "4.0.0-alpha.7",
3
+ "version": "4.0.0-beta.0",
4
4
  "description": "Handles the lifecyle and bootstrapping of generators in a specific environment",
5
5
  "keywords": [
6
6
  "development",
@@ -57,41 +57,36 @@
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",
69
67
  "debug": "^4.3.4",
70
- "escape-string-regexp": "^5.0.0",
71
68
  "execa": "^7.1.1",
72
- "fly-import": "^0.2.0",
69
+ "fly-import": "^0.3.0",
73
70
  "globby": "^13.1.4",
74
71
  "grouped-queue": "^2.0.0",
75
72
  "locate-path": "^7.2.0",
76
73
  "lodash-es": "^4.17.21",
77
- "log-symbols": "^5.1.0",
78
74
  "mem-fs": "^3.0.0",
79
75
  "mem-fs-editor": "^10.0.1",
80
- "npmlog": "^7.0.1",
81
76
  "preferred-pm": "^3.0.3",
82
77
  "semver": "^7.5.0",
83
78
  "slash": "^5.0.1",
84
- "untildify": "^4.0.0",
85
- "yeoman-generator": "^5.8.0"
79
+ "untildify": "^5.0.0"
86
80
  },
87
81
  "devDependencies": {
82
+ "@types/debug": "^4.1.8",
88
83
  "@types/lodash-es": "^4.17.7",
89
84
  "@types/semver": "^7.5.0",
90
85
  "c8": "^7.13.0",
91
86
  "cpy-cli": "^4.2.0",
92
87
  "esmocha": "^1.0.1",
93
88
  "fs-extra": "^11.1.1",
94
- "inquirer": "^9.2.1",
89
+ "inquirer": "^9.2.6",
95
90
  "jsdoc": "^4.0.2",
96
91
  "prettier": "^2.8.8",
97
92
  "prettier-plugin-packagejson": "^2.4.3",
@@ -103,10 +98,11 @@
103
98
  "typescript": "^5.0.4",
104
99
  "xo": "0.54.2",
105
100
  "yeoman-assert": "^3.1.1",
106
- "yeoman-generator": "^5.8.0",
107
- "yeoman-test": "^8.0.0-beta.5"
101
+ "yeoman-generator": "^5.9.0",
102
+ "yeoman-test": "^8.0.0-rc.1"
108
103
  },
109
104
  "peerDependencies": {
105
+ "@yeoman/types": "^1.0.1",
110
106
  "mem-fs": "^3.0.0"
111
107
  },
112
108
  "engines": {