yeoman-environment 6.0.1 → 6.2.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/bin/bin.cjs CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
2
  (async function () {
3
- // eslint-disable-next-line import-x/extensions
4
3
  await import('../dist/cli/index.js');
5
4
  })();
@@ -1,5 +1,5 @@
1
1
  import type { BaseGenerator, Logger } from '@yeoman/types';
2
- import type { InstallTask } from './package-manager.ts';
2
+ import type { PackageManagerInstallTaskOptions } from './package-manager.ts';
3
3
  export declare class ComposedStore {
4
4
  private readonly log?;
5
5
  private readonly generators;
@@ -9,15 +9,15 @@ export declare class ComposedStore {
9
9
  log?: Logger;
10
10
  });
11
11
  get customCommitTask(): (() => Promise<void> | void) | undefined;
12
- get customInstallTask(): InstallTask | undefined;
12
+ get customInstallTask(): PackageManagerInstallTaskOptions['customInstallTask'] | undefined;
13
13
  getGenerators(): Record<string, BaseGenerator>;
14
14
  addGenerator(generator: BaseGenerator): {
15
- uniqueBy: any;
16
- identifier: any;
15
+ uniqueBy: string;
16
+ identifier: string | undefined;
17
17
  added: boolean;
18
18
  generator: BaseGenerator | undefined;
19
19
  } | {
20
- identifier: any;
20
+ identifier: string | undefined;
21
21
  added: boolean;
22
22
  generator: BaseGenerator;
23
23
  uniqueBy?: undefined;
@@ -2,6 +2,9 @@ import crypto from 'node:crypto';
2
2
  import { toNamespace } from '@yeoman/namespace';
3
3
  import createdLogger from 'debug';
4
4
  const debug = createdLogger('yeoman:environment:composed-store');
5
+ const getFeaturesFromGenerator = (generator) => {
6
+ return generator.features ?? generator.getFeatures?.() ?? {};
7
+ };
5
8
  export class ComposedStore {
6
9
  log;
7
10
  generators = {};
@@ -20,7 +23,7 @@ export class ComposedStore {
20
23
  return { ...this.generators };
21
24
  }
22
25
  addGenerator(generator) {
23
- const { features = generator.getFeatures?.() ?? {} } = generator;
26
+ const features = getFeaturesFromGenerator(generator);
24
27
  let { uniqueBy } = features;
25
28
  const { uniqueGlobally } = features;
26
29
  let identifier = uniqueBy;
@@ -56,8 +59,8 @@ export class ComposedStore {
56
59
  findFeature(featureName) {
57
60
  return Object.entries(this.generators)
58
61
  .map(([generatorId, generator]) => {
59
- const { features = generator.getFeatures?.() } = generator;
60
- const feature = features?.[featureName];
62
+ const features = getFeaturesFromGenerator(generator);
63
+ const feature = features[featureName];
61
64
  return feature ? { generatorId, feature: feature } : undefined;
62
65
  })
63
66
  .filter(Boolean);
@@ -242,13 +242,13 @@ export default class EnvironmentBase extends EventEmitter {
242
242
  instantiate(constructor, ...arguments_) {
243
243
  const composeOptions = arguments_.length > 0 ? getInstantiateOptions(...arguments_) : {};
244
244
  const { namespace = UNKNOWN_NAMESPACE, resolved = UNKNOWN_RESOLVED, _meta } = constructor;
245
- const environmentOptions = { env: this, resolved, namespace };
245
+ const environmentOptions = { env: this, resolved, namespace, _meta };
246
246
  const generator = new constructor(composeOptions.generatorArgs ?? [], {
247
247
  ...this.sharedOptions,
248
248
  ...composeOptions.generatorOptions,
249
249
  ...environmentOptions,
250
250
  });
251
- generator._meta = _meta;
251
+ generator._meta ??= _meta;
252
252
  generator._environmentOptions = {
253
253
  ...this.options,
254
254
  ...this.sharedOptions,
@@ -62,7 +62,7 @@ declare class FullEnvironment extends EnvironmentBase {
62
62
  * @param {string} generatorNamespace
63
63
  * @param {string[]} args
64
64
  */
65
- execute(generatorNamespace: string, arguments_?: never[]): Promise<void>;
65
+ execute(generatorNamespace: string, arguments_?: string[]): Promise<void>;
66
66
  requireGenerator(namespace: string): Promise<BaseGeneratorConstructor | undefined>;
67
67
  /**
68
68
  * Install generators at the custom local repository and register.
@@ -74,7 +74,7 @@ class FullEnvironment extends EnvironmentBase {
74
74
  }
75
75
  groups[base].push(namespace);
76
76
  }
77
- for (const key of Object.keys(groups).sort()) {
77
+ for (const key of Object.keys(groups).toSorted()) {
78
78
  const group = groups[key];
79
79
  if (group.length > 0) {
80
80
  out.push('', key.charAt(0).toUpperCase() + key.slice(1));
@@ -52,5 +52,13 @@ export declare function lookupGenerators(options?: LookupOptions, register?: (me
52
52
  export declare function lookupGenerator(namespace: string, options?: ModuleLookupOptions & {
53
53
  packagePath?: boolean;
54
54
  generatorPath?: boolean;
55
- }): string | string[];
55
+ } & {
56
+ singleResult?: true;
57
+ }): string;
58
+ export declare function lookupGenerator(namespace: string, options?: ModuleLookupOptions & {
59
+ packagePath?: boolean;
60
+ generatorPath?: boolean;
61
+ } & {
62
+ singleResult: false;
63
+ }): string[];
56
64
  export {};
@@ -39,7 +39,7 @@ export async function lookupGenerators(options = {}, register) {
39
39
  ...options,
40
40
  };
41
41
  return moduleLookupSync(options, ({ packagePath, files }) => {
42
- files = [...files].sort((a, b) => {
42
+ files = [...files].toSorted((a, b) => {
43
43
  return defaultExtensions.indexOf(extname(a)) - defaultExtensions.indexOf(extname(b));
44
44
  });
45
45
  for (const filePath of files) {
@@ -51,18 +51,6 @@ export async function lookupGenerators(options = {}, register) {
51
51
  return;
52
52
  });
53
53
  }
54
- /**
55
- * Lookup for a specific generator.
56
- *
57
- * @param {String} namespace
58
- * @param {Object} [options]
59
- * @param {Boolean} [options.localOnly=false] - Set true to skip lookups of
60
- * globally-installed generators.
61
- * @param {Boolean} [options.packagePath=false] - Set true to return the package
62
- * path instead of generators file.
63
- * @param {Boolean} [options.singleResult=true] - Set false to return multiple values.
64
- * @return {String} generator
65
- */
66
54
  export function lookupGenerator(namespace, options) {
67
55
  options = typeof options === 'boolean' ? { localOnly: options } : (options ?? {});
68
56
  options.singleResult = options.singleResult ?? true;
@@ -7,7 +7,7 @@ export type PackageManagerInstallTaskOptions = {
7
7
  packageJsonLocation: string;
8
8
  adapter: InputOutputAdapter;
9
9
  nodePackageManager?: string;
10
- customInstallTask?: boolean | InstallTask;
10
+ customInstallTask?: boolean | InstallTask | 'ask';
11
11
  skipInstall?: boolean;
12
12
  };
13
13
  /**
@@ -23,7 +23,10 @@ export async function packageManagerInstallTask({ memFs, packageJsonLocation, cu
23
23
  * @return {Vinyl | undefined} a Vinyl file.
24
24
  */
25
25
  function getDestinationPackageJson() {
26
- return memFs.get(join(packageJsonLocation, 'package.json'));
26
+ const packageJsonFile = join(packageJsonLocation, 'package.json');
27
+ if (memFs.existsInMemory(packageJsonFile)) {
28
+ return memFs.get(packageJsonFile);
29
+ }
27
30
  }
28
31
  /**
29
32
  * @private
@@ -32,12 +35,12 @@ export async function packageManagerInstallTask({ memFs, packageJsonLocation, cu
32
35
  */
33
36
  function isDestinationPackageJsonCommitted() {
34
37
  const file = getDestinationPackageJson();
35
- return file.committed;
38
+ return file?.committed;
36
39
  }
37
40
  if (!getDestinationPackageJson()) {
38
41
  return false;
39
42
  }
40
- if (customInstallTask && typeof customInstallTask !== 'function') {
43
+ if (customInstallTask && typeof customInstallTask !== 'function' && customInstallTask !== 'ask') {
41
44
  debug('Install disabled by customInstallTask');
42
45
  return false;
43
46
  }
@@ -68,8 +71,18 @@ Running ${packageManagerName} install for you to install the required dependenci
68
71
  await execa(packageManagerName, ['install'], { stdio: 'inherit', cwd: packageJsonLocation });
69
72
  return true;
70
73
  };
71
- if (customInstallTask) {
74
+ if (typeof customInstallTask === 'function') {
72
75
  return customInstallTask(packageManagerName, execPackageManager);
73
76
  }
77
+ if (customInstallTask === 'ask') {
78
+ const { runInstall } = await adapter.prompt({
79
+ type: 'confirm',
80
+ name: 'runInstall',
81
+ message: `Do you want to run ${packageManagerName} install now?`,
82
+ });
83
+ if (!runInstall) {
84
+ return false;
85
+ }
86
+ }
74
87
  return execPackageManager();
75
88
  }
package/dist/store.d.ts CHANGED
@@ -8,6 +8,7 @@ import type { BaseEnvironment, BaseGeneratorMeta, GeneratorMeta, GetGeneratorCon
8
8
  */
9
9
  export default class Store {
10
10
  private readonly _meta;
11
+ private readonly _packagesJson;
11
12
  private readonly _packagesPaths;
12
13
  private readonly _packagesNS;
13
14
  private readonly environment;
@@ -61,6 +62,10 @@ export default class Store {
61
62
  * @return {Array} Stored packages namespaces.
62
63
  */
63
64
  getPackagesNS(): string[];
65
+ /**
66
+ * Read a package.json from packagePath. Parsed results are cached by packagePath.
67
+ */
68
+ private getPackageJson;
64
69
  private getFactory;
65
70
  private _getGenerator;
66
71
  }
package/dist/store.js CHANGED
@@ -9,6 +9,7 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
9
9
  import { pathToFileURL } from 'node:url';
10
10
  import { extname, join } from 'node:path';
11
11
  import { createRequire } from 'node:module';
12
+ import { readFileSync } from 'node:fs';
12
13
  import { toNamespace } from '@yeoman/namespace';
13
14
  import createDebug from 'debug';
14
15
  const debug = createDebug('yeoman:environment:store');
@@ -22,6 +23,8 @@ const require = createRequire(import.meta.url);
22
23
  */
23
24
  export default class Store {
24
25
  _meta = {};
26
+ // Cache parsed package.json by packagePath
27
+ _packagesJson = new Map();
25
28
  // Store packages paths by ns
26
29
  _packagesPaths = {};
27
30
  // Store packages ns
@@ -117,6 +120,7 @@ export default class Store {
117
120
  };
118
121
  const instantiate = async (arguments_ = [], options = {}) => this.environment.instantiate(await importGenerator(), { generatorArgs: arguments_, generatorOptions: options });
119
122
  const instantiateHelp = async () => instantiate([], { help: true });
123
+ const getPackageJson = () => this.getPackageJson(meta.packagePath);
120
124
  const { packageNamespace } = toNamespace(meta.namespace) ?? {};
121
125
  generatorMeta = {
122
126
  ...meta,
@@ -124,6 +128,7 @@ export default class Store {
124
128
  importModule,
125
129
  instantiate,
126
130
  instantiateHelp,
131
+ getPackageJson,
127
132
  packageNamespace,
128
133
  };
129
134
  this._meta[meta.namespace] = generatorMeta;
@@ -211,6 +216,25 @@ export default class Store {
211
216
  getPackagesNS() {
212
217
  return this._packagesNS;
213
218
  }
219
+ /**
220
+ * Read a package.json from packagePath. Parsed results are cached by packagePath.
221
+ */
222
+ getPackageJson(packagePath) {
223
+ if (!packagePath) {
224
+ return undefined;
225
+ }
226
+ if (!this._packagesJson.has(packagePath)) {
227
+ let packageJson;
228
+ try {
229
+ packageJson = JSON.parse(readFileSync(join(packagePath, 'package.json'), 'utf8'));
230
+ }
231
+ catch {
232
+ packageJson = undefined;
233
+ }
234
+ this._packagesJson.set(packagePath, packageJson);
235
+ }
236
+ return this._packagesJson.get(packagePath);
237
+ }
214
238
  getFactory(module) {
215
239
  // CJS is imported in default, for backward compatibility we support a Generator exported as `module.exports = { default }`
216
240
  return module.createGenerator ?? module.default?.createGenerator ?? module.default?.default?.createGenerator;
@@ -39,7 +39,7 @@ export const asNamespace = (filepath, { lookups = defaultLookups }) => {
39
39
  // Sort lookups by length so biggest are removed first
40
40
  const nsLookups = [...lookups, '..']
41
41
  .map(found => slash(found))
42
- .sort((a, b) => a.split('/').length - b.split('/').length)
42
+ .toSorted((a, b) => a.split('/').length - b.split('/').length)
43
43
  .toReversed();
44
44
  // If `ns` contains a lookup dir in its path, remove it.
45
45
  for (const lookup of nsLookups) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yeoman-environment",
3
- "version": "6.0.1",
3
+ "version": "6.2.0",
4
4
  "description": "Handles the lifecyle and bootstrapping of generators in a specific environment",
5
5
  "keywords": [
6
6
  "development",
@@ -16,7 +16,10 @@
16
16
  "app"
17
17
  ],
18
18
  "homepage": "http://yeoman.io",
19
- "repository": "yeoman/environment",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/yeoman/environment.git"
22
+ },
20
23
  "license": "BSD-2-Clause",
21
24
  "author": "Yeoman",
22
25
  "type": "module",
@@ -45,22 +48,18 @@
45
48
  "doc:prettier": "prettier $npm_package_config_doc_path --write --ignore-path .prettierignore-doc",
46
49
  "fix": "eslint --fix && prettier . --write",
47
50
  "prepare": "npm run build",
48
- "pretest": "eslint && prettier . --check",
49
- "test": "c8 esmocha --forbid-only",
50
- "test-base": "c8 esmocha test/environment*.js test/store.js test/util.js test/adapter.js",
51
- "test-environment": "c8 esmocha test/environment.js",
52
- "test-generators": "c8 esmocha test/generators.js",
53
- "test-resolver": "c8 esmocha test/resolver.js"
51
+ "pretest": "eslint && prettier . --check && tsc -p tsconfig.test.json",
52
+ "test": "c8 --include 'src/**' esmocha --forbid-only"
54
53
  },
55
54
  "config": {
56
55
  "doc_path": "./yeoman-environment-doc"
57
56
  },
58
57
  "dependencies": {
59
58
  "@yeoman/adapter": "^4.0.2",
60
- "@yeoman/conflicter": "^4.0.1",
61
- "@yeoman/namespace": "^1.0.1",
62
- "@yeoman/transform": "^2.1.0",
63
- "@yeoman/types": "^1.10.3",
59
+ "@yeoman/conflicter": "^4.1.0",
60
+ "@yeoman/namespace": "^2.1.0",
61
+ "@yeoman/transform": "^2.1.1",
62
+ "@yeoman/types": "^1.11.1",
64
63
  "arrify": "^3.0.0",
65
64
  "chalk": "^5.6.2",
66
65
  "commander": "^14.0.3",
@@ -72,7 +71,7 @@
72
71
  "locate-path": "^8.0.0",
73
72
  "lodash-es": "^4.18.1",
74
73
  "mem-fs": "^4.1.4",
75
- "mem-fs-editor": "^12.0.3",
74
+ "mem-fs-editor": "^12.0.4",
76
75
  "semver": "^7.7.4",
77
76
  "slash": "^5.1.0",
78
77
  "untildify": "^6.0.0",
@@ -84,27 +83,17 @@
84
83
  "@types/lodash-es": "^4.17.12",
85
84
  "@types/node": "^20.19.35",
86
85
  "@types/semver": "^7.7.1",
87
- "@yeoman/eslint": "1.0.0",
86
+ "@yeoman/eslint": "1.1.0",
88
87
  "c8": "^11.0.0",
89
- "eslint": "9.39.3",
88
+ "eslint": "10.2.1",
90
89
  "esmocha": "^5.0.0",
91
- "fs-extra": "^11.3.4",
92
90
  "jsdoc": "^4.0.5",
93
- "prettier": "3.8.1",
91
+ "prettier": "3.8.3",
94
92
  "prettier-plugin-packagejson": "^3.0.2",
95
93
  "rimraf": "^6.1.3",
96
- "sinon": "^21.1.0",
97
- "sinon-test": "^3.1.6",
98
94
  "strip-ansi": "^7.2.0",
99
- "typescript": "5.9.3",
100
- "yeoman-assert": "^3.1.1",
95
+ "typescript": "6.0.3",
101
96
  "yeoman-environment": "file:./",
102
- "yeoman-generator-2": "npm:yeoman-generator@^2.0.5",
103
- "yeoman-generator-4": "npm:yeoman-generator@^4.13.0",
104
- "yeoman-generator-5": "npm:yeoman-generator@^5.10.0",
105
- "yeoman-generator-6": "npm:yeoman-generator@^6.0.1",
106
- "yeoman-generator-7": "npm:yeoman-generator@^7.5.1",
107
- "yeoman-generator-8": "npm:yeoman-generator@^8.0.0-beta.8",
108
97
  "yeoman-test": "^11.3.1"
109
98
  },
110
99
  "peerDependencies": {
@@ -112,75 +101,18 @@
112
101
  "@yeoman/types": "^1.10.3",
113
102
  "mem-fs": "^4.1.4"
114
103
  },
115
- "acceptDependencies": {
116
- "@yeoman/adapter": ">=4.0.2"
117
- },
118
104
  "engines": {
119
105
  "node": "^20.17.0 || >=22.9.0"
120
106
  },
107
+ "acceptDependencies": {
108
+ "@yeoman/adapter": ">=4.0.2"
109
+ },
121
110
  "overrides": {
122
- "yeoman-generator-2": {
123
- "chalk": "^4.1.0",
124
- "dargs": "^7.0.0",
125
- "debug": "^4.1.1",
126
- "execa": "^5.1.1",
127
- "github-username": "^6.0.0",
128
- "lodash": "^4.17.11",
129
- "mem-fs-editor": "^9.0.0",
130
- "minimist": "^1.2.5",
131
- "pacote": "^15.2.0",
132
- "read-pkg-up": "^7.0.1",
133
- "run-async": "^2.0.0",
134
- "semver": "^7.2.1",
135
- "shelljs": "^0.8.5",
136
- "sort-keys": "^4.2.0",
137
- "text-table": "^0.2.0",
138
- "yeoman-environment": "file:./"
139
- },
140
- "yeoman-generator-4": {
141
- "chalk": "^4.1.0",
142
- "dargs": "^7.0.0",
143
- "debug": "^4.1.1",
144
- "execa": "^5.1.1",
145
- "github-username": "^6.0.0",
146
- "lodash": "^4.17.11",
147
- "mem-fs-editor": "^9.0.0",
148
- "minimist": "^1.2.5",
149
- "pacote": "^15.2.0",
150
- "read-pkg-up": "^7.0.1",
151
- "run-async": "^2.0.0",
152
- "semver": "^7.2.1",
153
- "shelljs": "^0.8.5",
154
- "sort-keys": "^4.2.0",
155
- "text-table": "^0.2.0",
156
- "yeoman-environment": "file:./"
157
- },
158
- "yeoman-generator-5": {
159
- "chalk": "^4.1.0",
160
- "dargs": "^7.0.0",
161
- "debug": "^4.1.1",
162
- "execa": "^5.1.1",
163
- "github-username": "^6.0.0",
164
- "lodash": "^4.17.11",
165
- "mem-fs-editor": "^9.0.0",
166
- "minimist": "^1.2.5",
167
- "pacote": "^15.2.0",
168
- "read-pkg-up": "^7.0.1",
169
- "run-async": "^2.0.0",
170
- "semver": "^7.2.1",
171
- "shelljs": "^0.8.5",
172
- "sort-keys": "^4.2.0",
173
- "text-table": "^0.2.0",
174
- "yeoman-environment": "file:./"
175
- },
176
- "yeoman-generator-6": {
177
- "yeoman-environment": "file:./"
178
- },
179
- "yeoman-generator-7": {
180
- "yeoman-environment": "file:./"
181
- },
182
- "yeoman-generator-8": {
111
+ "@yeoman-environment/generator-tests": {
183
112
  "yeoman-environment": "file:./"
184
113
  }
185
- }
114
+ },
115
+ "workspaces": [
116
+ "test/generators"
117
+ ]
186
118
  }