1const ROOT_NAMESPACE_NAME = '__rootNamespace__'; 2 3class Namespace { 4 constructor(name, parentNamespace) { 5 this.name = name; 6 this.parentNamespace = parentNamespace; 7 this.childNamespaces = {}; 8 this.tasks = {}; 9 this.rules = {}; 10 this.path = this.getPath(); 11 } 12 13 get fullName() { 14 return this._getFullName(); 15 } 16 17 addTask(task) { 18 this.tasks[task.name] = task; 19 task.namespace = this; 20 } 21 22 resolveTask(name) { 23 if (!name) { 24 return; 25 } 26 27 let taskPath = name.split(':'); 28 let taskName = taskPath.pop(); 29 let task; 30 let ns; 31 32 // Namespaced, return either relative to current, or from root 33 if (taskPath.length) { 34 taskPath = taskPath.join(':'); 35 ns = this.resolveNamespace(taskPath) || 36 Namespace.ROOT_NAMESPACE.resolveNamespace(taskPath); 37 task = (ns && ns.resolveTask(taskName)); 38 } 39 // Bare task, return either local, or top-level 40 else { 41 task = this.tasks[name] || Namespace.ROOT_NAMESPACE.tasks[name]; 42 } 43 44 return task || null; 45 } 46 47 48 resolveNamespace(relativeName) { 49 if (!relativeName) { 50 return this; 51 } 52 53 let parts = relativeName.split(':'); 54 let ns = this; 55 56 for (let i = 0, ii = parts.length; (ns && i < ii); i++) { 57 ns = ns.childNamespaces[parts[i]]; 58 } 59 60 return ns || null; 61 } 62 63 matchRule(relativeName) { 64 let parts = relativeName.split(':'); 65 parts.pop(); 66 let ns = this.resolveNamespace(parts.join(':')); 67 let rules = ns ? ns.rules : []; 68 let r; 69 let match; 70 71 for (let p in rules) { 72 r = rules[p]; 73 if (r.match(relativeName)) { 74 match = r; 75 } 76 } 77 78 return (ns && match) || 79 (this.parentNamespace && 80 this.parentNamespace.matchRule(relativeName)); 81 } 82 83 getPath() { 84 let parts = []; 85 let next = this.parentNamespace; 86 while (next) { 87 parts.push(next.name); 88 next = next.parentNamespace; 89 } 90 parts.pop(); // Remove '__rootNamespace__' 91 return parts.reverse().join(':'); 92 } 93 94 _getFullName() { 95 let path = this.path; 96 path = (path && path.split(':')) || []; 97 path.push(this.name); 98 return path.join(':'); 99 } 100 101 isRootNamespace() { 102 return !this.parentNamespace; 103 } 104} 105 106class RootNamespace extends Namespace { 107 constructor() { 108 super(ROOT_NAMESPACE_NAME, null); 109 Namespace.ROOT_NAMESPACE = this; 110 } 111} 112 113module.exports.Namespace = Namespace; 114module.exports.RootNamespace = RootNamespace; 115 116