Skip to content

Commit 61126cb

Browse files
committed
Merge pull request #86 from tetsuo/patch-js-relative-extension
Some improvements for remote dependency lookups
2 parents 625f3a7 + 9e51e46 commit 61126cb

File tree

21 files changed

+433
-183
lines changed

21 files changed

+433
-183
lines changed

lib/builders/lookup.js

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
2+
module.exports = Lookup;
3+
4+
var url = require('url');
5+
var path = require('path');
6+
var debug = require('debug')('component-builder:scripts:lookup');
7+
var createManifest = require('component-manifest');
8+
9+
var EXTENSIONS = [ // default extensions to look up
10+
'',
11+
'.js',
12+
'.json',
13+
'/index.js',
14+
];
15+
16+
var RELATIVE_PATH = /^\.{1,2}\/.*/;
17+
18+
/**
19+
* From a file, lookup another file within that dep.
20+
* For use within `require()`s.
21+
*
22+
* @param {Object} file
23+
* @param {Object} opts for manifest generator
24+
*/
25+
function Lookup (file, opts) {
26+
if (!(this instanceof Lookup)) return new Lookup(file, opts);
27+
this.file = file;
28+
this.opts = opts;
29+
this.manifestGenerator = createManifest(opts);
30+
}
31+
32+
/**
33+
* Executes a lookup.
34+
*
35+
* @type {String} target
36+
*/
37+
Lookup.prototype.exec = function* (target) {
38+
var ret;
39+
target = target.toLowerCase();
40+
41+
if (RELATIVE_PATH.exec(target)) {
42+
ret = this.relatives(target);
43+
if (ret != null) return ret
44+
return target;
45+
} else {
46+
ret = yield* this.nonrelatives(target)
47+
return ret;
48+
}
49+
};
50+
51+
/**
52+
* Lookup a relative file.
53+
*
54+
* @param {String} target
55+
* @param {Object} file
56+
* @return {String}
57+
*/
58+
Lookup.prototype.relatives = function (target, file) {
59+
file || (file = this.file);
60+
61+
var path_ = url.resolve(file.path, target);
62+
var files = file.manifest.files;
63+
64+
for (var i = 0; i < files.length; i++) {
65+
var f = files[i];
66+
// we need this fallback to check relatives from a foreign local
67+
var name = f.name || path.join(f.manifest.name, path.relative(f.manifest.path, f.filename));
68+
69+
for (var j = 0; j < EXTENSIONS.length; j++) {
70+
// check by adding extensions
71+
if (f.path.toLowerCase() === path_ + EXTENSIONS[j]) return name;
72+
}
73+
// check by removing extensions
74+
if (f.path.replace(/\.\w+$/, '').toLowerCase() === path_) return name;
75+
};
76+
77+
var message = 'ignore "' + target + '" , could not resolve from "' + file.branch.name + '"\'s file "' + file.path + '"';
78+
debug(message);
79+
80+
return null;
81+
};
82+
83+
/**
84+
* Lookup a nonrelative file.
85+
*
86+
* @param {String} target
87+
* @return {String}
88+
*/
89+
Lookup.prototype.nonrelatives = function* (target) {
90+
var frags = tofrags(target);
91+
var head = frags[0], tail = frags[1];
92+
var ret;
93+
94+
// aliases
95+
ret = this.aliases.apply(this, frags)
96+
if (ret != null)
97+
return ret;
98+
99+
// locals
100+
ret = yield* this.locals(target);
101+
if (ret != null)
102+
return ret;
103+
104+
// dependencies
105+
ret = yield* this.dependencies(target);
106+
if (ret != null)
107+
return ret;
108+
109+
var deps = this.file.branch.dependencies;
110+
var names = Object.keys(deps);
111+
var name, dep;
112+
113+
// component.json name, if different than repo
114+
for (var i = 0; i < names.length; i++) {
115+
name = names[i];
116+
dep = deps[name];
117+
if (dep.node.name.toLowerCase() === head) {
118+
return dep.canonical + tail;
119+
}
120+
}
121+
122+
// to do: look up stuff outside the dependencies
123+
debug('could not resolve "%s" from "%s"', target, this.file.name);
124+
return target;
125+
};
126+
127+
/**
128+
* Lookup an alias.
129+
*
130+
* <user>-<repo>
131+
* <user>~<repo>
132+
*
133+
* @param {String} head
134+
* @param {String} tail
135+
* @return {String}
136+
*/
137+
Lookup.prototype.aliases = function (head, tail) {
138+
var deps = this.file.branch.dependencies;
139+
var name;
140+
141+
function fn (canonical) {
142+
if (tail)
143+
return [canonical, tail].join('/');
144+
else
145+
return canonical;
146+
}
147+
148+
if (~head.indexOf('~')) { // <user>~<repo>
149+
name = head.replace('~', '/');
150+
if (deps[name])
151+
return fn(deps[name].canonical);
152+
} else if (~head.indexOf('-')) { // <user>-<repo>
153+
var names = Object.keys(deps);
154+
for (var i = 0; i < names.length; i++) {
155+
name = names[i];
156+
if (head === name.replace('/', '-'))
157+
return fn(deps[name].canonical);
158+
}
159+
}
160+
161+
return null;
162+
};
163+
164+
Lookup.prototype.foreignRelative = function* (branch, relativeFile) {
165+
if (typeof branch !== 'object')
166+
throw new Error('branch must be supplied');
167+
168+
var manifest = yield* this.manifestGenerator(branch);
169+
170+
var dummy = {
171+
path: '', // it should simulate a url-relative path
172+
manifest: manifest,
173+
branch: branch
174+
};
175+
176+
// resolve the file (if extension is not provided)
177+
var resolved = this.relatives(relativeFile, dummy);
178+
if (resolved == null) return null;
179+
180+
var relative = path.relative(manifest.name, resolved);
181+
182+
return relative;
183+
};
184+
185+
/**
186+
* Lookup a local dependency.
187+
*
188+
* <local-name>
189+
* <local-name>/<filename>
190+
*
191+
* @param {String} target
192+
* @return {String}
193+
*/
194+
Lookup.prototype.locals = function* (target) {
195+
var deps = this.file.branch.locals;
196+
var keys = Object.keys(deps);
197+
var match, re, i = 0;
198+
199+
for (; i < keys.length; i++) {
200+
re = new RegExp("^(" + keys[i] + ")(/.*)?$");
201+
202+
if (match = re.exec(target)) {
203+
var head = match[1];
204+
var tail = match[2] || '';
205+
var canonical = deps[head].canonical;
206+
207+
if (tail !== '') {
208+
var relativeFile = '.' + tail;
209+
var resolvedTail = yield* this.foreignRelative(deps[head], relativeFile);
210+
if (resolvedTail != null) {
211+
debug('resolved relative file for local "' + head + '/' + resolvedTail + '"');
212+
return canonical + '/' + resolvedTail;
213+
}
214+
}
215+
216+
return canonical + tail;
217+
}
218+
}
219+
};
220+
221+
/**
222+
* Lookup a remote dependency.
223+
*
224+
* <repo>
225+
* <reference>/<filename>
226+
*
227+
* @param {String} target
228+
* @return {String}
229+
*/
230+
Lookup.prototype.dependencies = function* (target) {
231+
var deps = this.file.branch.dependencies;
232+
var keys = Object.keys(deps);
233+
var match, re, i = 0, head;
234+
235+
for (; i < keys.length; i++) {
236+
head = keys[i];
237+
238+
re = new RegExp("^(" + head.split('/')[1] + ")(/.*)?$");
239+
240+
if (match = re.exec(target)) {
241+
var tail = match[2] || '';
242+
var canonical = deps[head].canonical;
243+
244+
if (tail !== '') {
245+
var relativeFile = tail.slice(1);
246+
var resolvedTail = yield* this.foreignRelative(deps[head], relativeFile);
247+
if (resolvedTail != null) {
248+
debug('resolved relative file for dependency "' + head + '/' + resolvedTail + '"');
249+
return canonical + '/' + resolvedTail;
250+
}
251+
}
252+
253+
return canonical + tail;
254+
}
255+
}
256+
};
257+
258+
/**
259+
* Split reference name.
260+
*
261+
* @param {String} target
262+
* @return {Array}
263+
*/
264+
function tofrags (target) {
265+
var frags = target.split('/');
266+
var head = frags[0];
267+
var tail = frags.length > 1
268+
? frags.slice(1).join('/')
269+
: '';
270+
return [head, tail];
271+
}

0 commit comments

Comments
 (0)