{"id":4123,"date":"2012-03-24T01:41:14","date_gmt":"2012-03-24T05:41:14","guid":{"rendered":"http:\/\/www.ericfeminella.com\/blog\/?p=4123"},"modified":"2016-03-30T14:20:59","modified_gmt":"2016-03-30T18:20:59","slug":"preprocessing-modules-with-requirejs-optimizer","status":"publish","type":"post","link":"https:\/\/www.ericfeminella.com\/blog\/2012\/03\/24\/preprocessing-modules-with-requirejs-optimizer\/","title":{"rendered":"Preprocessing Modules with RequireJS Optimizer"},"content":{"rendered":"<p><a href=\"http:\/\/requirejs.org\/\" target=\"_blank\">RequireJS<\/a> provides an effective means of bundling an application for production deployment by way of the RequireJS <a href=\"http:\/\/requirejs.org\/docs\/optimization.html\" target=\"_blank\">Optimizer<\/a>; which allows for optimizing, concatenating and minifying <a href=\"http:\/\/requirejs.org\/docs\/api.html#define\" target=\"_blank\">modules<\/a> via <a href=\"http:\/\/nodejs.org\/\" target=\"_blank\">Node<\/a> or <a href=\"http:\/\/www.mozilla.org\/rhino\/\" target=\"_blank\">Rhino<\/a>. <\/p>\n<p>The Optimizer is quite powerful insofar that <a href=\"https:\/\/github.com\/jrburke\/r.js\/blob\/master\/build\/example.build.js\" target=\"_blank\">fine-grained<\/a> control over various aspects of the optimization process can be implemented with relative ease. One such example is the ability to specify a callback function to be invoked for each module in the build prior to optimization. This essentially provides the necessary hooks needed to implement a preprocessor.<\/p>\n<h2>The onBuildWrite option<\/h2>\n<p>The <code>optimize<\/code> method of the RequireJS Optimizer accepts an <code>onBuildWrite<\/code> option which allows for a callback to be specified. The callback will be invoked prior to serialization of each module within the optimized bundle. Callbacks receive the name, path and contents of the module; and, are always expected to return a value. <\/p>\n<p>For example, consider the following build configuration which demonstrates a basic <code>onBuildWrite<\/code> callback that simply logs the name of each module processed by the build to the console and, returns the module&#8217;s content unmodified.<\/p>\n<pre lang=\"js\">\r\nvar config = {\r\n  baseUrl : 'src',\r\n  appDir  : '..\/',\r\n  dir     : '..\/deploy',\r\n  mainConfigFile : '..\/src\/main.js',\r\n  onBuildWrite   : function(name, path, contents) {\r\n    console.log('Writing: ' + name);\r\n    return contents\r\n  },\r\n  modules: [{\r\n    name: 'main'\r\n  }]\r\n};\r\nrequirejs.optimize(config, function(results) {...});\r\n \r\n<\/pre>\n<p>Using the above configuration, when executed against a (contrived) example consisting of just a single module, &#8220;ModuleA&#8221;, in Node, it would output the following to the console:<\/p>\n<pre lang=\"sh\">\r\n$ node build.js \r\nWriting: main\r\nWriting: ModuleA\r\n\r\nsrc\/main.js\r\n----------------\r\nsrc\/app\/module.a.js\r\nsrc\/main.js\r\n<\/pre>\n<p>If we were to print out the contents of the files we would see something like this:<\/p>\n<pre lang=\"sh\">\r\n$ node build.js \r\nWriting: ModuleA\r\ndefine(function() {\r\n  return {\r\n    name: \"Module A\"\r\n  }\r\n});\r\nWriting: main\r\nrequire.config({\r\n  paths: {\r\n    ModuleA: 'app\/module.a'\r\n  }\r\n});\r\nrequire(['ModuleA'], function(moduleA) {...});\r\n\r\nsrc\/main.js\r\n----------------\r\nsrc\/app\/module.a.js\r\nsrc\/main.js\r\n<\/pre>\n<p>With this in mind, a basic preprocessor can be implemented quite easily using the <code>onBuildWrite<\/code> option. Assume the <code>main.js<\/code> script has a token placeholder for the build version like so:<\/p>\n<pre lang=\"js\">\r\n\/*!\r\n * #version\r\n *\/\r\nrequire.config({\r\n  paths: {\r\n    ModuleA: 'app\/module.a',\r\n  }\r\n});\r\nrequire(['ModuleA'], function(moduleA) {...});\r\n<\/pre>\n<p>We can implement a simple preprocessor which replaces the <code>#version<\/code> token with a build date as follows:<\/p>\n<pre lang=\"js\">\r\nvar config = {\r\n  baseUrl : 'src',\r\n  appDir  : '..\/',\r\n  dir     : '..\/deploy',\r\n  mainConfigFile : '..\/src\/main.js',\r\n  onBuildWrite   : function(name, path, contents) {\r\n    console.log('Writing: ' + name);\r\n    if (name === 'main') {\r\n      \/\/ output the original source contents\r\n      console.log(contents);\r\n      \/\/ perform transformations on the original source\r\n      contents = contents.replace(\/#version\/i, new Date().toString());\r\n      \/\/ output the processed contents\r\n      console.log(contents);\r\n    }\r\n    \/\/ return contents\r\n    return contents;\r\n  },\r\n  modules: [{\r\n    name: 'main'\r\n  }],\r\n};\r\nrequirejs.optimize(config, function(results) {...});\r\n<\/pre>\n<p>The above <code>onBuildWrite<\/code> callback logs the original contents, replaces the <code>#version<\/code> token with the current date, logs the result and returns the processed content. The output of which would be similar to the following:<\/p>\n<pre class=\"lang:sh theme:twilight\">\r\n$ node build.js\r\nWriting: main\r\n\/*!\r\n * #version\r\n *\/\r\nrequire.config({\r\n  paths: {\r\n    ModuleA: 'app\/module.a'\r\n  }\r\n});\r\nrequire(['ModuleA'], function(moduleA) {...});\r\n\/*!\r\n * Fri Mar 23 2012 23:35:17 GMT-0400 (EDT)\r\n *\/\r\nrequire.config({\r\n  paths: {\r\n    ModuleA: 'app\/module.a'\r\n  }\r\n});\r\nrequire(['ModuleA'], function(moduleA) {...});\r\n\r\nsrc\/main.js\r\n----------------\r\nsrc\/app\/module.a.js\r\nsrc\/main.js\r\n<\/pre>\n<p>As can be seen in the above examples, implementing a basic preprocessor is rather simple to accomplish with the RequireJS Optimizer. The ability to do so allows for some interesting possibilities and is certainly something worth checking out if you are leveraging <a href=\"https:\/\/github.com\/amdjs\/amdjs-api\/wiki\/AMD\" title=\"Asynchronous Module Definition\" target=\"_blank\">AMD<\/a> via RequireJS.<\/p>\n<p>You can fork an example implementation at <a href=\"https:\/\/github.com\/efeminella\/requirejs-preprocessor-example\" target=\"_blank\">requirejs-preprocessor-example<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>RequireJS provides an effective means of bundling an application for production deployment by way of the RequireJS Optimizer; which allows for optimizing, concatenating and minifying modules via Node or Rhino. The Optimizer is quite powerful insofar that fine-grained control over various aspects of the optimization process can be implemented with relative ease. One such example is the ability to specify&#8230; <a class=\"read-more\" href=\"https:\/\/www.ericfeminella.com\/blog\/2012\/03\/24\/preprocessing-modules-with-requirejs-optimizer\/\">Continue Reading<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false}}},"categories":[78,35],"tags":[194,193],"class_list":["post-4123","post","type-post","status-publish","format-standard","hentry","category-javascript-2","category-software-engineering","tag-amd-2","tag-requirejs"],"jetpack_publicize_connections":[],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.ericfeminella.com\/blog\/wp-json\/wp\/v2\/posts\/4123","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ericfeminella.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ericfeminella.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ericfeminella.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ericfeminella.com\/blog\/wp-json\/wp\/v2\/comments?post=4123"}],"version-history":[{"count":0,"href":"https:\/\/www.ericfeminella.com\/blog\/wp-json\/wp\/v2\/posts\/4123\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.ericfeminella.com\/blog\/wp-json\/wp\/v2\/media?parent=4123"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ericfeminella.com\/blog\/wp-json\/wp\/v2\/categories?post=4123"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ericfeminella.com\/blog\/wp-json\/wp\/v2\/tags?post=4123"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}