Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/medikoo/modules-webmake
Bundle CommonJS/Node.js modules for web browser
https://github.com/medikoo/modules-webmake
Last synced: 5 days ago
JSON representation
Bundle CommonJS/Node.js modules for web browser
- Host: GitHub
- URL: https://github.com/medikoo/modules-webmake
- Owner: medikoo
- License: isc
- Created: 2011-05-04T07:40:20.000Z (over 13 years ago)
- Default Branch: master
- Last Pushed: 2021-06-30T16:05:34.000Z (over 3 years ago)
- Last Synced: 2024-05-03T01:23:46.167Z (6 months ago)
- Language: JavaScript
- Homepage:
- Size: 446 KB
- Stars: 410
- Watchers: 16
- Forks: 28
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
[![*nix build status][nix-build-image]][nix-build-url]
[![Windows build status][win-build-image]][win-build-url]
![Transpilation status][transpilation-image]
[![npm version][npm-image]][npm-url]# 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 do for Node.js.**
- Work with best dependency management system that JavaScript currently has.
- Easily, without boilerplate, reuse your modules in any environment that runs JavaScript.
No matter if it's a server, client (any web browser) any other custom environment as e.g. Adobe Photoshop, or let's say your dishwasher if it speaks JavaScript.
- Require **CSS** and **HTML** files same way. Webmake allows you to require them too, which makes it a full stack modules bundler for a web browser.Files support can be extended to any other format that compiles to one of _.js_, _.json_, _.css_ or _.html_. See **[custom extensions](#extensions)** for more information.
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**][slides]**_If you wonder how Webmake compares with other solutions, see [comparison section](#comparison-with-other-solutions)_**
## How does dependency resolution work?
As it has been stated, Webmake completely follows Node.js in that
Let's say in package named _foo_ you have following individual module files:
_add.js_
```javascript
module.exports = function () {
var sum = 0, i = 0, args = arguments, l = args.length;
while (i < l) sum += args[i++];
return sum;
};
```_increment.js_
```javascript
var add = require("./add");
module.exports = function (val) { return add(val, 1); };
```_program.js_
```javascript
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 bundle.js
The generated file _bundle.js_ now contains the following:
```javascript
(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_ module is executed immediately.
### Working with HTML and CSS
Technically you can construct whole website that way:
_body.html_
```html
Hello from NodeJS module
```_style.css_
```css
body {
font-family: Arial, Helvetica, sans-serif;
}
h1,
p {
margin: 20px;
}
p.footer {
font-size: 14px;
}
```_program.js_
```javascript
document.title = "Hello from NodeJS module";require("./style");
document.body.innerHTML = require("./body");
var footer = document.body.appendChild(document.createElement("p"));
footer.className = "footer";
footer.innerHTML = "Generated by Webmake!";
```Bundle it
$ webmake program.js bundle.js
See it working, by including it within document as such:
```html
```
## Installation
$ npm install -g webmake
## Usage
### From the shell:
$ webmake [options] []
**input** - Path to the initial module that should be executed when script is loaded.
**output** - (optional) Filename at which browser ready bundle should be saved. If not provided generated bundle is streamed to _stdout_.#### 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](#name-string)_ option, module will be defined with provided name.
##### cjs `string`
Expose bundle as CJS module.
##### include `string`
Additional module(s) that should be included but due specific reasons are
not picked by parser (can be set multiple times)##### ext `string`
Additional extensions(s) that should be used for modules resolution from custom formats e.g. _coffee-script_ or _yaml_.
See [extensions](#extensions) section for more info.##### sourceMap `boolean` (command line: _sourcemap_)
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][firebug issue].
##### ignoreErrors `boolean` (command line: _ignore-errors_)
Ignore not parsable require paths (e.g. `require('./lang/' + lang)`) or not polyfilled native modules requires (e.g. `require('fs')`) if any.
Dynamic paths in require calls are considered a bad practice and won't be possible with upcoming _ES6 modules_ standard. Still if we deal with modules that do that, we can workaround it by turning this option on, and including missing modules with [`include`](https://github.com/medikoo/modules-webmake/edit/master/README.md#include-string) option.##### useStrict `boolean` (command line: _use-strict_)
Enforce strict mode globally. Mind that by default in node.js environment CJS modules are not executed in strict mode. Relying on that feature may rise incompatibility issues in corner case scenarios.
##### cache `boolean` _programmatical usage only_
Cache files content and its calculated dependencies. On repeated request only modified files are re-read and parsed.
Speeds up re-generation of Webmake bundle, useful when Webmake is bound to server process, [see below example](#development-with-webmake).
Highly recommended if [extensions](#extensions) are used.
Defaults to _false_.##### transform `function` _programmatical usage only_
Provide a transform middleware. `transform` callback would be called on each module, with arguments: _absolute filename_ and _file code_ (as it is in origin file, before any internal transformations). If source module is meant to be processed by one of the extensions, you'll receive origin code before extension logic is applied, and you must return code that's valid for extension processor. So e.g. if you transform [LESS](https://github.com/acdaniel/webmake-less#webmake-less) code, you need to return valid _LESS_ code
If you're interested only in applying transform to e.g. _js_ files, be sure to filter your actions on basis of filename, and return code as you received if non transforms should be applied
In case of asynchronous operations, promise maybe returned, but it has to be promise that origins from [deferred](https://github.com/medikoo/deferred#deferred) package.
```javascript
webmake(programPath, { transform: function (filename, code) {
return transformCode(code)
} }
````transformCode` function should return either plain transformed _code_ string, or an object, with `code` and `sourceMap` properties, if we want to accompany our transform with a _sourceMap_.
### Programmatically:
```javascript
webmake(programPath[, 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.
_Example also uses [node-static][] module to serve other static files (CSS, images etc.) if you don't need it, just adjust code up to your needs._```javascript
// 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) {
// Start the flow (new Stream API demands that)
req.resume();
// 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, cache: true }, function (err, content) {
if (err) {
console.error("Webmake error: " + err.message);
// Expose eventual error brutally in browser
res.end(
"document.write('');"Could not generate " +
programUrl +
"" +
err.message.replace(/'/g, "\\'") +
"
);
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
See [webmake-middleware](https://github.com/gillesruppert/webmake-middleware) prepared by [Gilles Ruppert](http://latower.com/).
### Using Webmake with Grunt
See [grunt-webmake](https://github.com/sakatam/grunt-webmake) prepared by [Sakata Makoto](https://github.com/sakatam).
### Working with other module systems
When you work with old school scripts or framework that uses different modules system, then you'd rather just bundle needed utilities (not whole application) and expose them to global scope.
#### Webassemble -> https://github.com/kenspirit/webassemble
Webassemble written by [Ken Chen](https://github.com/kenspirit) provides a convinient way to expose different packages, written CJS style, to outer scripts. It automatically creates one entry package that does the job and is used as a starting point for a Webmake bundle.
### Extensions
#### Extensions published on NPM
##### JS
- **CoffeeScript - [webmake-coffee](https://github.com/medikoo/webmake-coffee)**
- **handlebars - [webmake-handlebars](https://github.com/acdaniel/webmake-handlebars)**
- **ejs - [webmake-ejs](https://github.com/tswaters/webmake-ejs)**##### JSON
- **YAML - [webmake-yaml](https://github.com/medikoo/webmake-yaml)**
##### CSS
- **LESS - [webmake-less](https://github.com/acdaniel/webmake-less)**
- **SASS - [webmake-sass](https://github.com/acdaniel/webmake-sass)****Submit any missing extension via [new issue form](https://github.com/medikoo/modules-webmake/issues/new)**.
#### Using extensions with Webmake
Install chosen extension:
_EXT should be replaced by name of available extension of your choice_.
$ npm install webmake-EXT
If you use global installation of Webmake, then extension also needs to be installed globally:
$ npm install -g webmake-EXT
When extension is installed, you need to ask Webmake to use it:
$ webmake --ext=EXT program.js bundle.js
Same way if used programmatically:
```javascript
webmake(inputPath, { ext: "EXT" }, cb);
```Multiple extensions can be used together:
$ webmake --ext=EXT --ext=EXT2 program.js bundle.js
Programmatically:
```javascript
webmake(inputPath, { ext: ["EXT", "EXT2"] }, cb);
```#### Writing an extension for a new format
Prepare a `webmake-*` NPM package _(replace '\*' with name of your extension)_, where main module is configured as in following example:
```javascript
// Define a file extension of a new format, can be an array e.g. ['sass', 'scss']
exports.extension = "coffee";// Which type is addressed by extension (can be either 'js', 'json', 'css' or 'html')
exports.type = "js";// Define a compile function, that for given source code, produces valid body of a JavaScript module:
exports.compile = function (source, options) {
// Return plain object, with compiled body assigned to `code` property.
return { code: compile(source) };// If compilation for some reason is asynchronous then assign promise
// (as produced by deferred library) which resolves with expected code body
return { code: compileAsync(source) };// If custom format provides a way to calculate a source map and `sourceMap` options is on
// it's nice to generate it:
var data, map, code;
if (options.sourceMap) {
data = compile(source, { sourceMap: true });// Include original file in the map.
map = JSON.parse(data.sourceMap);
map.file = options.generatedFilename;
map.sources = [options.localFilename];
map.sourcesContent = [source];
map = JSON.stringify(map);return { code: code, sourceMap: map };
}
};
```#### Providing extensions programmatically
Extension doesn't need to be installed as package, you may pass it programmatically:
```javascript
webmake(
inputPath,
{
ext: {
name: "coffee-script",
extension: "coffee",
type: "js",
compile: function (source, options) {
/* ... */
}
}
},
cb
);
```See below [writing extensions](#writing-an-extension-for-a-new-format) section to see how to configure fully working extensions
#### Writing extesions for either JSON, CSS or HTML
Be sure to state the right type, and return string that reflects addressed format (not JavaScript code)
e.g. extension for CSS:```javascript
exports.extension = "less";
exports.type = "css";
exports.compile = function (source, options) {
return { code: compileToCSS(source) }; // `compileToCSS` returns plain CSS string
};
```Publish it and refer to [Using extensions](#Using-extensions-with-webmake) section for usage instructions.
Finally if everything works, please let me know, so I can update this document with link to your extension.## Comparison with other solutions
### AMD
AMD is different format, and although most popular loader for AMD is named [RequireJS](http://requirejs.org/) it works very differently from _require_ as introduced earlier with CommonJS (one that Webmake handles).
Main idea behind AMD is that dependencies are resolved asynchronously (in contrary to synchronous resolution in case of CommonJS format). Sounds promising, but does it really make things better? Cause of waterfall nature of resolution and large number of HTTP requests not necessary. See [benchmark](https://github.com/medikoo/cjs-vs-amd-benchmark#compare-load-times-of-two-module-systems) that compares resolution speed of both formats when used in development mode.
Agreed advantage of AMD that attributes to its success is that in it's direct form works in a browser (it doesn't require any server setup), that is hard to achieve with CJS style (but [not impossible](https://github.com/creationix/chrome-app-module-loader)). Still due to large number of requests such approach is usually not suitable for production and it appears it's also [not that performant in development mode](https://github.com/medikoo/cjs-vs-amd-benchmark#compare-load-times-of-two-module-systems).
Quirks of AMD style is that it requires you to wrap all your modules with function wrappers, its modules are not runnable in direct form in Node.js and dependency resolution rules are basic and limited if you compare it with design of node.js + npm ecosystem.
### Browserify and other CJS bundlers
[Browserify](http://browserify.org/) is most popular CJS bundler, and shares very similar idea. The subtle difference is that Browserify is about porting code as written for node.js to web browser, so apart of resolving dependencies and bundling the code it struggles to bring what is needed and possible from Node.js API to the browser.
Webmake cares only about bringing node.js modules format to other environments. Conceptually it's addition to ECMAScript and not port of node.js to browser. It makes node.js modules format runnable in any environment that speaks at least ECMAScript 3. You can bundle with Webmake for Browser, TV, Adobe Photoshop or maybe a modern dishwasher.
When comparing with other CJS bundlers, main difference would be that Webmake completely follows resolution logic as it works in node.js. It resolves both packages and modules exactly as node.js, and it doesn't introduce any different ways to do that. Thanks to that, you can be sure that your modules are runnable in it's direct form both on server and client-side.
Other important difference is that Webmake doesn't do full AST scan to parse require's out of modules, it relies on [find-requires](https://github.com/medikoo/find-requires#find-requires--find-all-require-calls) module, which does only what's necessary to resolve dependencies list, and that makes it a noticeably faster solution.
### ES6 modules
Soon to be released, native JavaScript modules spec shares the main concept with CommmonJS. Thanks to that eventual transition will be easy and can be fully automated. First [transpilers](http://square.github.io/es6-module-transpiler/) are already here.
As soon as the standard will be finalized, implemented in first engines and possibly adapted by node.js Webmake will support it natively as well, then in a same way it will bundle it either for the sake of a bundle or for any ECMAScript 3+ environment that won't take it in natural way.
## Current limitations of Webmake
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](#include-stringarray)Only relative paths and outer packages paths are supported, following will work:
```javascript
require("./module-in-same-folder");
require("./module/path/deeper");
require("./some/very/very/very/long" + "/module/path");
require("../../module-path-up"); // unless it doesn't go out of package scope
require("other-package");
require("other-package/lib/some-module");
```But this won't:
```javascript
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
$ npm test
## Proud list of SPONSORS!
#### [@puzrin](https://github.com/puzrin) (Vitaly Puzrin) member of [Nodeca](https://github.com/nodeca)
Vitaly pushed forward development of support for _JSON_ files, [extensions functionality](#extensions), along with [webmake-yaml](https://github.com/medikoo/webmake-yaml) extension. Vitaly is a member of a team that is behind [js-yaml](https://github.com/nodeca/js-yaml) JavaScript YAML parser and dumper, and powerful social platform [Nodeca](http://dev.nodeca.com/). Big Thank You Vitaly!
## Contributors
- [@Phoscur](https://github.com/Phoscur) (Justus Maier)
- Help with source map feature
- [@jaap3](https://github.com/jaap3) (Jaap Roes)
- Documentation quality improvements[slides]: http://www.slideshare.net/medikoo/javascript-modules-done-right "JavaScript Modules Done Right on SlideShare"
[source maps]: http://pmuellr.blogspot.com/2011/11/debugging-concatenated-javascript-files.html "Debugging concatenated JavaScript files"[firebug issue]:
http://code.google.com/p/fbug/issues/detail?id=2198
'Issue 2198: @sourceURL doesn't work in eval() in some cases'[find-requires]: https://github.com/medikoo/find-requires "find-requires: Find all require() calls"
[node-static]: https://github.com/cloudhead/node-static "HTTP static-file server module"
[nix-build-image]: https://semaphoreci.com/api/v1/medikoo-org/modules-webmake/branches/master/shields_badge.svg
[nix-build-url]: https://semaphoreci.com/medikoo-org/modules-webmake
[win-build-image]: https://ci.appveyor.com/api/projects/status/16c6u5cafarb3yo0?svg=true
[win-build-url]: https://ci.appveyor.com/project/medikoo/modules-webmake
[cov-image]: https://img.shields.io/codecov/c/github/medikoo/modules-webmake.svg
[cov-url]: https://codecov.io/gh/medikoo/modules-webmake
[transpilation-image]: https://img.shields.io/badge/transpilation-free-brightgreen.svg
[npm-image]: https://img.shields.io/npm/v/webmake.svg
[npm-url]: https://www.npmjs.com/package/webmake