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.
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 +638 -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,227 @@
1
+ import { existsSync, lstatSync, readFileSync } from 'node:fs';
2
+ import { delimiter, dirname, join, resolve, sep } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import process from 'node:process';
5
+ import arrify from 'arrify';
6
+ import { uniq, compact } from 'lodash-es';
7
+ import { globbySync } from 'globby';
8
+ import slash from 'slash';
9
+ import createdLogger from 'debug';
10
+ import { execaOutput } from './util/util.js';
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = dirname(__filename);
13
+ // eslint-disable-next-line @typescript-eslint/naming-convention
14
+ const PROJECT_ROOT = join(__dirname, '..');
15
+ // eslint-disable-next-line @typescript-eslint/naming-convention
16
+ const PACKAGE_NAME_PATTERN = [JSON.parse(readFileSync(join(PROJECT_ROOT, 'package.json')).toString()).name];
17
+ const win32 = process.platform === 'win32';
18
+ const nvm = process.env.NVM_HOME;
19
+ const debug = createdLogger('yeoman:environment');
20
+ /**
21
+ * Search for npm packages.
22
+ */
23
+ export function moduleLookupSync(options, find) {
24
+ debug('Running lookup with options: %o', options);
25
+ options = { ...options };
26
+ options.filePatterns = arrify(options.filePatterns ?? 'package.json').map(filePattern => slash(filePattern));
27
+ if (options.packagePaths) {
28
+ options.packagePaths = arrify(options.packagePaths);
29
+ if (options.reverse) {
30
+ options.packagePaths = options.packagePaths.reverse();
31
+ }
32
+ }
33
+ else {
34
+ options.npmPaths = options.npmPaths ?? getNpmPaths(options);
35
+ if (options.reverse && Array.isArray(options.npmPaths)) {
36
+ options.npmPaths = options.npmPaths.reverse();
37
+ }
38
+ options.packagePatterns = arrify(options.packagePatterns ?? PACKAGE_NAME_PATTERN).map(packagePattern => slash(packagePattern));
39
+ options.packagePaths = findPackagesIn(options.npmPaths, options.packagePatterns);
40
+ }
41
+ debug('Lookup calculated options: %o', options);
42
+ const modules = [];
43
+ for (const packagePath of options.packagePaths) {
44
+ if (!existsSync(packagePath) || (!lstatSync(packagePath).isDirectory() && !lstatSync(packagePath).isSymbolicLink())) {
45
+ continue;
46
+ }
47
+ const files = globbySync(options.filePatterns, {
48
+ cwd: packagePath,
49
+ absolute: true,
50
+ ...options.globbyOptions,
51
+ });
52
+ const filePath = find({ files, packagePath });
53
+ if (filePath) {
54
+ return [{ filePath, packagePath }];
55
+ }
56
+ for (const filePath of files) {
57
+ modules.push({ filePath, packagePath });
58
+ }
59
+ }
60
+ return modules;
61
+ }
62
+ /**
63
+ * Search npm for every available generators.
64
+ * Generators are npm packages who's name start with `generator-` and who're placed in the
65
+ * top level `node_module` path. They can be installed globally or locally.
66
+ *
67
+ * @method
68
+ *
69
+ * @param searchPaths List of search paths
70
+ * @param packagePatterns Pattern of the packages
71
+ * @param globbyOptions
72
+ * @return List of the generator modules path
73
+ */
74
+ export function findPackagesIn(searchPaths, packagePatterns, globbyOptions) {
75
+ searchPaths = arrify(searchPaths)
76
+ .filter(Boolean)
77
+ .map(npmPath => resolve(npmPath));
78
+ let modules = [];
79
+ for (const root of searchPaths) {
80
+ if (!existsSync(root) || (!lstatSync(root).isDirectory() && !lstatSync(root).isSymbolicLink())) {
81
+ continue;
82
+ }
83
+ // Some folders might not be readable to the current user. For those, we add a try
84
+ // catch to handle the error gracefully as globby doesn't have an option to skip
85
+ // restricted folders.
86
+ try {
87
+ modules = modules.concat(globbySync(packagePatterns, {
88
+ cwd: root,
89
+ onlyDirectories: true,
90
+ expandDirectories: false,
91
+ absolute: true,
92
+ deep: 0,
93
+ ...globbyOptions,
94
+ }));
95
+ // To limit recursive lookups into non-namespace folders within globby,
96
+ // fetch all namespaces in root, then search each namespace separately
97
+ // for generator modules
98
+ const scopes = globbySync(['@*'], {
99
+ cwd: root,
100
+ onlyDirectories: true,
101
+ expandDirectories: false,
102
+ absolute: true,
103
+ deep: 0,
104
+ ...globbyOptions,
105
+ });
106
+ for (const scope of scopes) {
107
+ modules = modules.concat(globbySync(packagePatterns, {
108
+ cwd: scope,
109
+ onlyDirectories: true,
110
+ expandDirectories: false,
111
+ absolute: true,
112
+ deep: 0,
113
+ ...globbyOptions,
114
+ }));
115
+ }
116
+ }
117
+ catch (error) {
118
+ debug('Could not access %s (%s)', root, error);
119
+ }
120
+ }
121
+ return modules;
122
+ }
123
+ /**
124
+ * Get the npm lookup directories (`node_modules/`)
125
+ *
126
+ * @method
127
+ *
128
+ * @param {boolean|Object} [options]
129
+ * @param {boolean} [options.localOnly = false] - Set true to skip lookups of
130
+ * globally-installed generators.
131
+ * @param {boolean} [options.filterPaths = false] - Remove paths that don't ends
132
+ * with a supported path (don't touch at NODE_PATH paths).
133
+ * @return {Array} lookup paths
134
+ */
135
+ export function getNpmPaths(options = {}) {
136
+ // Resolve signature where options is boolean (localOnly).
137
+ if (typeof options === 'boolean') {
138
+ options = { localOnly: options };
139
+ }
140
+ // Start with the local paths.
141
+ let paths = getLocalNpmPaths();
142
+ // Append global paths, unless they should be excluded.
143
+ if (!options.localOnly) {
144
+ paths = paths.concat(getGlobalNpmPaths(options.filterPaths));
145
+ }
146
+ return uniq(paths);
147
+ }
148
+ /**
149
+ * Get the local npm lookup directories
150
+ * @private
151
+ * @return {Array} lookup paths
152
+ */
153
+ function getLocalNpmPaths() {
154
+ const paths = [];
155
+ // Walk up the CWD and add `node_modules/` folder lookup on each level
156
+ process
157
+ .cwd()
158
+ .split(sep)
159
+ .forEach((part, i, parts) => {
160
+ let lookup = join(...parts.slice(0, i + 1), 'node_modules');
161
+ if (!win32) {
162
+ lookup = `/${lookup}`;
163
+ }
164
+ paths.push(lookup);
165
+ });
166
+ return uniq(paths.reverse());
167
+ }
168
+ /**
169
+ * Get the global npm lookup directories
170
+ * Reference: https://nodejs.org/api/modules.html
171
+ * @private
172
+ * @return {Array} lookup paths
173
+ */
174
+ function getGlobalNpmPaths(filterPaths = true) {
175
+ let paths = [];
176
+ // Node.js will search in the following list of GLOBAL_FOLDERS:
177
+ // 1: $HOME/.node_modules
178
+ // 2: $HOME/.node_libraries
179
+ // 3: $PREFIX/lib/node
180
+ const filterValidNpmPath = function (path, ignore = false) {
181
+ return ignore ? [path] : ['/node_modules', '/.node_modules', '/.node_libraries', '/node'].some(dir => path.endsWith(dir)) ? [path] : [];
182
+ };
183
+ // Default paths for each system
184
+ if (nvm && process.env.NVM_HOME) {
185
+ paths.push(join(process.env.NVM_HOME, process.version, 'node_modules'));
186
+ }
187
+ else if (win32 && process.env.APPDATA) {
188
+ paths.push(join(process.env.APPDATA, 'npm/node_modules'));
189
+ }
190
+ else {
191
+ paths.push('/usr/lib/node_modules', '/usr/local/lib/node_modules');
192
+ }
193
+ // Add NVM prefix directory
194
+ if (process.env.NVM_PATH) {
195
+ paths.push(join(dirname(process.env.NVM_PATH), 'node_modules'));
196
+ }
197
+ // Adding global npm directories
198
+ // We tried using npm to get the global modules path, but it haven't work out
199
+ // because of bugs in the parseable implementation of `ls` command and mostly
200
+ // performance issues. So, we go with our best bet for now.
201
+ if (process.env.NODE_PATH) {
202
+ paths = compact(process.env.NODE_PATH.split(delimiter)).concat(paths);
203
+ }
204
+ // Global node_modules should be 4 or 2 directory up this one (most of the time)
205
+ // Ex: /usr/another_global/node_modules/yeoman-denerator/node_modules/yeoman-environment/lib (1 level dependency)
206
+ paths.push(...filterValidNpmPath(join(PROJECT_ROOT, '../../..'), !filterPaths));
207
+ // Ex: /usr/another_global/node_modules/yeoman-environment/lib (installed directly)
208
+ paths.push(join(PROJECT_ROOT, '..'));
209
+ // Get yarn global directory and infer the module paths from there
210
+ const yarnBase = execaOutput('yarn', ['global', 'dir'], { encoding: 'utf8' });
211
+ if (yarnBase) {
212
+ paths.push(resolve(yarnBase, 'node_modules'));
213
+ paths.push(resolve(yarnBase, '../link/'));
214
+ }
215
+ // Get npm global prefix and infer the module paths from there
216
+ const globalInstall = execaOutput('npm', ['root', '-g'], {
217
+ encoding: 'utf8',
218
+ });
219
+ if (globalInstall) {
220
+ paths.push(resolve(globalInstall));
221
+ }
222
+ // Adds support for generator resolving when yeoman-generator has been linked
223
+ if (process.argv[1]) {
224
+ paths.push(...filterValidNpmPath(join(dirname(process.argv[1]), '../..'), !filterPaths));
225
+ }
226
+ return uniq(paths.filter(Boolean).reverse());
227
+ }
@@ -0,0 +1,18 @@
1
+ import type { MemFsEditorFile } from 'mem-fs-editor';
2
+ import { type InputOutputAdapter } from '@yeoman/types';
3
+ import { type Store } from 'mem-fs';
4
+ export type PackageManagerInstallTaskOptions = {
5
+ memFs: Store<MemFsEditorFile>;
6
+ packageJsonLocation: string;
7
+ adapter: InputOutputAdapter;
8
+ nodePackageManager?: string;
9
+ customInstallTask?: boolean | ((nodePackageManager: string | undefined, defaultTask: () => Promise<boolean>) => void | Promise<void>);
10
+ skipInstall?: boolean;
11
+ };
12
+ /**
13
+ * Executes package manager install.
14
+ * - checks if package.json was committed.
15
+ * - uses a preferred package manager or try to detect.
16
+ * @return {Promise<boolean>} Promise true if the install execution suceeded.
17
+ */
18
+ export declare function packageManagerInstallTask({ memFs, packageJsonLocation, customInstallTask, adapter, nodePackageManager, skipInstall, }: PackageManagerInstallTaskOptions): Promise<boolean | void>;
@@ -1,92 +1,75 @@
1
- import path from 'node:path';
1
+ import { join, resolve } from 'node:path';
2
2
  import createdLogger from 'debug';
