1 | define([ |
---|
2 | "./buildControl", |
---|
3 | "./fileUtils", |
---|
4 | "./fs" |
---|
5 | ], function(bc, fileUtils, fs){ |
---|
6 | // find all files as given by files, dirs, trees, and packages |
---|
7 | var |
---|
8 | dirsProcessed = |
---|
9 | // a set of the directory names that have been inspected |
---|
10 | {}, |
---|
11 | |
---|
12 | treesDirsFiles = ["trees", "dirs", "files"], |
---|
13 | |
---|
14 | srcDirs = {}, |
---|
15 | |
---|
16 | destDirs = {}, |
---|
17 | |
---|
18 | getFilepath = fileUtils.getFilepath, |
---|
19 | catPath = fileUtils.catPath, |
---|
20 | compactPath = fileUtils.compactPath, |
---|
21 | |
---|
22 | start = function(resource, tagResource){ |
---|
23 | if(!resource.tag){ |
---|
24 | resource.tag = {}; |
---|
25 | } |
---|
26 | if(tagResource){ |
---|
27 | tagResource(resource); |
---|
28 | } |
---|
29 | bc.start(resource); |
---|
30 | srcDirs[getFilepath(resource.src)] = 1; |
---|
31 | destDirs[getFilepath(resource.dest)] = 1; |
---|
32 | }, |
---|
33 | |
---|
34 | getResourceTagFunction = function(resourceTags){ |
---|
35 | //resource tags is a map from tag to a function or a regular expression |
---|
36 | var getFilterFunction = function(item){ |
---|
37 | return typeof item=="function" ? |
---|
38 | item : |
---|
39 | function(filename){ |
---|
40 | return item.test(filename); |
---|
41 | }; |
---|
42 | }, |
---|
43 | tag = {}, |
---|
44 | gotOne = false; |
---|
45 | for(var p in resourceTags){ |
---|
46 | tag[p] = getFilterFunction(resourceTags[p]); |
---|
47 | gotOne = true; |
---|
48 | } |
---|
49 | if(!gotOne){ |
---|
50 | return 0; |
---|
51 | } |
---|
52 | return function(resource){ |
---|
53 | for(var p in tag){ |
---|
54 | if(tag[p](resource.src, resource.mid, resource)){ |
---|
55 | resource.tag[p] = 1; |
---|
56 | } |
---|
57 | } |
---|
58 | }; |
---|
59 | }, |
---|
60 | |
---|
61 | neverExclude = function(){ |
---|
62 | return 0; |
---|
63 | }, |
---|
64 | |
---|
65 | getExcludes = function(excludes){ |
---|
66 | // excludes is falsy, a function, or a regulare expression |
---|
67 | if(!excludes){ |
---|
68 | return neverExclude; |
---|
69 | }else if(typeof excludes=="function"){ |
---|
70 | return excludes; |
---|
71 | }else{ |
---|
72 | return function(filename){ |
---|
73 | return excludes.test(filename); |
---|
74 | }; |
---|
75 | } |
---|
76 | }, |
---|
77 | |
---|
78 | readSingleDir = function(srcBase, destBase, current, excludes, advise, traverse){ |
---|
79 | // Read a directory and advise of each child contained therein if the child is |
---|
80 | // not excluded. |
---|
81 | // |
---|
82 | // If traverse is truthy, then traverse the directory tree. When traversing, |
---|
83 | // current gives the current position in the traversal relative to srcBase. |
---|
84 | // The first call when traversing (or only call when not traversing) must |
---|
85 | // have current set to falsy. |
---|
86 | // |
---|
87 | // Notice that only the complete child path relative to srcBase is submitted |
---|
88 | // to excludes. This simplifies constructing exclude functions since srcBase |
---|
89 | // will never be part of the input to those functions. |
---|
90 | |
---|
91 | var dir = srcBase + (current ? "/" + current : ""), |
---|
92 | fullPrefix = dir + "/", |
---|
93 | currentPrefix = current ? current + "/" : "", |
---|
94 | subdirs = []; |
---|
95 | |
---|
96 | // inspect each directory once per build |
---|
97 | if(dirsProcessed[dir]){ |
---|
98 | return; |
---|
99 | } |
---|
100 | dirsProcessed[dir] = 1; |
---|
101 | |
---|
102 | fs.readdirSync(dir).forEach(function(filename){ |
---|
103 | var current = currentPrefix + filename; |
---|
104 | if(!excludes || !excludes(current)){ |
---|
105 | var fullFilename = fullPrefix + filename, |
---|
106 | stats = fs.statSync(fullFilename); |
---|
107 | if(stats.isDirectory()){ |
---|
108 | subdirs.push(current); |
---|
109 | }else{ |
---|
110 | advise(fullFilename, destBase + "/" + current); |
---|
111 | } |
---|
112 | } |
---|
113 | }); |
---|
114 | if(traverse && subdirs.length){ |
---|
115 | subdirs.forEach(function(current){ |
---|
116 | readSingleDir(srcBase, destBase, current, excludes, advise, 1); |
---|
117 | }); |
---|
118 | } |
---|
119 | }, |
---|
120 | |
---|
121 | readFile = function(item, advise){ |
---|
122 | advise(item[0], item[1]); |
---|
123 | }, |
---|
124 | |
---|
125 | srcPathExists = function(srcPath){ |
---|
126 | if(!fileUtils.dirExists(srcPath)){ |
---|
127 | bc.log("missingDirDuringDiscovery", ["directory", srcPath]); |
---|
128 | return 0; |
---|
129 | } |
---|
130 | return 1; |
---|
131 | }, |
---|
132 | |
---|
133 | readDir = function(item, advise){ |
---|
134 | if(srcPathExists(item[0])){ |
---|
135 | readSingleDir(item[0], item[1], 0, getExcludes(item[2]), advise, 0, 0); |
---|
136 | } |
---|
137 | }, |
---|
138 | |
---|
139 | readTree = function(item, advise){ |
---|
140 | if(srcPathExists(item[0])){ |
---|
141 | readSingleDir(item[0], item[1], 0, getExcludes(item[2]), advise, 1); |
---|
142 | } |
---|
143 | }, |
---|
144 | |
---|
145 | discover = { |
---|
146 | files:readFile, |
---|
147 | dirs:readDir, |
---|
148 | trees:readTree |
---|
149 | }, |
---|
150 | |
---|
151 | processPackage = function(pack, destPack){ |
---|
152 | // treeItem is the package location tree; it may give explicit exclude instructions |
---|
153 | var treeItem; |
---|
154 | for(var trees = pack.trees || [], i = 0; i<trees.length; i++){ |
---|
155 | if(trees[i][0]==pack.location){ |
---|
156 | treeItem = trees[i]; |
---|
157 | break; |
---|
158 | } |
---|
159 | } |
---|
160 | if(!treeItem){ |
---|
161 | // create a tree item; don't traverse into hidden, backup, etc. files (e.g., .svn, .git, etc.) |
---|
162 | treeItem = [pack.location, destPack.location, /(\/\.)|(^\.)|(~$)/]; |
---|
163 | } |
---|
164 | |
---|
165 | var filenames = []; |
---|
166 | readTree(treeItem, function(filename){ filenames.push(filename); }); |
---|
167 | |
---|
168 | // next, sift filenames to find AMD modules |
---|
169 | var |
---|
170 | maybeAmdModules = {}, |
---|
171 | notModules = {}, |
---|
172 | locationPathLength = pack.location.length + 1, |
---|
173 | packName = pack.name, |
---|
174 | prefix = packName ? packName + "/" : "", |
---|
175 | mainModuleInfo = packName && bc.getSrcModuleInfo(packName), |
---|
176 | mainModuleFilename = packName && mainModuleInfo.url; |
---|
177 | filenames.forEach(function(filename){ |
---|
178 | // strip the package location path and the .js suffix(iff any) to get the mid |
---|
179 | var |
---|
180 | maybeModule = /\.js$/.test(filename), |
---|
181 | mid = prefix + filename.substring(locationPathLength, maybeModule ? filename.length-3 : filename.length), |
---|
182 | moduleInfo = maybeModule && bc.getSrcModuleInfo(mid); |
---|
183 | if(!maybeModule){ |
---|
184 | notModules[mid] = [filename, mid]; |
---|
185 | }else if(filename==mainModuleFilename){ |
---|
186 | maybeAmdModules[packName] = mainModuleInfo; |
---|
187 | }else{ |
---|
188 | // notice that this may result in mapping the module to a different location |
---|
189 | maybeAmdModules[mid] = moduleInfo; |
---|
190 | } |
---|
191 | }); |
---|
192 | |
---|
193 | // add modules as per explicit pack.modules vector; this is a way to add modules that map strangely |
---|
194 | // (for example "myPackage/foo" maps to the file "myPackage/bar"); recall, packageInfo.modules has two forms: |
---|
195 | // |
---|
196 | // modules:{ |
---|
197 | // "foo":1, |
---|
198 | // "foo":"path/to/foo/filename.js" |
---|
199 | // } |
---|
200 | for(var mid in pack.modules){ |
---|
201 | var |
---|
202 | fullMid = prefix + mid, |
---|
203 | moduleInfo = bc.getSrcModuleInfo(fullMid); |
---|
204 | if(typeof pack.modules[mid]=="string"){ |
---|
205 | moduleInfo.url = pack.modules[mid]; |
---|
206 | } |
---|
207 | maybeAmdModules[fullMid] = moduleInfo; |
---|
208 | delete notModules[fullMid]; |
---|
209 | }; |
---|
210 | |
---|
211 | var tagResource = getResourceTagFunction(pack.resourceTags); |
---|
212 | |
---|
213 | // start all the package modules; each property holds a module info object |
---|
214 | for(var p in maybeAmdModules){ |
---|
215 | moduleInfo = maybeAmdModules[p]; |
---|
216 | var resource = { |
---|
217 | src:moduleInfo.url, |
---|
218 | dest:bc.getDestModuleInfo(moduleInfo.mid).url, |
---|
219 | pid:moduleInfo.pid, |
---|
220 | mid:moduleInfo.mid, |
---|
221 | pack:pack, |
---|
222 | deps:[] |
---|
223 | }; |
---|
224 | start(resource, tagResource); |
---|
225 | } |
---|
226 | |
---|
227 | // start all the "notModules" |
---|
228 | var prefixLength = prefix.length; |
---|
229 | for(p in notModules){ |
---|
230 | resource = { |
---|
231 | src:notModules[p][0], |
---|
232 | // not really an AMD mid, but the filename with installation-dependent prefix stripped |
---|
233 | // this makes tagging easier |
---|
234 | mid:notModules[p][1], |
---|
235 | dest:catPath(destPack.location, p.substring(prefixLength)) |
---|
236 | }; |
---|
237 | start(resource, tagResource); |
---|
238 | } |
---|
239 | |
---|
240 | // finish by processing all the trees, dirs, and files explicitly specified for the package |
---|
241 | for(i = 0; i<treesDirsFiles.length; i++){ |
---|
242 | var set = treesDirsFiles[i]; |
---|
243 | if(pack[set]){ |
---|
244 | pack[set].forEach(function(item){ |
---|
245 | discover[set](item, function(src, dest){ |
---|
246 | start({src:src, dest:dest}, tagResource); |
---|
247 | }); |
---|
248 | }); |
---|
249 | } |
---|
250 | } |
---|
251 | }, |
---|
252 | |
---|
253 | discoverPackages = function(){ |
---|
254 | // discover all the package modules; discover the default package last since it may overlap |
---|
255 | // into other packages and we want modules in those other packages to be discovered as members |
---|
256 | // of those other packages; not as a module in the default package |
---|
257 | for(var p in bc.packages){ |
---|
258 | processPackage(bc.packages[p], bc.destPackages[p]); |
---|
259 | } |
---|
260 | }; |
---|
261 | |
---|
262 | return function(){ |
---|
263 | /// |
---|
264 | // build/discover |
---|
265 | |
---|
266 | bc.waiting++; // matches *1* |
---|
267 | |
---|
268 | // start the synthetic report resource |
---|
269 | start({ |
---|
270 | tag:{report:1}, |
---|
271 | src:"*report", |
---|
272 | dest:"*report", |
---|
273 | reports:[] |
---|
274 | }); |
---|
275 | |
---|
276 | discoverPackages(); |
---|
277 | |
---|
278 | // discover all trees, dirs, and files |
---|
279 | var tagResource = getResourceTagFunction(bc.resourceTags); |
---|
280 | for(var i = 0; i<treesDirsFiles.length; i++){ |
---|
281 | var set = treesDirsFiles[i]; |
---|
282 | bc[set].forEach(function(item){ |
---|
283 | discover[set](item, function(src, dest){ |
---|
284 | start({src:src, dest:dest}, tagResource); |
---|
285 | }); |
---|
286 | }); |
---|
287 | } |
---|
288 | |
---|
289 | // advise all modules that are to be written as a layer |
---|
290 | // advise the loader of boot layers |
---|
291 | for(var mid in bc.layers){ |
---|
292 | var |
---|
293 | layer = bc.layers[mid], |
---|
294 | moduleInfo = bc.getSrcModuleInfo(mid), |
---|
295 | resource = bc.resources[moduleInfo.url]; |
---|
296 | if(!resource){ |
---|
297 | // this is a synthetic layer (just a set of real modules aggregated but doesn't exist in the source) |
---|
298 | resource = { |
---|
299 | tag:{synthetic:1, amd:1}, |
---|
300 | src:moduleInfo.url, |
---|
301 | dest:bc.getDestModuleInfo(moduleInfo.mid).url, |
---|
302 | pid:moduleInfo.pid, |
---|
303 | mid:moduleInfo.mid, |
---|
304 | pack:moduleInfo.pack, |
---|
305 | deps:[], |
---|
306 | text:"define([], 1);" + bc.newline, |
---|
307 | getText:function(){ |
---|
308 | return this.text; |
---|
309 | }, |
---|
310 | encoding:"utf8" |
---|
311 | }; |
---|
312 | start(resource); |
---|
313 | } |
---|
314 | resource.layer = layer; |
---|
315 | if(layer.boot){ |
---|
316 | if(bc.loader){ |
---|
317 | bc.loader.boots.push(resource); |
---|
318 | }else{ |
---|
319 | bc.log("inputNoLoaderForBoot", ["boot layer", mid]); |
---|
320 | } |
---|
321 | } |
---|
322 | } |
---|
323 | |
---|
324 | bc.passGate(); // matches *1* |
---|
325 | }; |
---|
326 | }); |
---|