You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
6.0 KiB
215 lines
6.0 KiB
'use strict'; |
|
const debug = require('debug')('yeoman:environment:namespace'); |
|
|
|
// ============ | == @ ======== scope ========== | ====== unscoped ====== | = : ========== generator ========== | = @ ===== semver ====== @ | = # ========= instanceId ========== | == + =========== method ============== |= flags = | |
|
const regexp = /^(?:(@[a-z0-9-~][a-z0-9-._~]*)\/)?([a-z0-9-~][a-z0-9-._~]*)(?::((?:[a-z0-9-~][a-z0-9-._~]*:?)*))?(?:@([a-z0-9-.~><+=^* ]*)@?)?(?:#((?:[a-z0-9-~][a-z0-9-._~]*|\*)))?(?:\+((?:[a-zA-Z0-9][a-zA-Z0-9_]*\+?)*))?(\?)?$/; |
|
|
|
const groups = {complete: 0, scope: 1, unscoped: 2, generator: 3, semver: 4, instanceId: 5, method: 6, flags: 7}; |
|
const flags = {optional: '?'}; |
|
|
|
const namespaceModule = module.exports; |
|
|
|
class YeomanNamespace { |
|
constructor(parsed) { |
|
this._original = parsed.complete; |
|
this.scope = parsed.scope; |
|
this.unscoped = parsed.unscoped; |
|
this.generator = parsed.generator; |
|
this.instanceId = parsed.instanceId; |
|
this.semver = parsed.semver; |
|
if (parsed.method) { |
|
this.methods = parsed.method.split('+'); |
|
} else { |
|
this.methods = parsed.methods; |
|
} |
|
this.flags = parsed.flags; |
|
|
|
// Populate flags |
|
if (this.flags) { |
|
Object.entries(flags).forEach(([name, value]) => { |
|
if (this.flags === value) { |
|
this[name] = true; |
|
} else { |
|
delete this[name]; |
|
} |
|
}); |
|
} |
|
|
|
debug('Parsed namespace %o', this); |
|
} |
|
|
|
static parse(complete) { |
|
const result = regexp.exec(complete); |
|
if (!result) { |
|
debug('Namespace failed RegExp parse %s, using fallback', complete); |
|
return null; |
|
} |
|
|
|
const parsed = {complete}; |
|
// Populate fields |
|
Object.entries(groups).forEach(([name, value]) => { |
|
if (result[value]) { |
|
parsed[name] = result[value]; |
|
} |
|
}); |
|
return parsed; |
|
} |
|
|
|
_update(parsed) { |
|
this.scope = parsed.scope || this.scope; |
|
this.unscoped = parsed.unscoped || this.unscoped; |
|
this.generator = parsed.generator || this.generator; |
|
this.instanceId = parsed.instanceId || this.instanceId; |
|
this.command = parsed.command || this.command; |
|
this.flags = parsed.flags || this.flags; |
|
} |
|
|
|
get _scopeAddition() { |
|
return this.scope ? `${this.scope}/` : ''; |
|
} |
|
|
|
get generatorName() { |
|
return this.generator ? `:${this.generator}` : ''; |
|
} |
|
|
|
_semverAddition(post) { |
|
if (!this.semver) { |
|
return post ? post : ''; |
|
} |
|
if (post) { |
|
return `@${this.semver}@${post}`; |
|
} |
|
return `@${this.semver}`; |
|
} |
|
|
|
get instanceName() { |
|
return this.instanceId ? `#${this.instanceId}` : ''; |
|
} |
|
|
|
get complete() { |
|
let methods = ''; |
|
if (this.methods && this.methods.length > 0) { |
|
methods = '+' + this.methods.join('+'); |
|
} |
|
const postSemver = `${this.instanceName}${methods}${this.flags || ''}`; |
|
return `${this.namespace}${this._semverAddition(postSemver)}`; |
|
} |
|
|
|
get packageNamespace() { |
|
return `${this._scopeAddition}${this.unscoped}`; |
|
} |
|
|
|
get namespace() { |
|
return `${this.packageNamespace}${this.generatorName}`; |
|
} |
|
|
|
set namespace(namespace) { |
|
this._update(YeomanNamespace.parse(namespace)); |
|
} |
|
|
|
get unscopedNamespace() { |
|
return `${this.unscoped}${this.generatorName}`; |
|
} |
|
|
|
get id() { |
|
return `${this.namespace}${this.instanceName}`; |
|
} |
|
|
|
get generatorHint() { |
|
return `${this._scopeAddition}generator-${this.unscoped}`; |
|
} |
|
|
|
get versionedHint() { |
|
return this.semver ? `${this.generatorHint}@"${this.semver}"` : this.generatorHint; |
|
} |
|
|
|
with(newValues) { |
|
const self = this; |
|
return new YeomanNamespace({ |
|
...self, |
|
...newValues |
|
}); |
|
} |
|
|
|
toString() { |
|
return this.complete; |
|
} |
|
} |
|
|
|
/** |
|
* Parse an namespace |
|
* |
|
* @private |
|
* @param {String} namespace |
|
* @return {Object} parsed |
|
* @return {String} parsed.complete - Complete namespace |
|
* @return {String} parsed.namespace - Namespace with format @scope/namespace:generator |
|
* @return {String} parsed.generatorHint - Package name |
|
* @return {String} parsed.id - Id of the instance. |
|
* @return {String} parsed.instanceId - Instance id with format @scope/namespace:generator#id |
|
* @return {String} parsed.method - Method id with format @scope/namespace:generator+foo+bar |
|
* @return {String} parsed.scope - Scope name |
|
* @return {String} parsed.packageNamespace - Package namespace with format @scope/namespace |
|
* @return {String} parsed.generator - Original namespace |
|
* @return {String} parsed.flags - Original namespace |
|
*/ |
|
namespaceModule.parseNamespace = function (complete) { |
|
if (typeof complete !== 'string') { |
|
return null; |
|
} |
|
const parsed = YeomanNamespace.parse(complete); |
|
return parsed ? new YeomanNamespace(parsed) : null; |
|
}; |
|
|
|
/** |
|
* Convert a namespace to a namespace object |
|
* |
|
* @private |
|
* @param {String | YeomanNamespace} namespace |
|
* @return {YeomanNamespace} |
|
*/ |
|
namespaceModule.toNamespace = function (namespace) { |
|
return namespaceModule.isNamespace(namespace) ? namespace : namespaceModule.parseNamespace(namespace); |
|
}; |
|
|
|
/** |
|
* Convert a package name to a namespace object |
|
* |
|
* @private |
|
* @param {String} packageName |
|
* @return {YeomanNamespace} |
|
*/ |
|
namespaceModule.namespaceFromPackageName = function (packageName) { |
|
const namespace = namespaceModule.parseNamespace(packageName); |
|
if (!namespace.unscoped.startsWith('generator-')) { |
|
throw new Error(`${packageName} is not a valid generator package name`); |
|
} |
|
namespace.unscoped = namespace.unscoped.replace(/^generator-/, ''); |
|
return namespace; |
|
}; |
|
|
|
/** |
|
* Convert a namespace to a namespace object |
|
* |
|
* @private |
|
* @param {String | YeomanNamespace} namespace |
|
* @return {YeomanNamespace} |
|
*/ |
|
namespaceModule.requireNamespace = function (namespace) { |
|
const parsed = namespaceModule.toNamespace(namespace); |
|
if (!parsed) { |
|
throw new Error(`Error parsing namespace ${namespace}`); |
|
} |
|
return parsed; |
|
}; |
|
|
|
/** |
|
* Test if the object is an Namespace instance. |
|
* |
|
* @private |
|
* @param {Object} namespace |
|
* @return {Boolean} True if namespace is a YeomanNamespace |
|
*/ |
|
namespaceModule.isNamespace = function (namespace) { |
|
return namespace && namespace.constructor && namespace.constructor.name === 'YeomanNamespace'; |
|
};
|
|
|