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

'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';
};