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.
Files changed (73) hide show
  1. package/bin/bin.cjs +4 -0
  2. package/dist/cli/index.d.ts +1 -0
  3. package/dist/cli/index.js +18 -15
  4. package/dist/cli/utils.d.ts +8 -0
  5. package/dist/cli/utils.js +6 -7
  6. package/dist/commands.d.ts +23 -0
  7. package/dist/commands.js +45 -0
  8. package/dist/commit.d.ts +17 -0
  9. package/dist/commit.js +22 -0
  10. package/dist/composed-store.d.ts +26 -0
  11. package/dist/composed-store.js +68 -0
  12. package/dist/constants.d.ts +3 -0
  13. package/dist/constants.js +17 -0
  14. package/dist/environment-base.d.ts +259 -0
  15. package/dist/environment-base.js +641 -0
  16. package/dist/environment-full.d.ts +115 -0
  17. package/dist/environment-full.js +321 -0
  18. package/dist/generator-lookup.d.ts +56 -0
  19. package/dist/generator-lookup.js +97 -0
  20. package/dist/index.d.ts +10 -0
  21. package/dist/index.js +9 -5
  22. package/dist/module-lookup.d.ts +59 -0
  23. package/dist/module-lookup.js +227 -0
  24. package/dist/package-manager.d.ts +18 -0
  25. package/dist/package-manager.js +52 -69
  26. package/dist/store.d.ts +66 -0
  27. package/dist/store.js +76 -47
  28. package/dist/util/command.d.ts +34 -0
  29. package/dist/util/command.js +32 -17
  30. package/dist/util/namespace.d.ts +26 -0
  31. package/dist/util/namespace.js +56 -190
  32. package/dist/util/resolve.d.ts +6 -0
  33. package/dist/util/resolve.js +40 -0
  34. package/dist/util/util.d.ts +7 -0
  35. package/dist/util/util.js +24 -23
  36. package/package.json +20 -33
  37. package/readme.md +2 -3
  38. package/dist/adapter.js +0 -97
  39. package/dist/adapter.js.map +0 -1
  40. package/dist/cli/index.js.map +0 -1
  41. package/dist/cli/utils.js.map +0 -1
  42. package/dist/command.js +0 -74
  43. package/dist/command.js.map +0 -1
  44. package/dist/composability.js +0 -78
  45. package/dist/composability.js.map +0 -1
  46. package/dist/environment.js +0 -1221
  47. package/dist/environment.js.map +0 -1
  48. package/dist/generator-features.js +0 -69
  49. package/dist/generator-features.js.map +0 -1
  50. package/dist/index.js.map +0 -1
  51. package/dist/namespace-composability.js +0 -340
  52. package/dist/namespace-composability.js.map +0 -1
  53. package/dist/package-manager.js.map +0 -1
  54. package/dist/resolver.js +0 -421
  55. package/dist/resolver.js.map +0 -1
  56. package/dist/spawn-command.js +0 -30
  57. package/dist/spawn-command.js.map +0 -1
  58. package/dist/store.js.map +0 -1
  59. package/dist/util/binary-diff.js +0 -36
  60. package/dist/util/binary-diff.js.map +0 -1
  61. package/dist/util/command.js.map +0 -1
  62. package/dist/util/conflicter.js +0 -346
  63. package/dist/util/conflicter.js.map +0 -1
  64. package/dist/util/esm.js +0 -22
  65. package/dist/util/esm.js.map +0 -1
  66. package/dist/util/log.js +0 -165
  67. package/dist/util/log.js.map +0 -1
  68. package/dist/util/namespace.js.map +0 -1
  69. package/dist/util/repository.js +0 -223
  70. package/dist/util/repository.js.map +0 -1
  71. package/dist/util/transform.js +0 -149
  72. package/dist/util/transform.js.map +0 -1
  73. package/dist/util/util.js.map +0 -1
