<commit_before># JavaScript Module Experiments

One of the many sucky things about JavaScript is its lack of support for modules. As a result, a number of people have built different systems which are all sort of incompatible. While trying to figure out a good JavaScript development setup, I found it helpful to implement a simple module in a few different ways. Comments welcomed.

## Building/running tests

1. Install NPM dependencies: `npm install` 
2. Compile with Closure, run all tests: `make`
3. Check out the examples. E.g. `open simple_both_closure/example_compiled.html`


## Ideal Goals

* Run unit tests in a browser.
* Run unit tests in a browser from the command line (PhantomJS).
* Import modules in Node.
* Run unit tests from the command line using Node.
* Type checking using the Closure compiler.
* Development flow: save the file, click reload, see your changes.


Sadly, since JavaScript modules are mostly incompatible, to do this we use Google Closure-style modules, and use some hacky scripts to transform them to other forms. I chose to use Google Closure modules since they work with the Closure Compiler type checks, and have a minimum of boilerplate, making them easy to write.

To make this work, unfortunately I need to transform the Javascript files from one form to another.


## Common types of JavaScript modules

### Basic browser module
```javascript
var namespace = namespace || {};
namespace.module = namespace.module || {};
(function(){

var someLocal = function() {
  return 5;
};

namespace.module.myFunction = function() {
  return namespace.relative.someFunction() + someLocal();
};

})();
```

Example: [`basic_browser`](../../tree/master/basic_browser)

* ✓ `someLocal` is hidden in the anonymous closure.
* ✓ runs directly in a browser.
* ✓ can be type checked.
* ✗ dependencies must be "magically" loaded by some other system.
* ✗ requires a bunch of boilerplate for deeply nested namespaces.


### Node modules
```javascript
var global = require('global');
var relative = require('./relative');

var someLocal = function() {
  return 5;
};

exports.myFunction = function() {
  return global.someFunction() + relative.someFunction() + someLocal();
};
```

Example: [`node`](../../tree/master/node)

* ✓ someLocal is purely local
* ✓ npm fetches dependencies, and browserify can run it in a browser.
* ✓ relatively free of boilerplate
* ✗ can't be type checked: Closure doesn't understand require.


### Google Closure modules
```javascript
goog.provide('namespace.module');

goog.require('global');
goog.require('namespace.relative');

var someLocal = function() {
  return 5;
};

namespace.module.myFunction = function() {
  return global.someFunction() + namespace.relative.someFunction() + someLocal();
};
```

Example: [`closure`](closure)

* ✓ relatively free of boilerplate.
* ✓ there is a way to determine which files are needed.
* ✓ can be type checked.
* ✗ `someLocal` is global, although you can put everything in a closure to hide it (or use `goog.scope`). However, this may not be needed, since the compiler will tell you if you clobber another variable.
* ✗ doesn't work directly in node.
* ✗ requires Closure library (although see [`closure/fakebase.js`](closure/fakebase.js))
* ✗ not widely used.


### NodeJS module that is Closure Compiler friendly
```javascript
/** @suppress{duplicate} */
var namespace = namespace || {};
namespace.module = namespace.module || {};
(function() {
var global = global || require('global');
namespace.relative = namespace.relative || require('./relative');

var someLocal = function() {
  return 5;
};

namespace.module.myFunction = function() {
  return global.someFunction() + namespace.relative.someFunction() + someLocal();
};

if (typeof exports !== 'undefined') {
  for (var member in namespace.relative) {
    if (namespace.relative.hasOwnProperty(member)) {
      exports[member] = namespace.relative[member];
    }
  }
}
})();

```

To make this work: 

* Use `var namespace = namespace || new Object();` to avoid Closure duplicate namespace warnings [1](http://blog.teamleadnet.com/2013/04/compiling-javascript-code-to-detect.html) [2](https://code.google.com/p/closure-compiler/issues/detail?id=970)
* Use `@suppress{duplicate}` on the namespace to suppress duplicate warnings.
* Import modules using `require` if they don't exist (```var module = module || require('module');```).
* Always use "fully qualified" names (`namespace.module.symbol`).
* Detect the `exports` object using typeof, and export all properties.

* ✓ can be type checked.
* ✓ can be used with Node.
* ✓ can be used in the browser, with or without compilation.
* ✗ no clean way to determine which files are required.
* ✗ some ugly boilerplate is required.


# Other Projects using Closure Compiler:
<commit_msg>README.md: Simplify and explain the current setup.<commit_after># JavaScript Module Experiments

This repository contains some simple examples I used to figure out how to get an "ideal" JavaScript module setup that works for us. We wanted:

* Code to work in both NodeJS and the browser without modification.
* Unit tests to run in both, from the command line (we use jasmine, jasmine-node, and Karma).
* Type-checked with the Closure compiler.

This repository shows how to do this.


## Building/running tests

1. Install NPM dependencies: `npm install` 
2. Compile with Closure, run all tests: `make`
3. Check out the examples. E.g. `open simple_both_closure/example_compiled.html`


## Brief contents

* `browser`: Example web browser module.
* `node`: Same example for NodeJS.
* `node_and_browser`: Module that works with both.
* `node_and_browser_closure`: Module that works with both and can by type-checked using Google's Closure compiler.


# Other Projects using Closure Compiler:

{'fraction_non_alphanumeric': 0.10807539339443195, 'fraction_numerical': 0.003631333218052914, 'mean_word_length': 4.4772727272727275, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5052469', 'n_tokens_mistral': 1658, 'n_tokens_neox': 1545, 'n_words': 695}