Over the last few months, my previous team and I at Zynga has been working hard on a more granular, JS-based alternative to Jasy. Without further ado, meet the Instant Play plugin collection for Grunt:

  1. Grunt Dependency Resolver (npm, github)
  2. Grunt Cruncher (npm, github)
  3. Grunt Vendor Mutator (npm, github)

In today’s post, I will talk a bit about the Dependency Resolver.

Dependency resolution in JavaScript, solved.

We don’t like package nor dependency management. All manual is the worst, but RequireJS and the likes are not much better: They all require specific syntax and additional code.

As much as kids today won’t understand why phones had cords, we don’t remember when we manually inserted script files into HTML pages. For years now, our previous build solution Jasy enabled us to simply create a new script file with a new class, rerun the build, and reload the page. Jasy automatically figured out where the class created in our new file was used (in a different JS file), and in what order it needed to be loaded. No additional code. No weird require syntax.

However, as much as we love Jasy, it has (to us, at least!) two fundamental flaws: It is written in Python 3, and thus hard to maintain, script and deploy by a JavaScript team, and use of its sister client framework Core was mandatory. Grunt Dependency Resolver solves the same problem, but is framework agnostic and completely written in JavaScript (Note: to be fair – Jasy covers a lot more than just dependency resolution. It also does asset management, permutations, docs and a lot more. For a complete web development framework, you should still check it out).

How does dependency resolution work?

Dependency resolution, in short, takes all your JS source files (given a starting point) and figures out automagically with files are needed to cover the entire functionality, and in which order they have to be included.

The basic process looks like this (provided our root project namespace is called “sample“):

  1. Scan index.html for <script> tags..
  2. Found js/main.js, analyze file for undeclared namespaces..
    1. Found three undeclared namespaces: $, sample.foo, sample.bar
    2. Try to mark files as inclusion candidates that match a pattern: js/bar.js, js/foo.js, js/$.js
    3. Repeat 2) for these files recursively..
  3. Sort all collected files into the right order of inclusion..
  4. Build a dynamic loader..
  5. Create a new version of index.html that links to js/main.loader.js instead!

External package management included!

In our Dependency Resolver, we go a bit further by adding solid, Bower-like external package management. In our case, it looks a little like this:

  1. Scan index.html for <script> tags..
  2. Found js/main.js, analyze file for undeclared namespaces..
    1. Found three undeclared namespaces: $, sample.foo, sample.bar
    2. See if any of the above is an external dependency..
      1. $ matches jQuery external…
      2. Check if jQuery was already included via CDN in index.html..if found, do nothing!
      3. Download latest version of jQuery and place it into the external folder
      4. Dynamically link ‘$’ to external/jquery/jquery-latest.js
    3. Try to mark files as inclusion candidates that match a pattern: js/bar.js, js/foo.js
    4. Repeat 2) for these files recursively..
  3. Sort all collected files into the right order of inclusion..
  4. Build a dynamic loader..
  5. Create a new version of index.html that links to js/main.loader.js instead!

The Dependency resolver supports automatically fetching and updating JS files and whole projects via HTTP, HTTPS, Git (even on separate branches), and is even able to extract zip files. So instead of setting up Git sub-modules in your project, you simply use the external mapping of the Dependency Resolver to other Git repositories and the resolver will take care of the checkout, inclusion and of course, keeping them up to date.

Good to know

  • Detect leaked globals with the scan tool
    In the bin/ folder, you will find a “scan” executable that is super useful as standalone for finding out variable leaks in a JS file. Running “scan file.js” will show you all variables not declared in that file (minus browser globals). But even without running this tool directly, the resolver automatically shows you every unresolved declaration when run. This is super useful to find accidentally leaked globals.
  • Bring your own loader
    The resolver already includes a basic script loader, but you can always provide your own one!
  • Multiple HTML templates with multiple starting points
    It can process multiple templates with multiple included starting points in each, and will still not include a file twice
  • Ready for production.
    In build mode, the resolver creates a single concatenated file with all resources instead of a dynamic loader.
  • Organize your files your way.Using sub namespaces? No problem. For “sample.foo.bar.baz“, the Resolver tries to include either of the following:
    • js/foo.bar.baz.js
    • js/foo/bar.baz.js
    • js/foo.bar/baz.js
    • js/foo/bar/baz.js

How to get started

Head over to the Github repository and read the docs, then check out the samples folder for actual working examples. Start with the very basic one.

When you think you know what you’re doing, simply “npm install grunt-dependency-resolver“.

Have a comment or question?