Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RaptorJS: A Modular JavaScript System Developed...

RaptorJS: A Modular JavaScript System Developed by eBay (Schmonference Conference)

In this talk, Patrick will share with you the details of RaptorJS, a JavaScript library developed at eBay that supports defining modules and classes that work seamlessly across all JavaScript environments–including Node and Rhino. RaptorJS also provides an adaptive module loader and packager that allows modules to conditionally include code based on the target environment, target device or any other desired criteria. In addition, the system is easily extensible to support any type of resource, not just raw JavaScript and CSS files, and it has allowed eBay to completely decouple itself from tedious build-time packaging systems required by other libraries and tools.

The developers of RaptorJS are excited to be open sourcing RaptorJS and believe that it will be a huge benefit to the web development community.

Talk given at the Schmonference Conference, June 29, 2012

psteeleidem

July 02, 2012
Tweet

More Decks by psteeleidem

Other Decks in Programming

Transcript

  1. RAPTORJS:  A  MODULAR  JAVASCRIPT   SYSTEM  DEVELOPED  BY  EBAY  

    Presented  by:   Patrick  Steele-­‐Idem   Presenta5on  Pla7orm  Engineer  @  eBay     Schmonference  Conf,  June  2012  
  2. Overview   •  Why?   •  Packaging  code   • 

    Delivering  code   •  Wri@ng  code  
  3. Managing  dependencies  in  JavaScript     applica@ons  can  be  hard.

        Dependencies  change.  Dependencies  can  be   transi@ve.  Order  oGen  maHers.   JS   JS   JS   JS   JS   JS   ?  
  4. Should  the  users  of  your  code  have  to  know  

    all  of  those  dependencies?     We  don’t  think  so.  
  5. JavaScript  Modules   JS   JS   JS   JS

      JS   JS   moduleA   moduleB   moduleC   JS   JS   JS   JS   JS   JS  
  6. Challenges  eBay  Faced   •  Adap@ve  packaging   •  Performance

     is  cri@cal  and  bundling  is  very   important   •  Server-­‐side  JavaScript  support  required   •  Keep  things  simple   •  Parallel  downloading  and  immediate   execu@on   •  Run@me  packaging  (no  build  tools)   •  CSS/JS  code  delivered  in  Jar  files  
  7. Modular  JavaScript  based  on  CommonJS   and/or  Asynchronous  Module  Defini@on

      (AMD)  help  a  lot,  but  there  hasn't  been   convergence  of  the  specifica@ons.  
  8. Modules:  CommonJS  vs  AMD   define(      "test/my-­‐amd-­‐module",  

         ["./another-­‐module"],        function(anotherModule)  {          anotherModule.doSomething();            return  {              greet:  function()  {                  console.log("Hello!");              }          }      });   var  anotherModule  =  require("./another-­‐module.js");   anotherModule.doSomething();     exports.greet  =  function()  {      console.log("Hello!");   }   node_modules/test/my-­‐commonjs-­‐module.js   scripts/test/my-­‐amd-­‐module.js   Doesn't  work  in  the  browser   More  verbose  and  callbacks  required  
  9. RaptorJS  Is  Not:   •  A  replacement  for  jQuery/Backbone/etc.  

    •  A  UI  framework   •  A  replacement  module  loader  for  Node   •  An  MV**  framework  
  10. Introducing  RaptorJS!     •  A  packager  and  op@mizer  

    •  An  op@onal  asynchronous  module  loader  for  the   browser   •  A  JavaScript  library  suppor@ng  the  require/define   module  paHern  (plus  support  for  class  inheritance,   mixins  and  enums)   •  A  module  loader  for  server-­‐side  JavaScript   RaptorJS  is  a  set  of  tools  that  includes  the  following:  
  11. Packaging  JavaScript  Modules   How  do  you  describe  the  dependencies

     of  a   module?       Answer:  package.json     Modules  can  resolve  to  package.json  files,  not  just   JavaScript  files.     A  package.json  file  allows  rich  metadata  and   dependency  informa@on  to  be  aHached  to  a   module.  
  12. Sample  package.json   {          "name":  "test",

             "version":  "1.0",          "description":  "A  test  module",          "authors":  [  ...  ],              "includes":  [                  {"module":  "listeners"},                  {"path":  "test.js"},                  {"path":  "TestClass.js"}          ]                     }   Op@onal   metadata   These  files  are  always   included/loaded  (in  order)   This  file  only  get  loaded  on  pages  that   have  the  “jquery”  extension  enabled   Op@onal   extensions   This  module  depends  on  the  “listeners”  module            "extensions":  [                  {                          "name":  "jquery",                          "includes":  [                                  {"path":  "test_jquery.js"}                          ]                  }          ]  
  13. The  RaptorJS  packager  is  source  code  agnos@c   since  all

     dependencies  are  described  external   from  the  code  in  package.json  files.  
  14. Adap@ve  Packaging   JavaScript  applica@ons  have  to  run  in  an

      increasingly  heterogeneous  environment—each   with  their  own  limita@ons  and  features.     RaptorJS  allows  developers  to  create  modules  that   adapt  to  the  environment  they  are  being  loaded  in.  
  15. Custom  Include  Handlers   {          ...

             "includes":  [                  {"type":  "mustache",  "path":  "my-­‐template.mustache"},                  {"path":  "my-­‐template.handlebars"},                  {"path":  "my-­‐styles.less"}          ]          ...   }   RaptorJS  supports  any  type  of  resource.  Including:     JavaScript,  CSS,  Less,  Dust,  Mustache,  CoffeeScript,  etc.   {          ...          "include-­‐handlers":  {                  "mustache":  {"class":  "mustache.MustacheIncludeHandler"},                  "less":  {"class":  "less.LessIncludeHandler"}          }          ...   }   Explicit  include  type   Type  based  on  file  extension  
  16. Flexible  Bundling  System   {      "pages":  [  

           {              "name":  "my-­‐page",              "includes":  [                  {  "module":  "moduleA"  },                  {  "module":  "moduleC"  }              ]          },          ...      ]   }   {      "bundles":  [          {              "name":  "my-­‐bundle",              "includes":  [                  {  "module":  "moduleA"  },                  {  "module":  "moduleB"  }              ]          },          ...      ]   }   bundle-­‐my-­‐bundle-­‐d60337ab.js   bundle-­‐my-­‐bundle-­‐6f1b08ca.css   <link  href="/bundle-­‐my-­‐bundle-­‐6f1b08ca.css">   <link  href="/page-­‐my-­‐page-­‐d0480e22.css">   ...   <script  src="/bundle-­‐my-­‐bundle-­‐d60337ab.js">   <script  src="/page-­‐my-­‐page-­‐77259164.js"  >  
  17. The  RaptorJS  Packager   •  Generates  JavaScript/CSS  bundles  and  the

      HTML  markup  to  include  in  your  pages   •  Generates  files  with  checksums  (cache   forever)   •  Use  at  build-­‐@me  or  run@me  
  18. Run@me  versus  Build-­‐Time     Packaging   •  Advantages  of

     Build-­‐@me  packaging   – Very  predictable  performance  at  run@me   – Easy  to  implement   – Very  reliable   – No  server-­‐side  support  required   •  Advantages  of  Run@me  packaging   – Modules  can  adapt  to  each  type  of  user   – Less  reliance  on  build  tools  
  19. The  RaptorJS  Async  Module  Loader   •  Extremely  simple  implementa@on

      •  All  URLs  must  be  provided  by  the  server   – URLs  include  checksums   •  Download  everything  in  parallel   •  Immediate  execu@on  
  20.   <link  rel="stylesheet"  type="text/css"  href="hHp://my-­‐cdn.com/my-­‐app/bundle-­‐myBundle-­‐a951bd18.css">   ...   <script  src="hHp://my-­‐cdn.com/my-­‐app/bundle-­‐core-­‐7039ab34.js"></script>

      <script  src="hHp://my-­‐cdn.com/my-­‐app/bundle-­‐testBundle-­‐6e0508ca.js"  async="true"></script>   Packager  HTML  Output   <script  type="text/javascript">_asyncModules={          "moduleA":  {              "requires":  ["moduleB",  "moduleC"],              "js":  ["http://my-­‐cdn.com/my-­‐app/bundle-­‐asynBundle-­‐d0480e22.js",                            "http://my-­‐cdn.com/my-­‐app/bundle-­‐myBundle-­‐35b3e071.js"],              "css":  [http://my-­‐cdn.com/my-­‐app/bundle-­‐myBundle-­‐a951bd18.css]          },          "moduleB":  {              "requires":  ["moduleC"],              "js":  ["http://my-­‐cdn.com/my-­‐app/bundle-­‐anotherBundle-­‐78ea03e1.js"]          },          "moduleC":  {              "js":  ["http://my-­‐cdn.com/my-­‐app/bundle-­‐common-­‐9eb172af.js"]          }   }   </script>   HTML  fragments  generated  by  the  packager  
  21. Object-­‐oriented  Programming   RaptorJS  provides  support  for  modules,  classes,  

    mixins  and  enums.       RaptorJS  classes  support  inheritance.     All  objects  are  lazily  ini@alized  and  created  using  a   factory  func@on.  The  factory  func@on  allows  each   object  to  have  a  private  namespace  using  func@on   closures.  
  22. Object-­‐oriented  Programming   Defining  Modules   raptor.define  (    

         "shapes",          function(raptor)  {                                    var  Rectangle  =  raptor.require("shapes.Rectangle"),                          Circle  =  raptor.require("shapes.Circle");                                    return  {                          createRectangle:  function(width,  height)  {                                  return  new  Rectangle(width,  height);                          },                                                    createCircle:  function(radius)  {                                  return  new  Circle(radius);                          },                                                    getTotalArea:  function(shapes)  {                                  var  area  =  0;                                  raptor.forEach(shapes,  function(shape)  {                                          area  +=  shape.getArea();                                  });                                  return  area;                          }                  };          });   Module  name   Factory   Func@on   Local/ private   variables   Module   proper@es  
  23. Object-­‐oriented  Programming   Defining  Classes   raptor.defineClass(      

       “some.namespace.MyClass”,          function()  {                  var  myPrivate  =  "Hello  World";                            var  MyClass  =  function(a,  b)  {                          this.a  =  a;                          this.b  =  b;                  };                            MyClass.MY_STATIC  =  “Hello!”;                    MyClass.prototype  =  {                          method1:  function()  {                          },                              method2:  function()  {                          }                  };                                    return  MyClass;          })   Class  name   Factory   Func@on   Local/private   variables   Constructor   Class   prototype   Sta@cs  
  24. Object-­‐oriented  Programming   Prototype-­‐based  Inheritance   raptor.defineClass(      

       “shapes.Rectangle”,          “shapes.Shape”,          function()  {                  var  Rectangle  =  function(width,  height)  {                          Rectangle.superclass.constructor.call(                                  this,  “rect”);                          this.width  =  width;                          this.height  =  height;                  };                                    Rectangle.prototype  =  {                          getArea:  function()  {                                  return  this.width  *  this.height;                          }                  };                                    return  Rectangle;          });   Superclass  name   Returned  class  gets  augmented  with   "superclass"—a  reference  to  the   prototype  of  the  superclass  
  25. Object-­‐oriented  Programming   Mixins   raptor.defineMixin(        

     “shapes.CircleMixins”,          function()  {                  area:  function()  {                          return  Math.PI  *  this.radius  *  this.radius;                  },                  scale:  function(percentage)  {                          this.radius  *=  percentage;                  }          });     raptor.defineClass(          “ui.CircleButton”,          {  "mixins":  ["shapes.CircleMixins”]  },          function()  {                  ...          });   Mixin  name   An  array  of  mixins  to  apply  to   the  class  prototype  
  26. Object-­‐oriented  Programming   Using  Modules  and  Classes   //  1)

     Obtaining  a  reference  to  the  “shapes”  module   var  shapes  =  raptor.require("shapes");     //  2)  Creating  an  instance  of  a  Rectangle   var  rect  =  shapes.createRectangle(5,  6);     //  3)  Obtaining  a  reference  to  the  “shapes.Circle”  class:   var  Circle  =  raptor.require("shapes.Circle");   //NOTE:  Circle  is  a  reference  to  the  constructor  function     //  4)  Creating  a  new  instance  of  a  Circle:   var  circle  =  new  Circle(25);     //  5)  Loading  modules  asynchronously:   raptor.require(          ['moduleA',  'moduleB'],            function(moduleA,  moduleB)  {                  //Do  something  with  the  loaded  modules...          });    
  27. Cross-­‐environment  Modules   Defining  Extensions   raptor.extend(      

       ”json",          function()  {                                    return  {                          parse:  function(str)  {                                  return  $.parseJSON(str);                          }                  };          });   Name  of  module  or   class  being  extended   Factory   Func@on   Mixin   proper@es   Extension  mixins  are  lazily  applied   when  a  module/class  is  first  ini@alized.  
  28. Tips  for  Tes@ng   U@lize  NodeJS  to  allow  your  RaptorJS

     modules  to  be   tested  on  the  server  from  within  your  IDE.     Write  tests  using  Jasmine  or  mocha,  both  which   supports  behavior-­‐driven  development  (BDD).     A  simulated  DOM  is  even  available  on  the  server  using   the  jsdom  module  for  Node.JS!     Sta@c  code  analysis  can  easily  be  done  using  the   JSHint  module  for  NodeJS.  Easily  customize  the  rules.     JavaScript  code  coverage  can  reportedusing  node-­‐ jscoverage!  
  29. Design  Philosophies   Cross-­‐environment   Same  APIs  in  mul@ple  environments

      API  Documenta@on   Automa@cally  generated  documenta@on   Reusable  Code   Packaging  and  dependency  management   Object  Oriented   Modules,  classes  and  mixins   Easily  Testable   Server-­‐side  tes@ng  and  sta@c  code  analysis   Lightweight  and  Fast   Lazy  ini@aliza@on,  @ny  footprint  and     clean  syntax   Framework  Agnos@c   Use  RaptorJS  with  your  favorite  libraries   Modular  and  Adap@ve   Load  only  what  you  need  
  30. That’s  all…for  now.  Ques@ons?     Contact  Informa<on:   Patrick

     Steele-­‐Idem  <[email protected]>   TwiHer:  @psteeleidem     Open  Source!   Source  code  will  be  on  Github  very  soon.  Follow  me   on  TwiHer  for  the  official  announcement!    
  31. Op@mizing  Web  Applica@ons   •  All  URLs  should  be  served

     up  by  a  CDN  or  caching   proxy   •  All  URLs  should  include  an  accurate  checksum   –  Accurate  checksums  cannot  be  generated  from  the  client   •  All  resources  should  be  set  to  cache  "permanently"  (no   revalidate)   •  Keep  bundles  consistent  across  pages!   •  Not  all  code  needs  to  be  loaded  asynchronously  to  be   non-­‐blocking!   •  Loading  ini@al  applica@on  code  should  not  depend  on  a   loader  to  be  loaded  first  
  32. How  is  RaptorJS  different?     •  Supports  modules,  class

     inheritance,  enums  and  mixins   •  Very  small   –  define/require:  4.53KB  (2KB  gzipped)   –  op@onal  async  loader:  5.46KB  (2.2KB  gzipped)   •  Very  simple  loader  (no  configura@on)   •  Supports  run@me  packaging  (no  build  tools)   •  Instead  of  resolving  to  a  file,  a  RaptorJS  module  resolves  to   a  "package.json"  files   –  One  module  per  directory   –  Module  can  be  split  it  into  one  or  more  files   •  Supports  declara@ve  extensions   •  Supports  any  type  of  resource  and  custom  preprocessors  
  33. A  Note  on  Rela@ve  Paths   •  Rela@ve  paths  on

     the  server...   – No  more  version  conflicts!  Awesome!   – Allows  different  versions  of  the  same  modules  to   be  independently  loaded   •  Rela@ve  paths  on  the  client...   – Doesn't  really  work  since  rela@ve  paths  require   context  (i.e.  its  absolute  path)   •  This  informa@on  is  not  in  your  code   – Do  you  really  want  to  send  mul@ple  versions  of   the  same  module  to  the  client?    
  34. Cross-­‐environment  Module  Example:  hHp.cookies   Web  Browser:  cookies_browser.js   loadCookies:

     function()  {   var  cookies  =  document.cookies.split(“;”);   for  (var  i=0;  i<cookies.length;  i++)  {          …   }   }     Mozilla  Rhino:  cookies_rhino.js   loadCookies:  function()  {   var  cookies  =  request.getCookies();   for  (var  i=0;  i<cookies.length;  i++)  {          …   }   }     Using  the  “hOp.cookies”  module  (any  environment):   var  cookies  =  raptor.require(“http.cookies”);   var  myCookie  =  cookies.getCookieValue(“myCookie”);   HHpServletRequest