Skip to content

Latest commit

 

History

History
247 lines (186 loc) · 7.49 KB

README.md

File metadata and controls

247 lines (186 loc) · 7.49 KB

modules-webmake

Bundle CommonJS/Node.js modules for web browsers.

Webmake allows you to organize JavaScript code for the browser the same way as you would for Node.js.

For a more in depth look into JavaScript modules and the reason for Webmake, see the slides from my presentation at Warsaw's MeetJS: JavaScript Modules Done Right

How does it work?

Let's say in package named foo you have following individual module files:

add.js

module.exports = function() {
  var sum = 0, i = 0, args = arguments, l = args.length;
  while (i < l) sum += args[i++];
  return sum;
};

increment.js

var add = require('./add');
module.exports = function(val) {
  return add(val, 1);
};

program.js

var inc = require('./increment');
var a = 1;
inc(a); // 2

Let's pack program.js with all it's dependencies so it will work in browsers:

$ webmake program.js build.js

The generated file build.js now contains the following:

(function (modules) {
  // about 60 lines of import/export path resolution logic
})({
  "foo": {
    "add.js": function (exports, module, require) {
      module.exports = function () {
        var sum = 0, i = 0, args = arguments, l = args.length;
        while (i < l) sum += args[i++];
        return sum;
      };
    },
    "increment.js": function (exports, module, require) {
      var add = require('./add');
      module.exports = function (val) {
        return add(val, 1);
      };
    },
    "program.js": function (exports, module, require) {
      var inc = require('./increment');
      var a = 1;
     inc(a); // 2
    }
  }
})("foo/program");

When loaded in browser, program.js is immediately executed.

Installation

$ npm install -g webmake

Usage

From the shell:

$ webmake [options] <input> <output>

input - Path to the initial module that should be executed when script is loaded.
output - Filename at which browser ready bundle should be saved

Options

name string

Name at which program should be exposed in your namespace. Technically just assigns exported module to global namespace.

amd string

Expose bundle as AMD module. If used together with name option, module will be defined with provided name.

include string

Additional module(s) that should be included but due specific reasons are not picked by parser (can be set multiple times)

sourceMap boolean

Include source maps, for easier debugging. Source maps work very well in WebKit and Chrome's web inspector. Firefox's Firebug however has some issues.

Programmatically:

webmake(inputPath[, options][, callback]);

webmake by default returns generated source to callback, but if output path is provided as one of the options, then source will be automatically saved to file

Development with Webmake

Currently best way is to use Webmake programmatically and setup a static-file server to generate bundle on each request. Webmake is fast, so it's acceptable approach even you bundle hundreds of modules at once.

You can setup simple static server as it's shown in following example script.
It uses also node-static module to serve other static files (CSS, images etc.) if you don't want it, just adjust code up to your needs.

// Dependencies:
var createServer = require('http').createServer;
var staticServer = require('node-static').Server;
var webmake      = require('webmake');

// Settings:
// Project path:
var projectPath  = '/Users/open-web-user/Projects/Awesome';
// Public folder path (statics)
var staticsPath  = projectPath + '/public';
// Path to js program file
var programPath = projectPath + '/lib/public/main.js';
// Server port:
var port = 8000;
// Url at which we want to serve generated js file
var programUrl = '/j/main.js';

// Setup statics server
staticServer = new staticServer(staticsPath);

// Initialize http server
createServer(function (req, res) {
  // Respond to request
  req.on('end', function () {
    if (req.url === programUrl) {
      // Generate bundle with Webmake

      // Send headers
      res.writeHead(200, {
        'Content-Type': 'application/javascript; charset=utf-8',
        // Do not cache generated bundle
        'Cache-Control': 'no-cache'
      });

      var time = Date.now();
      webmake(programPath, { sourceMap: true }, function (err, content) {
        if (err) {
          console.error("Webmake error: " + err.message);
          // Expose eventual error brutally in browser
          res.end('document.write(\'<div style="font-size: 1.6em; padding: 1em;'
            + ' text-align: left; font-weight: bold; color: red;'
            + ' position: absolute; top: 1em; left: 10%; width: 80%;'
            + ' background: white; background: rgba(255,255,255,0.9);'
            + ' border: 1px solid #ccc;"><div>Could not generate ' + programUrl
            + '</div><div style="font-size: 0.8em; padding-top: 1em">'
            + err.message.replace(/'/g, '\\\'') + '</div></div>\');');
          return;
        }

        // Send script
        console.log("Webmake OK (" + ((Date.now() - time)/1000).toFixed(3) + "s)");
        res.end(content);
      });
    } else {
      // Serve static file
      staticServer.serve(req, res);
    }
  });
}).listen(port);
console.log("Server started");

Using Webmake with Express or Connect

Try Middleware prepared by Gilles Ruppert

Limitations

The application calculates dependencies via static analysis of source code (with the help of the find-requires module). So in some edge cases not all require calls can be found. You can workaround that with help of include option

Only relative paths and outer packages paths are supported, following will work:

require('./module-in-same-folder');
require('./module/path/deeper');
require('./some/very/very/very/long' +
'/module/path');
require('../../module-path-up'); // unless it goes out of package scope
require('other-package');
require('other-package/lib/some-module');

But this won't:

require('/Users/foo/projects/awesome/my-module');

Different versions of same package will collide:
Let's say, package A uses version 0.2 of package C and package B uses version 0.3 of the same package. If both package A and B are required, package B will most likely end up buggy. This is because webmake will only bundle the version that was called first. So in this case package B will end up with version 0.2 instead of 0.3.

Tests Build Status

$ npm test

Contributors

  • @Phoscur (Justus Maier)
    • Help with source map feature
  • @jaap3 (Jaap Roes)
    • Documentation quality improvements

'Issue 2198: @sourceURL doesn't work in eval() in some cases'