[483] | 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 | }); |
---|