thanpolas // web development as it happens

A new solution for Javascript dependency management is now ready. Announcing the release of Mantri, a traditionaλ dependency system.

What? Why? How?

Mantri will discreetly manage your Javascript app dependencies during development and get out of the way in production.

This means smaller JS files for your production, absolute freedom of development expression and testing your codebase becomes silly easy.

Mantri is based on the powerful Google Closure Tools, it hacks and wraps around them, providing a robust and developer friendly API. It is comprised of a front-end library and back-end tasks for calculating your dependency tree and building your application for production.

The Ceremony

Each file should provide a unique namespace and can require any number of other namespaces:

goog.provide('app');

goog.require('app.router');
goog.require('app.controller');
goog.require('app.view');

The namespace that you provide, is actually a namespace. The function call goog.provide('app.view.about') is equivalent to:

app = app || {};
app.view = app.view || {};
app.view.about = app.view.about || {};

Because goog.provide() only initializes properties if they don't already exist, it never overwrites properties.

Path and Filename Discovery

The deps task is another key part of Mantri. The job of declaring the actual filenames and paths of your JS assets (modules?) is done by a back-end task. This task, needs to run whenever you change or create dependency declarations.

This operation allows you to move folders around, with hundreds of files inside them without any issues. You just run the deps task once and all paths are updated automatically.

And since this is 2013, our lives are so much easier by using a watch task to have Grunt automatically run the deps task for us.

Mantri is Synchronous

The only thing that's required in your Document for Mantri to work is a single script element.

<script src="js/libs/mantri.web.js"></script>

When the browser parses that element and evaluates Mantri's code, operations start executing synchronously. A request is made to fetch the configuration file mantriConf.json. Mantri will then use the document.write() method to synchronously inject <script> elements into your Document. The result is that all your JS files are loaded and parsed before the DOMContentLoaded event is fired.

All your code is loaded and evaluated before the browser finishes parsing your Document.

That's another aspect of the traditionaλ nature of Mantri. In the end, the Document will look like exactly as it would if you wrote all your app's <script> elements yourself.

Mantri is Testable

Easily. Naturally. Traditionaλy.

Since you can directly access any part of your codebase via the global namespace, it is silly easy to mock anything or unit test everything! Even private methods. A feat that can proove nearly impossible with AMD or even commonJS modules.

There Is Nothing Left in Production But Your Code

Smaller. Faster. Awesommmnerz.

During the build operation, Mantri will bundle your application into one file and remove all dependency declarations. They are no longer needed.

No Mantri runtime code included either! Only your code and your third-party dependencies, packed in a single dense file.

Please do not use the dependency system in a production environment. It is highly discouraged as the design patterns used will significantly slow down your page load time. Use only the optimized and minified single file that is produced by the build operation. Mantri is not a module loader for your live site.

Mantri Does Not Dictate How You Write Your Code

Mantri does not dictate how you write or structure your code. As long as you properly provide and require unique namespaces you are good to go.

You can then use the declared namespace as the equivalent of module.exports:

goog.provide('app.view.login');

(function(){
  var viewLogin = app.view.login = {};

  viewLogin.open = function(){ /* ... */ };
  /* ... */
})();

There Are no Conflicts With Other Libraries

By design, Google's dependency system that Mantri incorporates, is agnostic of its surroundings. A nice way to think of Mantri is as a plain python file operation that also happens to know how to talk Javascript.

That leaves plenty of room for hacking, abusing and generally knocking yourselves out with any imaginable combination of dependency management libraries.

A Simple Dependency System

Mantri focuses on providing a robust and scalable development environment enabling multiple teams collaborating seamlessly.

Mantri provides a robust and scalable development environment enabling multiple teams collaborating seamlessly. And it gets out of your way on production with a very simple API.

Bottom line is, you'll only ever need two commands for the CLI.

**mantri watch** Will monitor all your javascript files and automatically run the deps task for you.

mantri build Will start the build operation.

That's All!

These simple and powerful commands are all you need.

Of course you have full configuration power. You can use a set of command line options or try Mantri as a Grunt Plugin. Finally, a developer API is exposed for node.js, which you can simply require() to your codebase.

Why Namespaces And not Paths?

You only need to declare and require Namespaces! Paths will be automatically discovered by the deps task. Using namespaces is what being traditionaλ is all about.

Namespaces is the native way for Javascript to hierarchically organize the structures of your application. Having the ability to directly reference and study your objects from the developer console is priceless. Just hit app.router._currentView on the console and see exactly what's going on. For any part of your code, at any time.

There are other ways of finding out what the value of a private variable is, like console.log() or the debugger and break-points, but in my humble opinion, it's only fair to say that one more valuable tool in your utility belt does't hurt.

Exposing Internal Structures to Consumers

Using namespaces expose the internal structures of my app, I want privates to be privates and not accessible from the outside!

Certainly! That's as easy as writing an iife and instruct the build task to wrap your code inside this immediately invoked function expression:

;(function(){ %output% })();

That's all that's required, really. And it only happens to your production ready file.

Personally, i don't care about privacy. When I author a library, that library is intended to be used by developers. I trust developers. I trust that if you see a method or a property starting with an underscore, you know you are entering dangerous territory. I want to give you the freedom to do it.


I'd love to hear your thoughts and if you find Mantri to be helpful.

blog comments powered by Disqus