Don't like ads? PRO users don't see any ads ;-)
Guest

YUI3: Collect module meta-information for Loader

By: hasenstein on Jul 10th, 2012  |  syntax: JavaScript  |  size: 6.38 KB  |  hits: 43  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. //Async. vs. sync.: This is a build script not a server, therefore I use
  2. //synchronous calls whenever possible. Anyone deliberately using async.
  3. //calls HERE needs to have a certain body part located on top of the
  4. //spine examined. That is great for SERVERS (and GUIs) but does not
  5. //belong HERE.
  6. var path = require('path'),
  7.     fs = require('fs'),
  8.     YUI = require('yui').YUI,
  9.     Y = YUI(),
  10.     mods,
  11.     //step functions, defined below
  12.     step1, step2, step3;
  13.  
  14. //Three steps:
  15. //
  16. //First, iterate over all our source files and collect their individual
  17. //dependency information.
  18. //This information is than injected into the loader, so that it can
  19. //calculate dependencies for *our* modules - by default it only has
  20. //the necessary meta-information for YUI3 modules.
  21. //
  22. //The second step is to write the collected information as string into
  23. //our YUI3 startup code, to add it to the YUI3 loader configuration
  24. //object - IN THE GROUPS SECTION: if added to the global section the loader
  25. //will try to load it from the location of the YUI3 modules, since we do
  26. //not add path or fullpath info. We have group-configuration for how to
  27. //load OUR modules, however, so the info for OUR modules should go in there.
  28. //Now the loader running in our application is going to have
  29. //this information available when loading (OUR) modules on demand.
  30. //
  31. //In the third step we iterate over a number of collections of modules,
  32. //let the loader calculate all the dependent modules required (using
  33. //our collected meta-information), and write a file containing the combined
  34. //code for ALL the required modules.
  35. //Each HTML page in our application then loads this combo-file on startup,
  36. //so that the initial page load has all modules it requires right from the
  37. //start. Modules required later, based on user actions, are loaded on
  38. //demand.
  39. //This method significantly reduces page startup time because no additional
  40. //requests for javascript files beyond the one combo file are necessary.
  41.  
  42. /*
  43.  * STEP 1
  44.  *
  45.  * Collect the meta information from our application modules, and when
  46.  * done call the next step. This step only collects information, it does
  47.  * not do anything with it.
  48.  */
  49.  
  50. step1 = function () {
  51.     //This is where we collect the module information (name and dependencies, see
  52.     //http://yuilibrary.com/yui/docs/yui/yui-loader-ext.html)
  53.     var modules = {},
  54.         //This script is in the top level directory of our application codebase,
  55.         //and the build directory.
  56.         dir = path.join(__dirname, 'build/'),
  57.         files, i, file, stats, module_code, o;
  58.  
  59.     //Iterate over all minified files (which are directly) in the build-directory.
  60.     files = fs.readdirSync(dir);
  61.  
  62.     for (i = 0; i < files.length; i++) {
  63.  
  64.         //shortcut that includes the directory
  65.         file = dir + files[i];
  66.  
  67.         stats = fs.statSync(file);
  68.  
  69.         //Look only at those files loaded on-demand by our app, and only at one
  70.         //of either the debug or the minified versions.
  71.         if (stats.isFile() && file.search(/-min.js$/) !== -1 && file.search(/start-/) === -1 ) {
  72.  
  73.             module_code = fs.readFileSync(file, 'utf8');
  74.  
  75.             //Note that this function is executed immediately, giving us exactly
  76.             //the same as block scope.
  77.             //By executing the module code inside this function we have a
  78.             //temporary scope. This way we avoid tainting he outside scope,
  79.             //for example the YUI.add function, which we would otherwise
  80.             //overwrite for the entire application.
  81.             o = (function () {
  82.                 var YUI = {}, t;
  83.  
  84.                 //Overwrite the YUI.add function to be able to simply run the module files
  85.                 //to capture the module meta data. We are interested in the 1st and the 4th
  86.                 //argument only, name and the "details".
  87.                 YUI.add = function (name, body, version, details) {
  88.                     return {
  89.                         name: name,
  90.                         details: details
  91.                     };
  92.                 };
  93.  
  94.                 //Execute the module. The only thing executed should be a YUI.add()
  95.                 //function with 4 arguments, simply because YUI3 modules consist of
  96.                 //nothing but this one function:
  97.                 //Also see http://yuilibrary.com/yui/docs/api/classes/YUI.html
  98.                 //  YUI.add(
  99.                 //      "my-module",  //module name
  100.                 //      function (Y) {
  101.                 //          //the actual module code
  102.                 //          ...
  103.                 //      },
  104.                 //      "1.0",  //module version
  105.                 //      {
  106.                 //          requires: [
  107.                 //              "my-other-module"
  108.                 //          ]
  109.                 //      }
  110.                 //  );
  111.                 t = eval(module_code);
  112.  
  113.                 //The outside only gets this object returned by our YUI.add function above.
  114.                 //Of course, this assumes we don't get malicious code - this is NOT "security".
  115.                 return t;
  116.             }());
  117.  
  118.             //...which we can now collect to inject it into the loader in the next step.
  119.             //See page http://yuilibrary.com/yui/docs/yui/loader.html, configuration
  120.             //property "modules", for all options.
  121.             modules[o.name] = o.details;
  122.  
  123.         }  // if... (valid file)
  124.  
  125.     }  //for... (all files)
  126.  
  127.     //return the collected module meta information
  128.     return modules;
  129. };
  130.  
  131. /*
  132.  * STEP 2
  133.  *
  134.  * Add the collected meta information to our application file's YUI3
  135.  * configuration object.
  136.  */
  137. step2 = function (modules) {
  138.     var tmp;
  139.  
  140.     //Our ant build script produces build/start-debug.js (from src/start.js), then calls
  141.     //this script, and then produces start-min.js from it.
  142.     tmp = fs.readFileSync(path.join(__dirname, 'build/start-debug.js'), 'utf8');
  143.     tmp = tmp.replace(/\/\/DO NOT TOUCH THIS LINE - MODULES INSERTED BY BUILD SCRIPT/, "modules: " + JSON.stringify(modules, null, 4) + ",");
  144.     fs.writeFileSync(path.join(__dirname, 'build/start-debug.js'), tmp, 'utf8');
  145. };
  146.  
  147. /*
  148.  * STEP 3
  149.  */
  150.  
  151. //left out from this code sample - same as node.js CLI example at
  152. //http://yuilibrary.com/yui/docs/yui/loader-resolve.html
  153.  
  154. mods = step1();
  155. //The next steps both require the module dependency information we just collected.
  156. step2(mods);
  157. //step3(mods);