3
3
  import preferredPm from 'preferred-pm';
4
+ import { execa } from 'execa';
4
5
  const debug = createdLogger('yeoman:environment:package-manager');
5
- const packageManagerMixin = cls => class extends cls {
6
+ /**
7
+ * Executes package manager install.
8
+ * - checks if package.json was committed.
9
+ * - uses a preferred package manager or try to detect.
10
+ * @return {Promise<boolean>} Promise true if the install execution suceeded.
11
+ */
12
+ /*
13
+ const { customInstallTask } = this.composedStore;
14
+ packageJsonFile: join(this.cwd, 'package.json');
15
+
16
+ */
17
+ export async function packageManagerInstallTask({ memFs, packageJsonLocation, customInstallTask, adapter, nodePackageManager, skipInstall, }) {
18
+ packageJsonLocation = resolve(packageJsonLocation);
6
19
  /**
7
20
  * @private
8
21
  * Get the destination package.json file.
9
22
  * @return {Vinyl | undefined} a Vinyl file.
10
23
  */
11
- getDestinationPackageJson() {
12
- return this.sharedFs.get(path.resolve(this.cwd, 'package.json'));
24
+ function getDestinationPackageJson() {
25
+ return memFs.get(join(packageJsonLocation, 'package.json'));
13
26
  }
14
27
  /**
15
28
  * @private
16
29
  * Get the destination package.json commit status.
17
30
  * @return {boolean} package.json commit status.
18
31
  */
19
- isDestinationPackageJsonCommitted() {
20
- const file = this.getDestinationPackageJson();
21
- return file && file.committed;
32
+ function isDestinationPackageJsonCommitted() {
33
+ const file = getDestinationPackageJson();
34
+ return file.committed;
22
35
  }
23
- /**
24
- * @private
25
- * Detect the package manager based on files or use the passed one.
26
- * @return {string} package manager.
27
- */
28
- async detectPackageManager() {
29
- if (this.options.nodePackageManager) {
30
- return this.options.nodePackageManager;
31
- }
32
- const pm = await preferredPm(this.cwd);
33
- return pm && pm.name;
36
+ if (!getDestinationPackageJson()) {
37
+ return false;
34
38
  }
35
- /**
36
- * Executes package manager install.
37
- * - checks if package.json was committed.
38
- * - uses a preferred package manager or try to detect.
39
- * @return {Promise<boolean>} Promise true if the install execution suceeded.
40
- */
41
- async packageManagerInstallTask() {
42
- if (!this.getDestinationPackageJson()) {
43
- return false;
44
- }
45
- if (this.compatibilityMode === 'v4') {
46
- debug('Running in generator < 5 compatibility. Package manager install is done by the generator.');
47
- return false;
48
- }
49
- const customInstallTask = this.findGeneratorCustomInstallTask();
50
- if (customInstallTask && typeof customInstallTask !== 'function') {
51
- debug('Install disabled by customInstallTask');
52
- return false;
53
- }
54
- if (!this.isDestinationPackageJsonCommitted()) {
55
- this.adapter.log(`
39
+ if (customInstallTask && typeof customInstallTask !== 'function') {
40
+ debug('Install disabled by customInstallTask');
41
+ return false;
42
+ }
43
+ if (!isDestinationPackageJsonCommitted()) {
44
+ adapter.log(`
56
45
  No change to package.json was detected. No package manager install will be executed.`);
57
- return false;
58
- }
59
- this.adapter.log(`
46
+ return false;
47
+ }
48
+ adapter.log(`
60
49
  Changes to package.json were detected.`);
61
- if (this.options.skipInstall) {
62
- this.adapter.log(`Skipping package manager install.
50
+ if (skipInstall) {
51
+ adapter.log(`Skipping package manager install.
63
52
  `);
53
+ return false;
54
+ }
55
+ // eslint-disable-next-line unicorn/no-await-expression-member
56
+ let packageManagerName = nodePackageManager ?? (await preferredPm(packageJsonLocation))?.name;
57
+ const execPackageManager = async () => {
58
+ if (!packageManagerName) {
59
+ packageManagerName = 'npm';
60
+ adapter.log('Error detecting the package manager. Falling back to npm.');
61
+ }
62
+ if (!['npm', 'yarn', 'pnpm'].includes(packageManagerName)) {
63
+ adapter.log(`${packageManagerName} is not a supported package manager. Run it by yourself.`);
64
64
  return false;
65
65
  }
66
- let packageManagerName = await this.detectPackageManager();
67
- const execPackageManager = async () => {
68
- if (!packageManagerName) {
69
- packageManagerName = 'npm';
70
- this.adapter.log('Error detecting the package manager. Falling back to npm.');
71
- }
72
- if (!['npm', 'yarn', 'pnpm'].includes(packageManagerName)) {
73
- this.adapter.log(`${packageManagerName} is not a supported package manager. Run it by yourself.`);
74
- return false;
75
- }
76
- this.adapter.log(`
66
+ adapter.log(`
77
67
  Running ${packageManagerName} install for you to install the required dependencies.`);
78
- await this.spawnCommand(packageManagerName, ['install']);
79
- return true;
80
- };
81
- if (customInstallTask) {
82
- const result = customInstallTask(packageManagerName, execPackageManager);
83
- if (!result || !result.then) {
84
- return true;
85
- }
86
- return result;
87
- }
88
- return execPackageManager();
68
+ await execa(packageManagerName, ['install'], { stdio: 'inherit', cwd: packageJsonLocation });
69
+ return true;
70
+ };
71
+ if (customInstallTask) {
72
+ return customInstallTask(packageManagerName, execPackageManager);
89
73
  }
90
- };
91
- export default packageManagerMixin;
92
- //# sourceMappingURL=package-manager.js.map
74
+ return execPackageManager();
75
+ }
@@ -0,0 +1,66 @@
1
+ import type { BaseEnvironment, GetGeneratorConstructor, GeneratorMeta, BaseGeneratorMeta } from '@yeoman/types';
2
+ /**
3
+ * The Generator store
4
+ * This is used to store generator (npm packages) reference and instantiate them when
5
+ * requested.
6
+ * @constructor
7
+ * @private
8
+ */
9
+ export default class Store {
10
+ private readonly env;
11
+ private readonly _meta;
12
+ private readonly _packagesPaths;
13
+ private readonly _packagesNS;
14
+ constructor(env: BaseEnvironment);
15
+ /**
16
+ * Store a module under the namespace key
17
+ * @param meta
18
+ * @param generator - A generator module or a module path
19
+ */
20
+ add(meta: BaseGeneratorMeta, Generator?: unknown): GeneratorMeta;
21
+ /**
22
+ * Get the module registered under the given namespace
23
+ * @param {String} namespace
24
+ * @return {Module}
25
+ */
26
+ get(namespace: string): Promise<GetGeneratorConstructor>;
27
+ /**
28
+ * Get the module registered under the given namespace
29
+ * @param {String} namespace
30
+ * @return {Module}
31
+ */
32
+ getMeta(namespace: string): GeneratorMeta;
33
+ /**
34
+ * Returns the list of registered namespace.
35
+ * @return {Array} Namespaces array
36
+ */
37
+ namespaces(): string[];
38
+ /**
39
+ * Get the stored generators meta data
40
+ * @return {Object} Generators metadata
41
+ */
42
+ getGeneratorsMeta(): Record<string, GeneratorMeta>;
43
+ /**
44
+ * Store a package under the namespace key
45
+ * @param {String} packageNS - The key under which the generator can be retrieved
46
+ * @param {String} packagePath - The package path
47
+ */
48
+ addPackage(packageNS: string, packagePath: string): void;
49
+ /**
50
+ * Get the stored packages namespaces with paths.
51
+ * @return {Object} Stored packages namespaces with paths.
52
+ */
53
+ getPackagesPaths(): Record<string, string[]>;
54
+ /**
55
+ * Store a package ns
56
+ * @param {String} packageNS - The key under which the generator can be retrieved
57
+ */
58
+ addPackageNamespace(packageNS: string): void;
59
+ /**
60
+ * Get the stored packages namespaces.
61
+ * @return {Array} Stored packages namespaces.
62
+ */
63
+ getPackagesNS(): string[];
64
+ private getFactory;
65
+ private _getGenerator;
66
+ }
package/dist/store.js CHANGED
@@ -1,5 +1,7 @@
1
+ import { pathToFileURL } from 'node:url';
2
+ import { extname, join } from 'node:path';
3
+ import { toNamespace } from '@yeoman/namespace';
1
4
  import createDebug from 'debug';
2
- import { requireOrImport } from './util/esm.js';
3
5
  const debug = createDebug('yeoman:environment:store');
4
6
  /**
5
7
  * The Generator store
@@ -8,45 +10,71 @@ const debug = createDebug('yeoman:environment:store');
8
10
  * @constructor
9
11
  * @private
10
12
  */
11
- class Store {
12
- constructor() {
13
- this._meta = {};
14
- // Store packages paths by ns
15
- this._packagesPaths = {};
16
- // Store packages ns
17
- this._packagesNS = [];
13
+ export default class Store {
14
+ env;
15
+ _meta = {};
16
+ // Store packages paths by ns
17
+ _packagesPaths = {};
18
+ // Store packages ns
19
+ // eslint-disable-next-line @typescript-eslint/naming-convention
20
+ _packagesNS = [];
21
+ constructor(env) {
22
+ this.env = env;
18
23
  }
19
24
  /**
20
25
  * Store a module under the namespace key
21
- * @param {String} namespace - The key under which the generator can be retrieved
22
- * @param {String|Function} generator - A generator module or a module path
23
- * @param {String} packagePath - PackagePath to the generator npm package (optional)
24
- * @param {String} [resolved] - The file path to the generator (used only if generator is a module)
26
+ * @param meta
27
+ * @param generator - A generator module or a module path
25
28
  */
26
- add(namespace, generator, resolved, packagePath) {
27
- if (typeof generator === 'string') {
28
- this._storeAsPath(namespace, generator, packagePath);
29
- return;
29
+ add(meta, Generator) {
30
+ if (typeof meta.resolved === 'string') {
31
+ meta.resolved = extname(meta.resolved) ? join(meta.resolved) : join(meta.resolved, 'index.js');
30
32
  }
31
- this._storeAsModule(namespace, generator, resolved, packagePath);
32
- }
33
- _storeAsPath(namespace, resolvedPath, packagePath) {
34
- const importGenerator = () => requireOrImport(resolvedPath);
35
- this._meta[namespace] = {
36
- resolved: resolvedPath,
37
- namespace,
38
- packagePath,
39
- importGenerator,
33
+ if (meta.packagePath) {
34
+ meta.packagePath = join(meta.packagePath);
35
+ }
36
+ let importModule;
37
+ if (!Generator) {
38
+ if (!meta.resolved) {
39
+ throw new Error(`Generator Stub or resolved path is required for ${meta.namespace}`);
40
+ }
41
+ importModule = async () => import(pathToFileURL(meta.resolved).href);
42
+ }
43
+ let importPromise;
44
+ const importGenerator = async () => {
45
+ if (importPromise) {
46
+ Generator = await importPromise;
47
+ }
48
+ if (importModule && !Generator) {
49
+ importPromise = importModule();
50
+ Generator = await importPromise;
51
+ }
52
+ const factory = this.getFactory(Generator);
53
+ if (typeof factory === 'function') {
54
+ importPromise = factory(this.env);
55
+ Generator = await importPromise;
56
+ }
57
+ return this._getGenerator(Generator, meta);
40
58
  };
41
- }
42
- _storeAsModule(namespace, Generator, resolved, packagePath) {
43
- const importGenerator = () => Generator;
44
- this._meta[namespace] = {
45
- resolved,
46
- namespace,
47
- packagePath,
59
+ const instantiate = async (args = [], options = {}) => this.env.instantiate(await importGenerator(), { generatorArgs: args, generatorOptions: options });
60
+ const instantiateHelp = async () => instantiate([], { help: true });
61
+ const { packageNamespace } = toNamespace(meta.namespace) ?? {};
62
+ const generatorMeta = {
63
+ ...meta,
48
64
  importGenerator,
65
+ importModule,
66
+ instantiate,
67
+ instantiateHelp,
68
+ packageNamespace,
49
69
  };
70
+ this._meta[meta.namespace] = generatorMeta;
71
+ if (packageNamespace) {
72
+ this.addPackageNamespace(packageNamespace);
73
+ if (meta.packagePath) {
74
+ this.addPackage(packageNamespace, meta.packagePath);
75
+ }
76
+ }
77
+ return generatorMeta;
50
78
  }
51
79
  /**
52
80
  * Get the module registered under the given namespace
@@ -54,18 +82,7 @@ class Store {
54
82
  * @return {Module}
55
83
  */
56
84
  async get(namespace) {
57
- const meta = this.getMeta(namespace);
58
- if (!meta) {
59
- return;
60
- }
61
- const { importGenerator, ...additionalMeta } = meta;
62
- const maybeGenerator = importGenerator();
63
- if (maybeGenerator.then) {
64
- const esmGenerator = await maybeGenerator;
65
- return [esmGenerator, additionalMeta];
66
- }
67
- Object.assign(maybeGenerator, additionalMeta);
68
- return maybeGenerator;
85
+ return this.getMeta(namespace)?.importGenerator();
69
86
  }
70
87
  /**
71
88
  * Get the module registered under the given namespace
@@ -123,7 +140,7 @@ class Store {
123
140
  * Store a package ns
124
141
  * @param {String} packageNS - The key under which the generator can be retrieved
125
142
  */
126
- addPackageNS(packageNS) {
143
+ addPackageNamespace(packageNS) {
127
144
  if (!this._packagesNS.includes(packageNS)) {
128
145
  this._packagesNS.push(packageNS);
129
146
  }
@@ -132,9 +149,21 @@ class Store {
132
149
  * Get the stored packages namespaces.
133
150
  * @return {Array} Stored packages namespaces.
134
151
  */
152
+ // eslint-disable-next-line @typescript-eslint/naming-convention
135
153
  getPackagesNS() {
136
154
  return this._packagesNS;
137
155
  }
156
+ getFactory(module) {
157
+ // CJS is imported in default, for backward compatibility we support a Generator exported as `module.exports = { default }`
158
+ return module.createGenerator ?? module.default?.createGenerator ?? module.default?.default?.createGenerator;
159
+ }
160
+ _getGenerator(module, meta) {
161
+ // eslint-disable-next-line @typescript-eslint/naming-convention
162
+ const Generator = module.default?.default ?? module.default ?? module;
163
+ if (typeof Generator !== 'function') {
164
+ throw new TypeError("The generator doesn't provides a constructor.");
165
+ }
166
+ Object.assign(Generator, meta);
167
+ return Generator;
168
+ }
138
169
  }
139
- export default Store;
140
- //# sourceMappingURL=store.js.map
@@ -0,0 +1,34 @@
1
+ import { Command, Option } from 'commander';
2
+ import type BaseEnvironment from '../environment-base.js';
3
+ export default class YeomanCommand extends Command {
4
+ env?: BaseEnvironment;
5
+ createCommand(name?: string): YeomanCommand;
6
+ /**
7
+ * Override addOption to register a negative alternative for every option.
8
+ * @param {Option} option
9
+ * @return {YeomanCommand} this;
10
+ */
11
+ addOption(option: Option): this;
12
+ /**
13
+ * Load Generator options into a commander instance.
14
+ *
15
+ * @param {Generator} generator - Generator
16
+ * @return {Command} return command
17
+ */
18
+ registerGenerator(generator: any): this;
19
+ /**
20
+ * Register arguments using generator._arguments structure.
21
+ * @param {object[]} generatorArgs
22
+ * @return {YeomanCommand} this;
23
+ */
24
+ addGeneratorArguments(generatorArgs?: any[]): this;
25
+ /**
26
+ * Register options using generator._options structure.
27
+ * @param {object} options
28
+ * @param {string} blueprintOptionDescription - description of the blueprint that adds the option
29
+ * @return {YeomanCommand} this;
30
+ */
31
+ addGeneratorOptions(options: Record<string, any>): this;
32
+ _addGeneratorOption(optionName: string, optionDefinition: any, additionalDescription?: string): any;
33
+ }
34
+ export declare const addEnvironmentOptions: (command?: YeomanCommand) => YeomanCommand;