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,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
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
-
|
|
2
|
-
export { default
|
|
3
|
-
export { default as
|
|
4
|
-
export
|
|
5
|
-
|
|
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[];
|