| 1 | const ROOT_NAMESPACE_NAME = '__rootNamespace__';
|
|---|
| 2 |
|
|---|
| 3 | class 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 |
|
|---|
| 106 | class RootNamespace extends Namespace {
|
|---|
| 107 | constructor() {
|
|---|
| 108 | super(ROOT_NAMESPACE_NAME, null);
|
|---|
| 109 | Namespace.ROOT_NAMESPACE = this;
|
|---|
| 110 | }
|
|---|
| 111 | }
|
|---|
| 112 |
|
|---|
| 113 | module.exports.Namespace = Namespace;
|
|---|
| 114 | module.exports.RootNamespace = RootNamespace;
|
|---|
| 115 |
|
|---|