@@ -0,0 +1,115 @@
1
+ import { type LookupOptions } from './generator-lookup.js';
2
+ import EnvironmentBase, { type EnvironmentOptions } from './environment-base.js';
3
+ declare class FullEnvironment extends EnvironmentBase {
4
+ constructor(options?: EnvironmentOptions);
5
+ /**
6
+ * Load options passed to the Generator that should be used by the Environment.
7
+ *
8
+ * @param {Object} options
9
+ */
10
+ loadEnvironmentOptions(options: EnvironmentOptions): Pick<EnvironmentOptions, "skipInstall" | "nodePackageManager">;
11
+ /**
12
+ * Load options passed to the Environment that should be forwarded to the Generator.
13
+ *
14
+ * @param {Object} options
15
+ */
16
+ loadSharedOptions(options: EnvironmentOptions): Pick<EnvironmentOptions, "skipInstall" | "forceInstall" | "skipCache" | "skipLocalCache" | "skipParseOptions" | "localConfigOnly" | "askAnswered">;
17
+ /**
18
+ * @protected
19
+ * Outputs the general help and usage. Optionally, if generators have been
20
+ * registered, the list of available generators is also displayed.
21
+ *
22
+ * @param {String} name
23
+ */
24
+ help(name?: string): string;
25
+ /**
26
+ * @protected
27
+ * Returns the list of registered namespace.
28
+ * @return {Array}
29
+ */
30
+ namespaces(): string[];
31
+ /**
32
+ * @protected
33
+ * Returns stored generators meta
34
+ * @return {Object}
35
+ */
36
+ getGeneratorsMeta(): Record<string, import("@yeoman/types").GeneratorMeta>;
37
+ /**
38
+ * @protected
39
+ * Get registered generators names
40
+ *
41
+ * @return {Array}
42
+ */
43
+ getGeneratorNames(): (string | undefined)[];
44
+ /**
45
+ * Get last added path for a namespace
46
+ *
47
+ * @param {String} - namespace
48
+ * @return {String} - path of the package
49
+ */
50
+ getPackagePath(namespace: string): string | undefined;
51
+ /**
52
+ * Get paths for a namespace
53
+ *
54
+ * @param - namespace
55
+ * @return array of paths.
56
+ */
57
+ getPackagePaths(namespace: string): string[];
58
+ /**
59
+ * Generate a command for the generator and execute.
60
+ *
61
+ * @param {string} generatorNamespace
62
+ * @param {string[]} args
63
+ */
64
+ execute(generatorNamespace: string, args?: never[]): Promise<void>;
65
+ requireGenerator(namespace: string): Promise<any>;
66
+ /**
67
+ * Install generators at the custom local repository and register.
68
+ *
69
+ * @param {Object} packages - packages to install key(packageName): value(versionRange).
70
+ * @return {Boolean} - true if the install succeeded.
71
+ */
72
+ installLocalGenerators(packages: Record<string, string | undefined>): Promise<boolean>;
73
+ /**
74
+ * Lookup and register generators from the custom local repository.
75
+ *
76
+ * @param {String[]} [packagesToLookup='generator-*'] - packages to lookup.
77
+ */
78
+ lookupLocalPackages(packagesToLookup?: string[]): Promise<void>;
79
+ /**
80
+ * Lookup and register generators from the custom local repository.
81
+ *
82
+ * @private
83
+ * @param {YeomanNamespace[]} namespacesToLookup - namespaces to lookup.
84
+ * @return {Promise<Object[]>} List of generators
85
+ */
86
+ lookupLocalNamespaces(namespacesToLookup: string | string[]): Promise<void | never[]>;
87
+ /**
88
+ * Search for generators or sub generators by namespace.
89
+ *
90
+ * @private
91
+ * @param {boolean|Object} [options] options passed to lookup. Options singleResult,
92
+ * filePatterns and packagePatterns can be overridden
93
+ * @return {Array|Object} List of generators
94
+ */
95
+ lookupNamespaces(namespaces: string | string[], options?: LookupOptions): Promise<import("@yeoman/types").LookupGeneratorMeta[][]>;
96
+ /**
97
+ * Load or install namespaces based on the namespace flag
98
+ *
99
+ * @private
100
+ * @param {String|Array} - namespaces
101
+ * @return {boolean} - true if every required namespace was found.
102
+ */
103
+ prepareEnvironment(namespaces: string | string[]): Promise<boolean>;
104
+ /**
105
+ * Tries to locate and run a specific generator. The lookup is done depending
106
+ * on the provided arguments, options and the list of registered generators.
107
+ *
108
+ * When the environment was unable to resolve a generator, an error is raised.
109
+ *
110
+ * @param {String|Array} args
111
+ * @param {Object} [options]
112
+ */
113
+ run(args?: string[], options?: any): Promise<void>;
114
+ }
115
+ export default FullEnvironment;
@@ -0,0 +1,321 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { join } from 'node:path';
3
+ import { requireNamespace, toNamespace } from '@yeoman/namespace';
4
+ import { flyImport } from 'fly-import';
5
+ import { defaults, pick, uniq } from 'lodash-es';
6
+ import semver from 'semver';
7
+ import YeomanCommand from './util/command.js';
8
+ import EnvironmentBase from './environment-base.js';
9
+ import { splitArgsFromString } from './util/util.js';
10
+ class FullEnvironment extends EnvironmentBase {
11
+ constructor(options = {}, adapterCompat) {
12
+ if (adapterCompat) {
13
+ options.adapter = adapterCompat;
14
+ }
15
+ super(options);
16
+ this.loadSharedOptions(this.options);
17
+ if (this.sharedOptions.skipLocalCache === undefined) {
18
+ this.sharedOptions.skipLocalCache = true;
19
+ }
20
+ }
21
+ /**
22
+ * Load options passed to the Generator that should be used by the Environment.
23
+ *
24
+ * @param {Object} options
25
+ */
26
+ loadEnvironmentOptions(options) {
27
+ const environmentOptions = pick(options, ['skipInstall', 'nodePackageManager']);
28
+ defaults(this.options, environmentOptions);
29
+ return environmentOptions;
30
+ }
31
+ /**
32
+ * Load options passed to the Environment that should be forwarded to the Generator.
33
+ *
34
+ * @param {Object} options
35
+ */
36
+ loadSharedOptions(options) {
37
+ const optionsToShare = pick(options, [
38
+ 'skipInstall',
39
+ 'forceInstall',
40
+ 'skipCache',
41
+ 'skipLocalCache',
42
+ 'skipParseOptions',
43
+ 'localConfigOnly',
44
+ 'askAnswered',
45
+ ]);
46
+ Object.assign(this.sharedOptions, optionsToShare);
47
+ return optionsToShare;
48
+ }
49
+ /**
50
+ * @protected
51
+ * Outputs the general help and usage. Optionally, if generators have been
52
+ * registered, the list of available generators is also displayed.
53
+ *
54
+ * @param {String} name
55
+ */
56
+ help(name = 'init') {
57
+ const out = [
58
+ 'Usage: :binary: GENERATOR [args] [options]',
59
+ '',
60
+ 'General options:',
61
+ " --help # Print generator's options and usage",
62
+ ' -f, --force # Overwrite files that already exist',
63
+ '',
64
+ 'Please choose a generator below.',
65
+ '',
66
+ ];
67
+ const ns = this.namespaces();
68
+ const groups = {};
69
+ for (const namespace of ns) {
70
+ const base = namespace.split(':')[0];
71
+ if (!groups[base]) {
72
+ groups[base] = [];
73
+ }
74
+ groups[base].push(namespace);
75
+ }
76
+ for (const key of Object.keys(groups).sort()) {
77
+ const group = groups[key];
78
+ if (group.length > 0) {
79
+ out.push('', key.charAt(0).toUpperCase() + key.slice(1));
80
+ }
81
+ for (const ns of groups[key]) {
82
+ out.push(` ${ns}`);
83
+ }
84
+ }
85
+ return out.join('\n').replace(/:binary:/g, name);
86
+ }
87
+ /**
88
+ * @protected
89
+ * Returns the list of registered namespace.
90
+ * @return {Array}
91
+ */
92
+ namespaces() {
93
+ return this.store.namespaces();
94
+ }
95
+ /**
96
+ * @protected
97
+ * Returns stored generators meta
98
+ * @return {Object}
99
+ */
100
+ getGeneratorsMeta() {
101
+ return this.store.getGeneratorsMeta();
102
+ }
103
+ /**
104
+ * @protected
105
+ * Get registered generators names
106
+ *
107
+ * @return {Array}
108
+ */
109
+ getGeneratorNames() {
110
+ return uniq(Object.keys(this.getGeneratorsMeta()).map(namespace => toNamespace(namespace)?.packageNamespace));
111
+ }
112
+ /**
113
+ * Get last added path for a namespace
114
+ *
115
+ * @param {String} - namespace
116
+ * @return {String} - path of the package
117
+ */
118
+ getPackagePath(namespace) {
119
+ if (namespace.includes(':')) {
120
+ const generator = this.getGeneratorMeta(namespace);
121
+ return generator?.packagePath;
122
+ }
123
+ const packagePaths = this.getPackagePaths(namespace) || [];
124
+ return packagePaths[0];
125
+ }
126
+ /**
127
+ * Get paths for a namespace
128
+ *
129
+ * @param - namespace
130
+ * @return array of paths.
131
+ */
132
+ getPackagePaths(namespace) {
133
+ return (this.store.getPackagesPaths()[namespace] || this.store.getPackagesPaths()[requireNamespace(this.alias(namespace)).packageNamespace]);
134
+ }
135
+ /**
136
+ * Generate a command for the generator and execute.
137
+ *
138
+ * @param {string} generatorNamespace
139
+ * @param {string[]} args
140
+ */
141
+ async execute(generatorNamespace, args = []) {
142
+ const namespace = requireNamespace(generatorNamespace);
143
+ if (!(await this.get(namespace.namespace))) {
144
+ await this.lookup({
145
+ packagePatterns: [namespace.generatorHint],
146
+ singleResult: true,
147
+ });
148
+ }
149
+ if (!(await this.get(namespace.namespace))) {
150
+ await this.installLocalGenerators({
151
+ [namespace.generatorHint]: namespace.semver,
152
+ });
153
+ }
154
+ const namespaceCommand = this.command ? this.command.command(namespace.namespace) : new YeomanCommand();
155
+ namespaceCommand.usage('[generator-options]');
156
+ // Instantiate the generator for options
157
+ const generator = await this.create(namespace.namespace, { generatorArgs: [], generatorOptions: { help: true } });
158
+ namespaceCommand.registerGenerator(generator);
159
+ namespaceCommand._parseCommand([], args);
160
+ return this.run([namespace.namespace, ...namespaceCommand.args], {
161
+ ...namespaceCommand.opts(),
162
+ });
163
+ }
164
+ async requireGenerator(namespace) {
165
+ if (namespace === undefined) {
166
+ try {
167
+ // @ts-expect-error yeoman-generator type maybe missing
168
+ // eslint-disable-next-line @typescript-eslint/naming-convention
169
+ const { default: Generator } = await import('yeoman-generator');
170
+ return Generator;
171
+ }
172
+ catch { }
173
+ // eslint-disable-next-line @typescript-eslint/naming-convention
174
+ const { default: Generator } = await flyImport('yeoman-generator');
175
+ return Generator;
176
+ }
177
+ // Namespace is a version
178
+ if (semver.valid(namespace)) {
179
+ // Create a hash to install any version range in the local repository
180
+ const hash = createHash('shake256', { outputLength: 2 }).update(namespace, 'utf8').digest('hex');
181
+ // eslint-disable-next-line @typescript-eslint/naming-convention
182
+ const { default: Generator } = await flyImport(`@yeoman/generator-impl-${hash}@npm:yeoman-generator@${namespace}`);
183
+ return Generator;
184
+ }
185
+ return this.get(namespace);
186
+ }
187
+ /**
188
+ * Install generators at the custom local repository and register.
189
+ *
190
+ * @param {Object} packages - packages to install key(packageName): value(versionRange).
191
+ * @return {Boolean} - true if the install succeeded.
192
+ */
193
+ async installLocalGenerators(packages) {
194
+ const entries = Object.entries(packages);
195
+ const specs = entries.map(([packageName, version]) => `${packageName}${version ? `@${version}` : ''}`);
196
+ const installResult = await this.repository.install(specs);
197
+ const failToInstall = installResult.find(result => !result.path);
198
+ if (failToInstall) {
199
+ throw new Error(`Fail to install ${failToInstall.pkgid}`);
200
+ }
201
+ await this.lookup({ packagePaths: installResult.map(result => result.path) });
202
+ return true;
203
+ }
204
+ /**
205
+ * Lookup and register generators from the custom local repository.
206
+ *
207
+ * @param {String[]} [packagesToLookup='generator-*'] - packages to lookup.
208
+ */
209
+ async lookupLocalPackages(packagesToLookup = ['generator-*']) {
210
+ await this.lookup({
211
+ packagePatterns: packagesToLookup,
212
+ npmPaths: [join(this.repository.repositoryPath, 'node_modules')],
213
+ });
214
+ }
215
+ /**
216
+ * Lookup and register generators from the custom local repository.
217
+ *
218
+ * @private
219
+ * @param {YeomanNamespace[]} namespacesToLookup - namespaces to lookup.
220
+ * @return {Promise<Object[]>} List of generators
221
+ */
222
+ async lookupLocalNamespaces(namespacesToLookup) {
223
+ if (!namespacesToLookup) {
224
+ return [];
225
+ }
226
+ namespacesToLookup = Array.isArray(namespacesToLookup) ? namespacesToLookup : [namespacesToLookup];
227
+ const namespaces = namespacesToLookup.map(ns => requireNamespace(ns));
228
+ // Keep only those packages that has a compatible version.
229
+ return this.lookupLocalPackages(namespaces.map(ns => ns.generatorHint));
230
+ }
231
+ /**
232
+ * Search for generators or sub generators by namespace.
233
+ *
234
+ * @private
235
+ * @param {boolean|Object} [options] options passed to lookup. Options singleResult,
236
+ * filePatterns and packagePatterns can be overridden
237
+ * @return {Array|Object} List of generators
238
+ */
239
+ async lookupNamespaces(namespaces, options = {}) {
240
+ if (!namespaces) {
241
+ return [];
242
+ }
243
+ namespaces = Array.isArray(namespaces) ? namespaces : [namespaces];
244
+ const namespacesObjs = namespaces.map(ns => requireNamespace(ns));
245
+ const options_ = namespacesObjs.map(ns => {
246
+ const nsOptions = { packagePatterns: [ns.generatorHint] };
247
+ if (ns.generator) {
248
+ // Build filePatterns to look specifically for the namespace.
249
+ const genPath = ns.generator.split(':').join('/');
250
+ let filePatterns = [`${genPath}/index.?s`, `${genPath}.?s`];
251
+ const lookups = options.lookups ?? this.lookups;
252
+ filePatterns = lookups.flatMap(prefix => filePatterns.map(pattern => join(prefix, pattern)));
253
+ nsOptions.filePatterns = filePatterns;
254
+ nsOptions.singleResult = true;
255
+ }
256
+ return nsOptions;
257
+ });
258
+ return Promise.all(options_.flatMap(async (opt) => this.lookup({ ...opt, ...options })));
259
+ }
260
+ /**
261
+ * Load or install namespaces based on the namespace flag
262
+ *
263
+ * @private
264
+ * @param {String|Array} - namespaces
265
+ * @return {boolean} - true if every required namespace was found.
266
+ */
267
+ async prepareEnvironment(namespaces) {
268
+ namespaces = Array.isArray(namespaces) ? namespaces : [namespaces];
269
+ let missing = namespaces.map(ns => requireNamespace(ns));
270
+ const updateMissing = async () => {
271
+ const entries = await Promise.all(missing.map(async (ns) => [ns, await this.get(ns)]));
272
+ missing = entries.filter(([_ns, gen]) => Boolean(gen)).map(([ns]) => ns);
273
+ };
274
+ await updateMissing();
275
+ // Install missing
276
+ const toInstall = Object.fromEntries(missing.map(ns => [ns.generatorHint, ns.semver]));
277
+ await this.installLocalGenerators(toInstall);
278
+ await updateMissing();
279
+ if (missing.length === 0) {
280
+ return true;
281
+ }
282
+ throw new Error(`Error preparing environment for ${missing.map(ns => ns.complete).join(',')}`);
283
+ }
284
+ /**
285
+ * Tries to locate and run a specific generator. The lookup is done depending
286
+ * on the provided arguments, options and the list of registered generators.
287
+ *
288
+ * When the environment was unable to resolve a generator, an error is raised.
289
+ *
290
+ * @param {String|Array} args
291
+ * @param {Object} [options]
292
+ */
293
+ async run(args, options) {
294
+ args = Array.isArray(args) ? args : splitArgsFromString(args);
295
+ options = { ...options };
296
+ const name = args.shift();
297
+ if (!name) {
298
+ throw new Error('Must provide at least one argument, the generator namespace to invoke.');
299
+ }
300
+ this.loadEnvironmentOptions(options);
301
+ if (this.experimental && !this.getGeneratorMeta(name)) {
302
+ try {
303
+ await this.prepareEnvironment(name);
304
+ }
305
+ catch { }
306
+ }
307
+ const generator = await this.create(name, {
308
+ generatorArgs: args,
309
+ generatorOptions: {
310
+ ...options,
311
+ initialGenerator: true,
312
+ },
313
+ });
314
+ if (options.help) {
315
+ console.log(generator.help());
316
+ return undefined;
317
+ }
318
+ return this.runGenerator(generator);
319
+ }
320
+ }
321
+ export default FullEnvironment;
@@ -0,0 +1,56 @@
1
+ import { type LookupOptions as LookupOptionsApi } from '@yeoman/types';
2
+ import { type ModuleLookupOptions } from './module-lookup.js';
3
+ export type LookupOptions = LookupOptionsApi & ModuleLookupOptions & {
4
+ lookups?: string[];
5
+ };
6
+ type LookupMeta = {
7
+ filePath: string;
8
+ packagePath: string;
9
+ lookups: string[];
10
+ };
11
+ export declare const defaultExtensions: string[];
12
+ /**
13
+ * Search for generators and their sub generators.
14
+ *
15
+ * A generator is a `:lookup/:name/index.js` file placed inside an npm package.
16
+ *
17
+ * Defaults lookups are:
18
+ * - ./
19
+ * - generators/
20
+ * - lib/generators/
21
+ *
22
+ * So this index file `node_modules/generator-dummy/lib/generators/yo/index.js` would be
23
+ * registered as `dummy:yo` generator.
24
+ *
25
+ * @param {boolean|Object} [options]
26
+ * @param {boolean} [options.localOnly = false] - Set true to skip lookups of
27
+ * globally-installed generators.
28
+ * @param {string|Array} [options.packagePaths] - Paths to look for generators.
29
+ * @param {string|Array} [options.npmPaths] - Repository paths to look for generators packages.
30
+ * @param {string|Array} [options.filePatterns='*\/index.js'] - File pattern to look for.
31
+ * @param {string|Array} [options.packagePatterns='generator-*'] - Package pattern to look for.
32
+ * @param {boolean} [options.singleResult=false] - Set true to stop lookup on the first match.
33
+ * @param {Number} [options.globbyDeep] - Deep option to be passed to globby.
34
+ * @return {Promise<Object[]>} List of generators
35
+ */
36
+ export declare function lookupGenerators(options?: LookupOptions, register?: (meta: LookupMeta) => boolean): Promise<{
37
+ filePath: string;
38
+ packagePath: string;
39
+ }[]>;
40
+ /**
41
+ * Lookup for a specific generator.
42
+ *
43
+ * @param {String} namespace
44
+ * @param {Object} [options]
45
+ * @param {Boolean} [options.localOnly=false] - Set true to skip lookups of
46
+ * globally-installed generators.
47
+ * @param {Boolean} [options.packagePath=false] - Set true to return the package
48
+ * path instead of generators file.
49
+ * @param {Boolean} [options.singleResult=true] - Set false to return multiple values.
50
+ * @return {String} generator
51
+ */
52
+ export declare function lookupGenerator(namespace: string, options?: ModuleLookupOptions & {
53
+ packagePath?: boolean;
54
+ generatorPath?: boolean;
55
+ }): string | string[];
56
+ export {};
@@ -0,0 +1,97 @@
1
+ import { extname, isAbsolute, join, posix } from 'node:path';
2
+ import { pathToFileURL } from 'node:url';
3
+ import { requireNamespace, toNamespace } from '@yeoman/namespace';
4
+ import { moduleLookupSync, getNpmPaths, findPackagesIn } from './module-lookup.js';
5
+ import { asNamespace, defaultLookups } from './util/namespace.js';
6
+ export const defaultExtensions = ['.ts', '.cts', '.mts', '.js', '.cjs', '.mjs'];
7
+ /**
8
+ * Search for generators and their sub generators.
9
+ *
10
+ * A generator is a `:lookup/:name/index.js` file placed inside an npm package.
11
+ *
12
+ * Defaults lookups are:
13
+ * - ./
14
+ * - generators/
15
+ * - lib/generators/
16
+ *
17
+ * So this index file `node_modules/generator-dummy/lib/generators/yo/index.js` would be
18
+ * registered as `dummy:yo` generator.
19
+ *
20
+ * @param {boolean|Object} [options]
21
+ * @param {boolean} [options.localOnly = false] - Set true to skip lookups of
22
+ * globally-installed generators.
23
+ * @param {string|Array} [options.packagePaths] - Paths to look for generators.
24
+ * @param {string|Array} [options.npmPaths] - Repository paths to look for generators packages.
25
+ * @param {string|Array} [options.filePatterns='*\/index.js'] - File pattern to look for.
26
+ * @param {string|Array} [options.packagePatterns='generator-*'] - Package pattern to look for.
27
+ * @param {boolean} [options.singleResult=false] - Set true to stop lookup on the first match.
28
+ * @param {Number} [options.globbyDeep] - Deep option to be passed to globby.
29
+ * @return {Promise<Object[]>} List of generators
30
+ */
31
+ export async function lookupGenerators(options = {}, register) {
32
+ const { lookups = defaultLookups } = options;
33
+ options = {
34
+ // Js generators should be after, last will override registered one.
35
+ filePatterns: lookups.flatMap(prefix => defaultExtensions.map(ext => `${prefix}/*/index${ext}`)),
36
+ filterPaths: false,
37
+ packagePatterns: ['generator-*'],
38
+ reverse: !options.singleResult,
39
+ ...options,
40
+ };
41
+ return moduleLookupSync(options, ({ packagePath, files }) => {
42
+ files = [...files].sort((a, b) => {
43
+ return defaultExtensions.indexOf(extname(a)) - defaultExtensions.indexOf(extname(b));
44
+ });
45
+ for (const filePath of files) {
46
+ const registered = register?.({ filePath, packagePath, lookups });
47
+ if (options.singleResult && registered) {
48
+ return filePath;
49
+ }
50
+ }
51
+ return undefined;
52
+ });
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
+ export function lookupGenerator(namespace, options) {
67
+ options = typeof options === 'boolean' ? { localOnly: options } : options ?? {};
68
+ options.singleResult = options.singleResult ?? true;
69
+ options.filePatterns = options.filePatterns ?? defaultLookups.map(prefix => join(prefix, '*/index.{js,ts}'));
70
+ const ns = requireNamespace(namespace);
71
+ options.packagePatterns = options.packagePatterns ?? [ns.generatorHint];
72
+ options.npmPaths = options.npmPaths ?? getNpmPaths({ localOnly: options.localOnly }).reverse();
73
+ options.packagePatterns = options.packagePatterns ?? ['generator-*'];
74
+ options.packagePaths = options.packagePaths ?? findPackagesIn(options.npmPaths, options.packagePatterns);
75
+ let paths = options.singleResult ? undefined : [];
76
+ moduleLookupSync(options, ({ files, packagePath }) => {
77
+ for (const filename of files) {
78
+ const fileNs = asNamespace(filename, { lookups: defaultLookups });
79
+ const ns = toNamespace(fileNs);
80
+ if (namespace === fileNs || (options.packagePath && namespace === ns?.packageNamespace)) {
81
+ // Version 2.6.0 returned pattern instead of modulePath for options.packagePath
82
+ const returnPath = options.packagePath ? packagePath : options.generatorPath ? posix.join(filename, '../../') : filename;
83
+ if (options.singleResult) {
84
+ paths = returnPath;
85
+ return filename;
86
+ }
87
+ paths.push(returnPath);
88
+ }
89
+ }
90
+ return undefined;
91
+ });
92
+ if (options.singleResult) {
93
+ const generatorPath = paths;
94
+ return generatorPath && isAbsolute(generatorPath) ? pathToFileURL(generatorPath).toString() : generatorPath;
95
+ }
96
+ return paths.map(gen => (isAbsolute(gen) ? pathToFileURL(gen).toString() : gen));
97
+ }
@@ -0,0 +1,10 @@
1
+ import { type EnvironmentOptions } from './environment-base.js';
2
+ import Environment from './environment-full.js';
3
+ export { default } from './environment-full.js';
4
+ export { default as EnvironmentBase } from './environment-base.js';
5
+ export declare const createEnv: (options?: EnvironmentOptions) => Environment;
6
+ export * from './commands.js';
7
+ export * from './util/command.js';
8
+ export * from './package-manager.js';
9
+ export * from './commit.js';
10
+ export { lookupGenerator } from './generator-lookup.js';
package/dist/index.js CHANGED
@@ -1,5 +1,9 @@
1
- export { default as TerminalAdapter } from './adapter.js';
2
- export { default as Conflicter } from './util/conflicter.js';
3
- export { default as createLogger } from './util/log.js';
4
- export { default } from './environment.js';
5
- //# sourceMappingURL=index.js.map
1
+ import Environment from './environment-full.js';
2
+ export { default } from './environment-full.js';
3
+ export { default as EnvironmentBase } from './environment-base.js';
4
+ export const createEnv = (options) => new Environment(options);
5
+ export * from './commands.js';
6
+ export * from './util/command.js';
7
+ export * from './package-manager.js';
8
+ export * from './commit.js';
9
+ export { lookupGenerator } from './generator-lookup.js';
@@ -0,0 +1,59 @@
1
+ export type ModuleLookupOptions = {
2
+ /** Set true to skip lookups of globally-installed generators */
3
+ localOnly?: boolean;
4
+ /** Paths to look for generators */
5
+ packagePaths?: string[];
6
+ /** Repository paths to look for generators packages */
7
+ npmPaths?: string[];
8
+ /** File pattern to look for */
9
+ filePatterns?: string[];
10
+ /** The package patterns to look for */
11
+ packagePatterns?: string[];
12
+ /** A value indicating whether the lookup should be stopped after finding the first result */
13
+ singleResult?: boolean;
14
+ filterPaths?: boolean;
15
+ /** Set true reverse npmPaths/packagePaths order */
16
+ reverse?: boolean;
17
+ /** The `deep` option to pass to `globby` */
18
+ globbyDeep?: number;
19
+ globbyOptions?: any;
20
+ };
21
+ /**
22
+ * Search for npm packages.
23
+ */
24
+ export declare function moduleLookupSync(options: ModuleLookupOptions, find: (arg: {
25
+ files: string[];
26
+ packagePath: string;
27
+ }) => string | undefined): {
28
+ filePath: string;
29
+ packagePath: string;
30
+ }[];
31
+ /**
32
+ * Search npm for every available generators.
33
+ * Generators are npm packages who's name start with `generator-` and who're placed in the
34
+ * top level `node_module` path. They can be installed globally or locally.
35
+ *
36
+ * @method
37
+ *
38
+ * @param searchPaths List of search paths
39
+ * @param packagePatterns Pattern of the packages
40
+ * @param globbyOptions
41
+ * @return List of the generator modules path
42
+ */
43
+ export declare function findPackagesIn(searchPaths: string[], packagePatterns: string[], globbyOptions?: any): any[];
44
+ /**
45
+ * Get the npm lookup directories (`node_modules/`)
46
+ *
47
+ * @method
48
+ *
49
+ * @param {boolean|Object} [options]
50
+ * @param {boolean} [options.localOnly = false] - Set true to skip lookups of
51
+ * globally-installed generators.
52
+ * @param {boolean} [options.filterPaths = false] - Remove paths that don't ends
53
+ * with a supported path (don't touch at NODE_PATH paths).
54
+ * @return {Array} lookup paths
55
+ */
56
+ export declare function getNpmPaths(options?: {
57
+ localOnly?: boolean;
58
+ filterPaths?: boolean;
59
+ }): string[];