-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnamespacer.js
241 lines (203 loc) · 5.74 KB
/
namespacer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
'use strict';
const path = require('path'),
fs = require('fs'),
glob = require('glob');
const Space = require('./lib/Space'),
Listing = require('./lib/Listing');
class Namespace {
/**
* Create namespace instance with configuration
* @param conf {Object} Object of namespaces and folder paths
* @param root {string} Relative or absolute path to root
*/
constructor(conf, root = null){
if(!conf)
throw new Error('No namespace config given');
Namespace.addSpacesFromObject(conf, root);
}
/**
* Resolve filepath
* @param req {string} Namespaced path
* @throws Error
* @returns {string}
*/
static resolve(req) {
if(req == null)
throw new Error(`'${req}' path must be defined`);
for (let space of Namespace._spaces)
if (space.test(req))
return space.path(req);
throw new Error(`'${req}' not found in namespace`);
}
/**
* Resolve and require namespace
* @param req {string} Namespaced path
* @return {Object|*}
*/
static require(req){
if(glob.hasMagic(req)) {
//Strip pesky glob
let _all = req.replace(new RegExp(/\*\*$/), '');
let _immediate = req.replace(new RegExp(/\*$/), '');
if(_all !== req)
return Namespace.listAll(_all).map((listing) => listing.require());
else
return Namespace.list(_immediate).map((listing) => listing.require());
}
return require(Namespace.resolve(req));
}
/**
* Create space from name, relative path from root
* @param name {string} Namespace
* @param rel {string} Path to namespace
* @param root {string} Path to root
* @returns {Space}
* @private
*/
static _createSpace({ name, rel, root }){
return new Space({ name, root, location: rel });
}
/**
* List files in immediate namespace
* @param req {string} Namespaced path
* @param globStr {string} Glob to list files with
*/
static list(req, globStr='!(_)*.js'){
if(req == null)
throw new Error('No namespace given');
let space = Namespace.resolve(req);
return glob.sync(`${space}/${globStr}`).map((path) => new Listing({path}));
}
/**
* List all files in namespace
* @param req {string} Namespaced path
* @param globStr {string} Glob to list files with
*/
static listAll(req, globStr='!(_)*.js'){
if(req == null)
throw new Error('No namespace given');
let space = Namespace.resolve(req);
return glob.sync(`${space}/**/${globStr}`).map((path) => new Listing({path}));
}
/**
* Try to guess what path root we want
* @private
*/
static _getSpaceRoot(){
return process.cwd(); //path.dirname(module.parent.paths[0])
}
/**
* Get spaces file relative to including module
* @param fp {string} Relative path to spaces file
* @private
* @return {string}
*/
static _getSpaceRootFromIncludedModule(fp){
return path.normalize(path.join(path.dirname(module.parent.filename), fp));
}
/**
* Read from configuration
* @param conf {Object} Namespace configuration object
* @param root {string} Path to root
* @returns {Array}
* @private
*/
static _readConf(conf, root){
let _spaces = [];
root = root || Namespace._getSpaceRoot();
for(let space of Object.keys(conf))
_spaces.push(Namespace._createSpace({ name: space, rel: conf[space], root }));
//Sort by more specific namespace first
_spaces.sort(Namespace._spaceSorter);
return _spaces;
}
/**
* Sort namespaces based on length (more specific first)
* @param a {string}
* @param b {string}
* @returns {number}
* @private
*/
static _spaceSorter(a,b){
return b.name().length - a.name().length
}
/**
* Sort global namespaces
* @private
*/
static _sortSpaces(){
Namespace._spaces.sort(Namespace._spaceSorter);
}
/**
* Add namespaces from object
* @param obj {Object} Configuration object
* @param root {string} Path to root
*/
static addSpacesFromObject(obj, root){
let _spaces = Namespace._readConf(obj, root);
if(Namespace._spaces.length > 0)
Namespace._spaces.push(..._spaces);
else
Namespace._spaces = _spaces;
Namespace._sortSpaces();
}
/**
* Load namespace from file
* @param fp {string|null} Absolute or relative path
*/
static addSpacesFromFile(fp=null){
if(fp == null){
//Find default files
let js = path.normalize(path.join(Namespace._getSpaceRoot(), '.spaces.js'));
let json = path.normalize(path.join(Namespace._getSpaceRoot(), '.spaces.json'));
if(fs.existsSync(js))
fp = js;
else if (fs.existsSync(json))
fp = json;
else
throw new Error('No spaces file could be found!');
}
if(path.isAbsolute(fp))
Namespace.addSpacesFromObject(require(fp), path.dirname(fp));
else {
let absolute = Namespace._getSpaceRootFromIncludedModule(fp);
Namespace.addSpacesFromObject(require(absolute), path.dirname(absolute));
}
}
/**
* Clear out namespaces
* This destroys data
* @private
*/
static _clearSpaces(){
Namespace._spaces = []
}
/**
* Get object of all spaces Returns mutated spaces
* @returns {Object}
*/
static getSpaces(){
//Super efficient deep clone
return JSON.parse(JSON.stringify(Namespace._spaces));
}
}
if(!Namespace._spaces)
/**
* Static variable of all namespaces
* @type {Array}
* @static
* @private
*/
Namespace._clearSpaces();
module.exports = {
/**
* Static namespace reference
*/
namespace: Namespace,
/**
* Create namespace instance that references
* @param conf {Object} Configuration object to add to global namespaces
* @param root {string|null} Path to root of namespace
*/
instance: (conf, root = null) => new Namespace(conf, root)
};