yeoman-environment 8.1.0 → 8.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/utils.js +6 -3
- package/dist/environment-base.d.ts +11 -1
- package/dist/environment-base.js +17 -8
- package/dist/environment-full.d.ts +2 -2
- package/dist/environment-full.js +9 -5
- package/dist/generator-lookup.d.ts +7 -2
- package/dist/generator-lookup.js +4 -4
- package/dist/module-lookup.js +4 -5
- package/dist/store.d.ts +1 -1
- package/dist/store.js +34 -17
- package/dist/util/namespace.d.ts +11 -1
- package/dist/util/namespace.js +21 -6
- package/dist/util/util.d.ts +13 -0
- package/dist/util/util.js +24 -0
- package/package.json +1 -3
package/dist/cli/utils.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { requireNamespace } from '@yeoman/namespace';
|
|
2
|
-
import { groupBy } from 'lodash-es';
|
|
3
2
|
import createLogger from 'debug';
|
|
4
3
|
import { createEnv as createEnvironment } from "../index.js";
|
|
5
4
|
const debug = createLogger('yeoman:yoe');
|
|
6
5
|
export const printGroupedGenerator = (generators) => {
|
|
7
|
-
const grouped =
|
|
8
|
-
for (const
|
|
6
|
+
const grouped = new Map();
|
|
7
|
+
for (const generator of generators) {
|
|
8
|
+
const packagePath = String(generator.packagePath);
|
|
9
|
+
grouped.set(packagePath, [...(grouped.get(packagePath) ?? []), generator]);
|
|
10
|
+
}
|
|
11
|
+
for (const [packagePath, group] of grouped) {
|
|
9
12
|
const namespace = requireNamespace(group[0].namespace);
|
|
10
13
|
console.log(` ${namespace.packageNamespace} at ${packagePath}`);
|
|
11
14
|
for (const generator of group) {
|
|
@@ -47,6 +47,15 @@ export type EnvironmentOptions = ConflicterOptions & BaseEnvironmentOptions & Om
|
|
|
47
47
|
*/
|
|
48
48
|
store?: Store;
|
|
49
49
|
};
|
|
50
|
+
/** The options to instantiate a generator with, and the namespace it was asked with. */
|
|
51
|
+
type EnvironmentInstantiateOptions<G extends BaseGenerator = BaseGenerator> = InstantiateOptions<G> & {
|
|
52
|
+
/**
|
|
53
|
+
* The namespace the generator is created for, complete with its version, instance id, methods and flags
|
|
54
|
+
* (`namespace@semver@#id+method?`), passed to the generator as the `namespaceId` option. The `namespace` option
|
|
55
|
+
* stays the one of the generator, without them.
|
|
56
|
+
*/
|
|
57
|
+
namespaceId?: string;
|
|
58
|
+
};
|
|
50
59
|
/**
|
|
51
60
|
* Copy and remove null and undefined values
|
|
52
61
|
* @param object
|
|
@@ -122,7 +131,7 @@ export default class EnvironmentBase extends EventEmitter implements BaseEnviron
|
|
|
122
131
|
* @param instantiateOptions
|
|
123
132
|
* @return The instantiated generator
|
|
124
133
|
*/
|
|
125
|
-
instantiate<G extends BaseGenerator = BaseGenerator>(generator: GetGeneratorConstructor<G>, instantiateOptions?:
|
|
134
|
+
instantiate<G extends BaseGenerator = BaseGenerator>(generator: GetGeneratorConstructor<G>, instantiateOptions?: EnvironmentInstantiateOptions<G>): Promise<G>;
|
|
126
135
|
/**
|
|
127
136
|
* @protected
|
|
128
137
|
* Compose with the generator.
|
|
@@ -314,3 +323,4 @@ export default class EnvironmentBase extends EventEmitter implements BaseEnviron
|
|
|
314
323
|
*/
|
|
315
324
|
getContextMap(key: string, factory?: () => Map<string, any>): Map<string, any>;
|
|
316
325
|
}
|
|
326
|
+
export {};
|
package/dist/environment-base.js
CHANGED
|
@@ -13,7 +13,6 @@ import { filePipeline, transform } from '@yeoman/transform';
|
|
|
13
13
|
import { toNamespace } from '@yeoman/namespace';
|
|
14
14
|
import chalk from 'chalk';
|
|
15
15
|
import {} from '@yeoman/conflicter';
|
|
16
|
-
import { defaults, pick } from 'lodash-es';
|
|
17
16
|
import { ComposedStore } from "./composed-store.js";
|
|
18
17
|
import Store, {} from "./store.js";
|
|
19
18
|
import {} from "./generator-lookup.js";
|
|
@@ -21,7 +20,7 @@ import { UNKNOWN_NAMESPACE, UNKNOWN_RESOLVED, defaultQueues } from "./constants.
|
|
|
21
20
|
import { resolveModulePath } from "./util/resolve.js";
|
|
22
21
|
import { commitSharedFsTask } from "./commit.js";
|
|
23
22
|
import { packageManagerInstallTask } from "./package-manager.js";
|
|
24
|
-
import { splitArgsFromString as splitArgumentsFromString } from "./util/util.js";
|
|
23
|
+
import { defaults, pick, splitArgsFromString as splitArgumentsFromString } from "./util/util.js";
|
|
25
24
|
const require = createRequire(import.meta.url);
|
|
26
25
|
const ENVIRONMENT_VERSION = require('../package.json').version;
|
|
27
26
|
const debug = createdLogger('yeoman:environment');
|
|
@@ -228,6 +227,17 @@ export default class EnvironmentBase extends EventEmitter {
|
|
|
228
227
|
}
|
|
229
228
|
return Generator;
|
|
230
229
|
};
|
|
230
|
+
const instantiate = (Generator) => {
|
|
231
|
+
const checked = checkGenerator(Generator);
|
|
232
|
+
if (!namespace) {
|
|
233
|
+
return this.instantiate(checked, ...arguments_);
|
|
234
|
+
}
|
|
235
|
+
// Read after checkGenerator, which updates the namespace to the one of an aliased generator.
|
|
236
|
+
return this.instantiate(checked, {
|
|
237
|
+
...getInstantiateOptions(...arguments_),
|
|
238
|
+
namespaceId: namespace.complete,
|
|
239
|
+
});
|
|
240
|
+
};
|
|
231
241
|
if (typeof namespaceOrPath === 'string') {
|
|
232
242
|
const meta = this.findMeta(namespaceOrPath);
|
|
233
243
|
const constructor = meta?.importGenerator();
|
|
@@ -236,14 +246,12 @@ export default class EnvironmentBase extends EventEmitter {
|
|
|
236
246
|
// constructor = await this.get(namespace);
|
|
237
247
|
}
|
|
238
248
|
if (constructor && 'then' in constructor) {
|
|
239
|
-
return constructor.then(g =>
|
|
240
|
-
return this.instantiate(checkGenerator(g), ...arguments_);
|
|
241
|
-
});
|
|
249
|
+
return constructor.then(g => instantiate(g));
|
|
242
250
|
}
|
|
243
|
-
return
|
|
251
|
+
return instantiate(constructor);
|
|
244
252
|
}
|
|
245
253
|
else {
|
|
246
|
-
return
|
|
254
|
+
return instantiate(namespaceOrPath);
|
|
247
255
|
}
|
|
248
256
|
}
|
|
249
257
|
instantiate(constructor, ...arguments_) {
|
|
@@ -251,7 +259,8 @@ export default class EnvironmentBase extends EventEmitter {
|
|
|
251
259
|
const { namespace = UNKNOWN_NAMESPACE, resolved = UNKNOWN_RESOLVED } = constructor;
|
|
252
260
|
// The generator class keeps the meta of the store, which may be shared: the generator gets the one of this environment.
|
|
253
261
|
const _meta = this.bindMeta(constructor._meta);
|
|
254
|
-
const
|
|
262
|
+
const { namespaceId } = composeOptions;
|
|
263
|
+
const environmentOptions = { env: this, resolved, namespace, _meta, ...(namespaceId && { namespaceId }) };
|
|
255
264
|
const generator = new constructor(composeOptions.generatorArgs ?? [], {
|
|
256
265
|
...this.sharedOptions,
|
|
257
266
|
...composeOptions.generatorOptions,
|
|
@@ -8,13 +8,13 @@ declare class FullEnvironment extends EnvironmentBase {
|
|
|
8
8
|
*
|
|
9
9
|
* @param {Object} options
|
|
10
10
|
*/
|
|
11
|
-
loadEnvironmentOptions(options: EnvironmentOptions):
|
|
11
|
+
loadEnvironmentOptions(options: EnvironmentOptions): Record<string, any>;
|
|
12
12
|
/**
|
|
13
13
|
* Load options passed to the Environment that should be forwarded to the Generator.
|
|
14
14
|
*
|
|
15
15
|
* @param {Object} options
|
|
16
16
|
*/
|
|
17
|
-
loadSharedOptions(options: EnvironmentOptions):
|
|
17
|
+
loadSharedOptions(options: EnvironmentOptions): Record<string, any>;
|
|
18
18
|
/**
|
|
19
19
|
* @protected
|
|
20
20
|
* Outputs the general help and usage. Optionally, if generators have been
|
package/dist/environment-full.js
CHANGED
|
@@ -2,12 +2,12 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
3
|
import { requireNamespace, toNamespace } from '@yeoman/namespace';
|
|
4
4
|
import { flyImport } from 'fly-import';
|
|
5
|
-
import { defaults, pick, uniq } from 'lodash-es';
|
|
6
5
|
import { valid } from 'semver';
|
|
7
6
|
import {} from "./generator-lookup.js";
|
|
8
7
|
import YeomanCommand from "./util/command.js";
|
|
9
8
|
import EnvironmentBase, {} from "./environment-base.js";
|
|
10
|
-
import { splitArgsFromString as splitArgumentsFromString } from "./util/util.js";
|
|
9
|
+
import { defaults, pick, splitArgsFromString as splitArgumentsFromString } from "./util/util.js";
|
|
10
|
+
import { nestedGeneratorsFolder } from "./util/namespace.js";
|
|
11
11
|
class FullEnvironment extends EnvironmentBase {
|
|
12
12
|
constructor(options = {}, adapterCompat) {
|
|
13
13
|
if (adapterCompat) {
|
|
@@ -108,7 +108,7 @@ class FullEnvironment extends EnvironmentBase {
|
|
|
108
108
|
* @return {Array}
|
|
109
109
|
*/
|
|
110
110
|
getGeneratorNames() {
|
|
111
|
-
return
|
|
111
|
+
return [...new Set(Object.keys(this.getGeneratorsMeta()).map(namespace => toNamespace(namespace)?.packageNamespace))];
|
|
112
112
|
}
|
|
113
113
|
/**
|
|
114
114
|
* Get last added path for a namespace
|
|
@@ -262,8 +262,12 @@ class FullEnvironment extends EnvironmentBase {
|
|
|
262
262
|
const nsOptions = { packagePatterns: [ns.generatorHint] };
|
|
263
263
|
if (ns.generator) {
|
|
264
264
|
// Build filePatterns to look specifically for the namespace.
|
|
265
|
-
const
|
|
266
|
-
|
|
265
|
+
const generatorPaths = [ns.generator.split(':').join('/')];
|
|
266
|
+
if (options.nestedGenerators ?? this.store.lookupOptions.nestedGenerators) {
|
|
267
|
+
// `app:sub` is `app/generators/sub` too.
|
|
268
|
+
generatorPaths.push(ns.generator.split(':').join(`/${nestedGeneratorsFolder}/`));
|
|
269
|
+
}
|
|
270
|
+
let filePatterns = generatorPaths.flatMap(genPath => [`${genPath}/index.?s`, `${genPath}.?s`]);
|
|
267
271
|
const lookups = options.lookups ?? this.store.lookups;
|
|
268
272
|
filePatterns = lookups.flatMap(prefix => filePatterns.map(pattern => join(prefix, pattern)));
|
|
269
273
|
nsOptions.filePatterns = filePatterns;
|
|
@@ -2,6 +2,11 @@ import type { LookupOptions as LookupOptionsApi } from '@yeoman/types';
|
|
|
2
2
|
import { type ModuleLookupOptions } from './module-lookup.ts';
|
|
3
3
|
export type LookupOptions = LookupOptionsApi & ModuleLookupOptions & {
|
|
4
4
|
lookups?: string[];
|
|
5
|
+
/**
|
|
6
|
+
* Look up the generators nested in the `generators` folder of a generator too:
|
|
7
|
+
* `generators/server/generators/cache` is registered as `server:cache`.
|
|
8
|
+
*/
|
|
9
|
+
nestedGenerators?: boolean;
|
|
5
10
|
};
|
|
6
11
|
type LookupMeta = {
|
|
7
12
|
filePath: string;
|
|
@@ -49,13 +54,13 @@ export declare function lookupGeneratorsSync(options?: LookupOptions, register?:
|
|
|
49
54
|
* @param {Boolean} [options.singleResult=true] - Set false to return multiple values.
|
|
50
55
|
* @return {String} generator
|
|
51
56
|
*/
|
|
52
|
-
export declare function lookupGenerator(namespace: string, options?: ModuleLookupOptions & {
|
|
57
|
+
export declare function lookupGenerator(namespace: string, options?: ModuleLookupOptions & Pick<LookupOptions, 'nestedGenerators'> & {
|
|
53
58
|
packagePath?: boolean;
|
|
54
59
|
generatorPath?: boolean;
|
|
55
60
|
} & {
|
|
56
61
|
singleResult?: true;
|
|
57
62
|
}): string;
|
|
58
|
-
export declare function lookupGenerator(namespace: string, options?: ModuleLookupOptions & {
|
|
63
|
+
export declare function lookupGenerator(namespace: string, options?: ModuleLookupOptions & Pick<LookupOptions, 'nestedGenerators'> & {
|
|
59
64
|
packagePath?: boolean;
|
|
60
65
|
generatorPath?: boolean;
|
|
61
66
|
} & {
|
package/dist/generator-lookup.js
CHANGED
|
@@ -2,7 +2,7 @@ import { extname, isAbsolute, join, posix } from 'node:path';
|
|
|
2
2
|
import { pathToFileURL } from 'node:url';
|
|
3
3
|
import { requireNamespace, toNamespace } from '@yeoman/namespace';
|
|
4
4
|
import { findPackagesIn, getNpmPaths, moduleLookupSync } from "./module-lookup.js";
|
|
5
|
-
import { defaultLookups } from "./util/namespace.js";
|
|
5
|
+
import { defaultLookups, withNestedLookups } from "./util/namespace.js";
|
|
6
6
|
import Store from "./store.js";
|
|
7
7
|
export const defaultExtensions = ['.ts', '.cts', '.mts', '.js', '.cjs', '.mjs'];
|
|
8
8
|
/**
|
|
@@ -30,10 +30,10 @@ export const defaultExtensions = ['.ts', '.cts', '.mts', '.js', '.cjs', '.mjs'];
|
|
|
30
30
|
* @return {Object[]} List of generators
|
|
31
31
|
*/
|
|
32
32
|
export function lookupGeneratorsSync(options = {}, register) {
|
|
33
|
-
const { lookups = defaultLookups } = options;
|
|
33
|
+
const { lookups = defaultLookups, nestedGenerators } = options;
|
|
34
34
|
options = {
|
|
35
35
|
// Js generators should be after, last will override registered one.
|
|
36
|
-
filePatterns: lookups.flatMap(prefix => defaultExtensions.map(extension => `${prefix}/*/index${extension}`)),
|
|
36
|
+
filePatterns: withNestedLookups(lookups, nestedGenerators).flatMap(prefix => defaultExtensions.map(extension => `${prefix}/*/index${extension}`)),
|
|
37
37
|
filterPaths: false,
|
|
38
38
|
packagePatterns: ['generator-*'],
|
|
39
39
|
reverse: !options.singleResult,
|
|
@@ -64,7 +64,7 @@ export function lookupGenerator(namespace, options) {
|
|
|
64
64
|
}
|
|
65
65
|
// The generators are looked up by a store of their own, which keeps the ones asked for.
|
|
66
66
|
const generators = new Store().lookupSync({
|
|
67
|
-
filePatterns: defaultLookups.map(prefix => join(prefix, '*/index.{js,ts}')),
|
|
67
|
+
filePatterns: withNestedLookups(defaultLookups, lookupOptions.nestedGenerators).map(prefix => join(prefix, '*/index.{js,ts}')),
|
|
68
68
|
reverse: false,
|
|
69
69
|
...lookupOptions,
|
|
70
70
|
lookups: defaultLookups,
|
package/dist/module-lookup.js
CHANGED
|
@@ -3,7 +3,6 @@ import { delimiter, dirname, join, resolve, sep } from 'node:path';
|
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import process from 'node:process';
|
|
5
5
|
import arrify from 'arrify';
|
|
6
|
-
import { compact, uniq } from 'lodash-es';
|
|
7
6
|
import { globbySync } from 'globby';
|
|
8
7
|
import slash from 'slash';
|
|
9
8
|
import createdLogger from 'debug';
|
|
@@ -141,7 +140,7 @@ export function getNpmPaths(options = {}) {
|
|
|
141
140
|
if (!options.localOnly) {
|
|
142
141
|
paths = paths.concat(getGlobalNpmPaths(options.filterPaths));
|
|
143
142
|
}
|
|
144
|
-
return
|
|
143
|
+
return [...new Set(paths)];
|
|
145
144
|
}
|
|
146
145
|
/**
|
|
147
146
|
* Get the local npm lookup directories
|
|
@@ -161,7 +160,7 @@ function getLocalNpmPaths() {
|
|
|
161
160
|
}
|
|
162
161
|
paths.push(lookup);
|
|
163
162
|
});
|
|
164
|
-
return
|
|
163
|
+
return [...new Set(paths.toReversed())];
|
|
165
164
|
}
|
|
166
165
|
/**
|
|
167
166
|
* Get the global npm lookup directories
|
|
@@ -197,7 +196,7 @@ function getGlobalNpmPaths(filterPaths = true) {
|
|
|
197
196
|
// because of bugs in the parseable implementation of `ls` command and mostly
|
|
198
197
|
// performance issues. So, we go with our best bet for now.
|
|
199
198
|
if (process.env.NODE_PATH) {
|
|
200
|
-
paths =
|
|
199
|
+
paths = process.env.NODE_PATH.split(delimiter).filter(Boolean).concat(paths);
|
|
201
200
|
}
|
|
202
201
|
// Global node_modules should be 4 or 2 directory up this one (most of the time)
|
|
203
202
|
// Ex: /usr/another_global/node_modules/yeoman-denerator/node_modules/yeoman-environment/lib (1 level dependency)
|
|
@@ -228,5 +227,5 @@ function getGlobalNpmPaths(filterPaths = true) {
|
|
|
228
227
|
if (process.argv[1]) {
|
|
229
228
|
paths.push(...filterValidNpmPath(join(dirname(process.argv[1]), '../..'), !filterPaths));
|
|
230
229
|
}
|
|
231
|
-
return
|
|
230
|
+
return [...new Set(paths.filter(Boolean).toReversed())];
|
|
232
231
|
}
|
package/dist/store.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export type StoreLookupOptions = LookupOptions & StoreEnvironmentOptions & {
|
|
|
22
22
|
* Lookup options shared by every lookup of a store, the options passed to a lookup take precedence.
|
|
23
23
|
* What to look up (paths, package patterns and `singleResult`) is given by each lookup.
|
|
24
24
|
*/
|
|
25
|
-
export type StoreSharedLookupOptions = Pick<StoreLookupOptions, 'lookups' | 'localOnly' | 'filterPaths' | 'customizeNamespace' | 'filter' | 'usePackageName'>;
|
|
25
|
+
export type StoreSharedLookupOptions = Pick<StoreLookupOptions, 'lookups' | 'localOnly' | 'filterPaths' | 'customizeNamespace' | 'filter' | 'usePackageName' | 'nestedGenerators'>;
|
|
26
26
|
/** A generator found by a lookup, `registered` tells if it was added to the store. */
|
|
27
27
|
export type StoreLookupGeneratorMeta = FoundGenerator & ((StoreGeneratorMeta & {
|
|
28
28
|
registered: true;
|
package/dist/store.js
CHANGED
|
@@ -10,7 +10,7 @@ import { pathToFileURL } from 'node:url';
|
|
|
10
10
|
import { basename, extname, join, relative } from 'node:path';
|
|
11
11
|
import { createRequire } from 'node:module';
|
|
12
12
|
import { readFileSync, realpathSync } from 'node:fs';
|
|
13
|
-
import { toNamespace } from '@yeoman/namespace';
|
|
13
|
+
import { namespaceFromPackageName, toNamespace } from '@yeoman/namespace';
|
|
14
14
|
import createDebug from 'debug';
|
|
15
15
|
import { lookupGeneratorsSync } from "./generator-lookup.js";
|
|
16
16
|
import { asNamespace, defaultLookups } from "./util/namespace.js";
|
|
@@ -20,6 +20,15 @@ const require = createRequire(import.meta.url);
|
|
|
20
20
|
const operationsKey = Symbol('operations');
|
|
21
21
|
const setOperations = (meta, operations) => Object.defineProperty(meta, operationsKey, { value: operations });
|
|
22
22
|
const getOperations = (meta) => meta[operationsKey];
|
|
23
|
+
/** The namespace of a generator package, like `@scope/foo` for `@scope/generator-foo`, undefined for another name. */
|
|
24
|
+
const namespaceOfPackage = (packageName) => {
|
|
25
|
+
try {
|
|
26
|
+
return namespaceFromPackageName(packageName).namespace;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
23
32
|
/**
|
|
24
33
|
* The Generator store
|
|
25
34
|
* This is used to store generator (npm packages) reference and instantiate them when
|
|
@@ -74,7 +83,7 @@ export default class Store {
|
|
|
74
83
|
* @param lookups - The lookups of the store if omitted
|
|
75
84
|
*/
|
|
76
85
|
namespace(filepath, lookups = this.lookups) {
|
|
77
|
-
return asNamespace(filepath, { lookups });
|
|
86
|
+
return asNamespace(filepath, { lookups, nestedGenerators: this.lookupOptions.nestedGenerators });
|
|
78
87
|
}
|
|
79
88
|
/**
|
|
80
89
|
* A copy of the store with the same generators, packages and lookup options, whose metas default to the given
|
|
@@ -264,25 +273,33 @@ export default class Store {
|
|
|
264
273
|
const { registerToScope, customizeNamespace = (ns) => ns, usePackageName, filter, env, lookups = this.lookups, ...remainingOptions } = { ...this.lookupOptions, ...options };
|
|
265
274
|
const lookupOptions = { ...remainingOptions, lookups };
|
|
266
275
|
const generators = [];
|
|
276
|
+
const { nestedGenerators } = lookupOptions;
|
|
267
277
|
lookupGeneratorsSync(lookupOptions, ({ packagePath, filePath, lookups }) => {
|
|
268
|
-
|
|
269
|
-
let namespacePath;
|
|
270
|
-
if (packageName) {
|
|
271
|
-
namespacePath = join(packageName, relative(packagePath, filePath));
|
|
272
|
-
}
|
|
273
|
-
else {
|
|
274
|
-
let repositoryPath = join(packagePath, '..');
|
|
275
|
-
if (basename(repositoryPath).startsWith('@')) {
|
|
276
|
-
// Scoped package
|
|
277
|
-
repositoryPath = join(repositoryPath, '..');
|
|
278
|
-
}
|
|
279
|
-
namespacePath = relative(repositoryPath, filePath);
|
|
280
|
-
}
|
|
281
|
-
let namespace = customizeNamespace(asNamespace(namespacePath, { lookups }));
|
|
278
|
+
let namespace;
|
|
282
279
|
try {
|
|
280
|
+
const packageName = usePackageName ? this.getPackageJson(packagePath)?.name : undefined;
|
|
281
|
+
const packageNamespace = packageName ? namespaceOfPackage(packageName) : undefined;
|
|
282
|
+
let foundNamespace;
|
|
283
|
+
if (packageNamespace) {
|
|
284
|
+
// The namespace of the package, then the generator from its path in the package.
|
|
285
|
+
const generatorPath = join('generator-package', relative(packagePath, filePath));
|
|
286
|
+
foundNamespace = `${packageNamespace}${asNamespace(generatorPath, { lookups, nestedGenerators }).slice('package'.length)}`;
|
|
287
|
+
}
|
|
288
|
+
else if (packageName) {
|
|
289
|
+
foundNamespace = asNamespace(join(packageName, relative(packagePath, filePath)), { lookups, nestedGenerators });
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
let repositoryPath = join(packagePath, '..');
|
|
293
|
+
if (basename(repositoryPath).startsWith('@')) {
|
|
294
|
+
// Scoped package
|
|
295
|
+
repositoryPath = join(repositoryPath, '..');
|
|
296
|
+
}
|
|
297
|
+
foundNamespace = asNamespace(relative(repositoryPath, filePath), { lookups, nestedGenerators });
|
|
298
|
+
}
|
|
299
|
+
namespace = customizeNamespace(foundNamespace);
|
|
283
300
|
const resolved = realpathSync(filePath);
|
|
284
301
|
if (!namespace) {
|
|
285
|
-
namespace = customizeNamespace(asNamespace(resolved, { lookups }));
|
|
302
|
+
namespace = customizeNamespace(asNamespace(resolved, { lookups, nestedGenerators }));
|
|
286
303
|
}
|
|
287
304
|
namespace = namespace;
|
|
288
305
|
if (registerToScope && !namespace.startsWith('@')) {
|
package/dist/util/namespace.d.ts
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
type AsNamespaceOptions = {
|
|
2
2
|
lookups?: string[];
|
|
3
|
+
/** Generators nested in the `generators` folder of a generator: `app/generators/sub` is `app:sub`. */
|
|
4
|
+
nestedGenerators?: boolean;
|
|
3
5
|
};
|
|
6
|
+
/** The folder holding the generators nested in a generator. */
|
|
7
|
+
export declare const nestedGeneratorsFolder = "generators";
|
|
8
|
+
/**
|
|
9
|
+
* The lookups of the generators, with the lookups of the generators nested in them if `nestedGenerators`:
|
|
10
|
+
* `generators` and `generators/*\/generators`.
|
|
11
|
+
*/
|
|
12
|
+
export declare const withNestedLookups: (lookups: string[], nestedGenerators?: boolean) => string[];
|
|
4
13
|
export declare const defaultLookups: string[];
|
|
5
14
|
/**
|
|
6
15
|
* Given a String `filepath`, tries to figure out the relative namespace.
|
|
@@ -21,6 +30,7 @@ export declare const defaultLookups: string[];
|
|
|
21
30
|
*
|
|
22
31
|
* @param filepath
|
|
23
32
|
* @param lookups paths
|
|
33
|
+
* @throws if the path is in a scoped package that is not a `generator-` one
|
|
24
34
|
*/
|
|
25
|
-
export declare const asNamespace: (filepath: string, { lookups }: AsNamespaceOptions) => string;
|
|
35
|
+
export declare const asNamespace: (filepath: string, { lookups, nestedGenerators }: AsNamespaceOptions) => string;
|
|
26
36
|
export {};
|
package/dist/util/namespace.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
import { parse } from 'node:path';
|
|
1
|
+
import { parse, posix } from 'node:path';
|
|
2
2
|
import slash from 'slash';
|
|
3
|
-
import { escapeRegExp
|
|
3
|
+
import { escapeRegExp } from "./util.js";
|
|
4
|
+
/** The folder holding the generators nested in a generator. */
|
|
5
|
+
export const nestedGeneratorsFolder = 'generators';
|
|
6
|
+
/**
|
|
7
|
+
* The lookups of the generators, with the lookups of the generators nested in them if `nestedGenerators`:
|
|
8
|
+
* `generators` and `generators/*\/generators`.
|
|
9
|
+
*/
|
|
10
|
+
export const withNestedLookups = (lookups, nestedGenerators) => nestedGenerators ? lookups.flatMap(lookup => [lookup, posix.join(lookup, '*', nestedGeneratorsFolder)]) : lookups;
|
|
4
11
|
export const defaultLookups = ['.', 'generators', 'lib/generators', 'dist/generators'];
|
|
5
12
|
/**
|
|
6
13
|
* Given a String `filepath`, tries to figure out the relative namespace.
|
|
@@ -21,8 +28,9 @@ export const defaultLookups = ['.', 'generators', 'lib/generators', 'dist/genera
|
|
|
21
28
|
*
|
|
22
29
|
* @param filepath
|
|
23
30
|
* @param lookups paths
|
|
31
|
+
* @throws if the path is in a scoped package that is not a `generator-` one
|
|
24
32
|
*/
|
|
25
|
-
export const asNamespace = (filepath, { lookups = defaultLookups }) => {
|
|
33
|
+
export const asNamespace = (filepath, { lookups = defaultLookups, nestedGenerators }) => {
|
|
26
34
|
if (!filepath) {
|
|
27
35
|
throw new Error('Missing file path');
|
|
28
36
|
}
|
|
@@ -37,7 +45,8 @@ export const asNamespace = (filepath, { lookups = defaultLookups }) => {
|
|
|
37
45
|
const parsed = parse(ns);
|
|
38
46
|
ns = parsed.dir ? `${parsed.dir}/${parsed.name}` : parsed.name;
|
|
39
47
|
// Sort lookups by length so biggest are removed first
|
|
40
|
-
|
|
48
|
+
// The folder of the nested generators is removed like a lookup.
|
|
49
|
+
const nsLookups = [...lookups, ...(nestedGenerators ? [nestedGeneratorsFolder] : []), '..']
|
|
41
50
|
.map(found => slash(found))
|
|
42
51
|
.toSorted((a, b) => a.split('/').length - b.split('/').length)
|
|
43
52
|
.toReversed();
|
|
@@ -47,15 +56,21 @@ export const asNamespace = (filepath, { lookups = defaultLookups }) => {
|
|
|
47
56
|
ns = ns.replaceAll(new RegExp(`(?:/|^)${escapeRegExp(lookup)}(?=/)`, 'g'), '');
|
|
48
57
|
}
|
|
49
58
|
const folders = ns.split('/');
|
|
50
|
-
const scope = findLast(
|
|
59
|
+
const scope = folders.findLast(folder => folder.startsWith('@'));
|
|
51
60
|
// Cleanup `ns` from unwanted parts and then normalize slashes to `:`
|
|
52
61
|
ns = ns
|
|
53
62
|
.replaceAll('//', '') // Remove double `/`
|
|
54
|
-
|
|
63
|
+
// Remove the path before the package, the last folder starting with `generator-`, and its `generator-` prefix:
|
|
64
|
+
// a `generator-` inside the name of the package or of a generator is kept.
|
|
65
|
+
.replace(/^(?:.*\/)?generator-/, '')
|
|
55
66
|
.replace(/\/(index|main)$/, '') // Remove `/index` or `/main`
|
|
56
67
|
.replace(/^\//, '') // Remove leading `/`
|
|
57
68
|
.replaceAll(/\/+/g, ':'); // Replace slashes by `:`
|
|
58
69
|
if (scope) {
|
|
70
|
+
// The path up to the scope is left when the package is not a `generator-` one.
|
|
71
|
+
if (new RegExp(`(?:^|:)${escapeRegExp(scope)}:`).test(ns)) {
|
|
72
|
+
throw new Error(`The scoped package of ${filepath} is not a generator- package, it has no namespace.`);
|
|
73
|
+
}
|
|
59
74
|
ns = `${scope}/${ns}`;
|
|
60
75
|
}
|
|
61
76
|
return ns;
|
package/dist/util/util.d.ts
CHANGED
|
@@ -4,3 +4,16 @@ export declare const execaOutput: (cmg: string, arguments_: string[]) => string
|
|
|
4
4
|
* and then splits up the remaining arguments if they are not part of a quote.
|
|
5
5
|
*/
|
|
6
6
|
export declare function splitArgsFromString(argumentsString: string | string[]): string[];
|
|
7
|
+
/**
|
|
8
|
+
* The properties of an object present in it (own or inherited), like lodash `pick`.
|
|
9
|
+
*/
|
|
10
|
+
export declare const pick: (object: object | undefined, keys: readonly string[]) => Record<string, any>;
|
|
11
|
+
/**
|
|
12
|
+
* Assign to the target the properties of the sources it has as undefined, the first source first, like lodash
|
|
13
|
+
* `defaults`.
|
|
14
|
+
*/
|
|
15
|
+
export declare const defaults: <T extends object>(target: T, ...sources: Array<object | undefined>) => T;
|
|
16
|
+
/**
|
|
17
|
+
* Escape the characters with a meaning in a regular expression, like lodash `escapeRegExp`.
|
|
18
|
+
*/
|
|
19
|
+
export declare const escapeRegExp: (value: string) => string;
|
package/dist/util/util.js
CHANGED
|
@@ -34,3 +34,27 @@ export function splitArgsFromString(argumentsString) {
|
|
|
34
34
|
}
|
|
35
35
|
return result;
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* The properties of an object present in it (own or inherited), like lodash `pick`.
|
|
39
|
+
*/
|
|
40
|
+
export const pick = (object, keys) => Object.fromEntries(object ? keys.filter(key => key in object).map(key => [key, object[key]]) : []);
|
|
41
|
+
/**
|
|
42
|
+
* Assign to the target the properties of the sources it has as undefined, the first source first, like lodash
|
|
43
|
+
* `defaults`.
|
|
44
|
+
*/
|
|
45
|
+
export const defaults = (target, ...sources) => {
|
|
46
|
+
for (const source of sources) {
|
|
47
|
+
if (!source)
|
|
48
|
+
continue;
|
|
49
|
+
for (const key in source) {
|
|
50
|
+
if (target[key] === undefined) {
|
|
51
|
+
target[key] = source[key];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return target;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Escape the characters with a meaning in a regular expression, like lodash `escapeRegExp`.
|
|
59
|
+
*/
|
|
60
|
+
export const escapeRegExp = (value) => value.replaceAll(/[\\^$.*+?()[\]{}|]/g, String.raw `\$&`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yeoman-environment",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.3.0",
|
|
4
4
|
"description": "Handles the lifecyle and bootstrapping of generators in a specific environment",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"development",
|
|
@@ -72,7 +72,6 @@
|
|
|
72
72
|
"globby": "^16.2.4",
|
|
73
73
|
"grouped-queue": "^2.1.0",
|
|
74
74
|
"locate-path": "^8.0.0",
|
|
75
|
-
"lodash-es": "^4.18.1",
|
|
76
75
|
"mem-fs": "^6.0.0",
|
|
77
76
|
"mem-fs-editor": "^12.0.9",
|
|
78
77
|
"semver": "^7.8.5",
|
|
@@ -82,7 +81,6 @@
|
|
|
82
81
|
},
|
|
83
82
|
"devDependencies": {
|
|
84
83
|
"@types/debug": "^4.1.13",
|
|
85
|
-
"@types/lodash-es": "^4.17.12",
|
|
86
84
|
"@types/node": "^26.4.1",
|
|
87
85
|
"@types/semver": "^7.8.0",
|
|
88
86
|
"@yeoman/eslint": "1.1.0",
|