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