#AMDclean
A build tool that converts AMD code to standard JavaScript.
npm install amdclean --save-dev
Getting Started Video
Use Case
Single file client-side JavaScript libraries or web apps that want to use AMD and/or CommonJS modules to structure and build their code, but don't want any additional footprint.
Used By
Why
Many developers like to use the AMD and/or CommonJS (CJS) module APIs to write modular JavaScript, but do not want to include a full AMD or CJS loader (e.g. require.js), or shim (e.g. almond.js, browserify) because of file size/source code readability concerns.
By incorporating AMDclean.js into the build process, you no longer need to include Require.js or Almond.js in production, or use Browserify.
Since AMDclean rewrites your source code into standard JavaScript, it is a great
fit for JavaScript library/web app authors who want a tiny download in one file after using the
RequireJS Optimizer. AMDclean uses multiple different optimization algorithms to create the smallest file possible, while still making your code readable.
Restrictions
Note: Same restrictions as almond.js.
It is best used for libraries or apps that use AMD or CommonJS (using the cjsTranslate Require.js optimizer option) and optimize all modules into one file or multiple bundles. If you do not include Require.js or a similar loader, you cannot dynamically load code.
What is Supported
{
'transformAMDChecks': false
}
- full-fledged CommonJS files using the cjsTranslate Require.js option.
- Exporting global modules to the global
window
object
Download
Node - npm install amdclean --save-dev
Web - Latest release
Usage
There are a few different ways that AMDclean can be used including:
-
With the RequireJS Optimizer (plain node, Grunt, Gulp, etc)
-
As a standalone node module
-
As a client-side library
Note: AMDclean does not have any module ordering logic, so if you do not use the RequireJS optimizer then you need to find another solution for resolving module dependencies before your files can be "cleaned".
AMDclean with the RequireJS Optimizer
-
Download the RequireJS optimizer.
-
npm install amdclean --save-dev
-
Add a onModuleBundleComplete
config property to your RequireJS build configuration file instead. Like this:
onModuleBundleComplete: function (data) {
var fs = module.require('fs'),
amdclean = module.require('amdclean'),
outputFile = data.path,
cleanedCode = amdclean.clean({
'filePath': outputFile
});
fs.writeFileSync(outputFile, cleanedCode);
}
-
Run the optimizer using Node (also works in Java). More details can be found in the the r.js repo.
-
If you are using the RequireJS optimizer Grunt task, then it is very easy to integrate AMDclean using the onModuleBundleComplete
config option. Here is an example Grunt file that includes the RequireJS optimizer plugin with AMDclean support:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
requirejs: {
js: {
options: {
'findNestedDependencies': true,
'baseUrl': 'src/js/app/modules',
'optimize': 'none',
'mainConfigFile': 'src/js/app/config/config.js',
'include': ['first'],
'out': 'src/js/app/exampleLib.js',
'onModuleBundleComplete': function (data) {
var fs = require('fs'),
amdclean = require('amdclean'),
outputFile = data.path;
fs.writeFileSync(outputFile, amdclean.clean({
'filePath': outputFile
}));
}
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-requirejs');
grunt.registerTask('build', ['requirejs:js']);
grunt.registerTask('default', ['build']);
};
- If you are using the RequireJS node module with Gulp, then it is very easy to integrate AMDclean using the
onModuleBundleComplete
config option. Here is an example Gulp task that includes the RequireJS optimizer node module with AMDclean support:
gulp.task('build', function() {
var requirejs = require('requirejs');
requirejs.optimize({
'findNestedDependencies': true,
'baseUrl': './src/',
'optimize': 'none',
'include': ['first'],
'out': './build/example.js',
'onModuleBundleComplete': function(data) {
var fs = require('fs'),
amdclean = require('amdclean'),
outputFile = data.path;
fs.writeFileSync(outputFile, amdclean.clean({
'filePath': outputFile
}));
}
});
});
AMDclean as a Node Module
var amdclean = require('amdclean');
var code = 'define("exampleModule", function() {});'
var cleanedCode = amdclean.clean(code);
AMDclean as a Client-side Library
<script src="http://esprima.org/esprima.js"></script>
<script src="http://constellation.github.io/escodegen/escodegen.browser.js"></script>
<script src="https://rawgithub.com/Constellation/estraverse/master/estraverse.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.2.1/lodash.js"></script>
<script src="https://rawgithub.com/gfranko/amdclean/master/src/amdclean.js"></script>
- Use the global
amdclean
object and clean()
method
var cleanedCode = amdclean.clean('define("example", [], function() { var a = true; });');
Requirements
Optional Dependencies
How it works
AMDclean uses Esprima to generate an AST (Abstract Syntax Tree) from the provided source code, estraverse to traverse and update the AST, and escodegen to generate the new standard JavaScript code.
Note: If you are interested in how this works, watch this presentation about building Static Code Analysis Tools.
Here are a few different techniques that AMDclean uses to convert AMD to standard JavaScript code:
Define Calls
AMD
define('example', [], function() {
});
Standard
var example;
example = undefined;
AMD
define('example', [], function() {
var test = true;
});
Standard
var example;
example = function () {
var test = true;
}();
AMD
define('example', [], function() {
return function(name) {
return 'Hello ' + name;
};
});
Standard
var example;
example = function (name) {
return 'Hello ' + name;
};
AMD
define('example', [], function() {
return 'I love AMDclean';
});
Standard
var example;
example = 'I love AMDclean';
AMD
define('example', ['example1', 'example2'], function(one, two) {
var test = true;
});
Standard
var example;
example = function (one, two) {
var test = true;
}(example1, example2);
AMD
define("backbone", ["underscore","jquery"], (function (global) {
return function () {
var ret, fn;
return ret || global.Backbone;
};
}(this)));
Standard
var backbone;
backbone = window.Backbone;
AMD
define('third',{
exampleProp: 'This is an example'
});
Standard
var third;
third = {
exampleProp: 'This is an example'
};
Require Calls
Note: require(['someModule'])
calls, with no callback function, are removed from the built source code
AMD
require([], function() {
var example = true;
});
Standard
(function () {
var example = true;
}());
AMD
require(['anotherModule'], function(someModule) {
var example = true;
});
Standard
(function (someModule) {
var example = true;
}(anotherModule));
Optimization Algorithms
AMDclean uses a few different strategies to decrease file size:
Remove Unused Dependencies/Parameters
AMD
define('example', ['example1', 'example2'], function() {
var test = true;
});
Standard
var example;
example = function() {
var test = true;
}();
Remove Exact Matching Dependencies/Parameters
AMD
define('example', ['example1', 'example2'], function(example1, anotherExample) {
var test = true;
});
Standard
var example;
example = function(anotherExample) {
var test = true;
}(example2);
Hoist Common Non-Matching Dependencies
- Note - For this behavior, you must set the
aggressiveOptimizations
option to true
AMD
define('example', ['example1'], function(firstExample) {
var test = true;
});
define('anotherExample', ['example1'], function(firstExample) {
var test = true;
});
Standard
var example, firstExample;
firstExample = example1;
example = function() {
var test = true;
};
anotherExample = function() {
var test = true;
};
Options
The amdclean clean()
method accepts a string or an object. Below is an example object with all of the available configuration options:
amdclean.clean({
'code': '',
'sourceMap': null,
'aggressiveOptimizations': false,
'filePath': '',
'globalModules': [],
'esprima': {
'comment': true,
'loc': true,
'range': true,
'tokens': true
},
'escodegen': {
'comment': true,
'format': {
'indent': {
'style': ' ',
'adjustMultilineComment': true
}
}
},
'commentCleanName': 'amdclean',
'ignoreModules': [],
'removeModules': [],
'removeAllRequires': false,
'removeUseStricts': true,
'transformAMDChecks': true,
'createAnonymousAMDModule': false,
'shimOverrides': {},
'prefixMode': 'standard',
'prefixTransform': function(postNormalizedModuleName, preNormalizedModuleName) { return postNormalizedModuleName; },
'wrap': {
'start': ';(function() {\n',
'end': '\n}());'
},
'config': {},
'IIFEVariableNameTransform': function(moduleName, moduleId){return 'GlobalModules[\'' + moduleId + '\'] = ' + moduleName; }
})
Unit Tests
All unit tests are written using the jasmine-node library and can be found in the test/specs/
folder. You can run the unit tests by typing: npm test
or gulp test
.
Contributing
Please send all PR's to the dev
branch.
If your PR is a code change:
- Update the appropriate module inside of the
src/modules
directory. - Add a Jasmine unit test to
convert.js
inside of the test/specs
folder - Install all node.js dev dependencies:
npm install
- Install gulp.js globally:
sudo npm install gulp -g
- Lint, Minify, and Run all unit tests with Gulp:
gulp
- Verify that the minified output file has been updated in
build/amdclean.min.js
- Send the PR!
Note: There is a gulp watch
task that will automatically lint, minify, unit test, and build AMDclean whenever a module inside of the src/modules
directory is changed. I recommend using it.
FAQ
After I build with AMDclean, I am getting JavaScript errors. What gives?
Why should I use AMDclean instead of Browserify?
- This is a loaded question. Here is a short list of pros/cons when using each library:
Browserify Pros
- Uses the node.js style
node_modules
file lookup algorithm, which allows you to npm install
an npm module and automatically use it
Browserify Cons
- Requires a development build step
- Does not support AMD modules out of the box
- Does not support dynamic module loading out of the box
- Adds boilerplate code to files (increasing file size and decreasing code readability)
AMDclean Pros
- Does not require a build step in development when used with Require.js
- Supports both AMD and CommonJS modules when used with the Require.js optimizer
- Does not add boilerplate code to files and uses advanced file optimizations to decrease file size and increase code readability
AMDclean Cons
- Does not use the node.js style
node_modules
file lookup algorithm, which means that you can not automatically use npm
to install modules without having to set up configuration first
Why should I use AMDclean instead of Almond.js?
- Although Almond is very small (~1k gzipped and minified), most JavaScript library authors do not want to have to include it in their library's source code. AMDclean allows you to use AMD without increasing your library's file size. AMDclean also implements multiple different optimization algorithms to make your source code even smaller.
Do I have to use the onModuleBundleComplete Require.js hook?
- Yes, you should be using it. In
< 2.0
versions of AMDclean, the onBuildWrite
Require.js hook was used instead, but the onBuildWrite
hook has been deprecated. Use the onModuleBundleComplete
Require.js hook like this:
onModuleBundleComplete: function (data) {
var fs = require('fs'),
amdclean = require('amdclean'),
outputFile = data.path;
fs.writeFileSync(outputFile, amdclean.clean({
'filePath': outputFile,
'globalObject': true
}));
}
Does AMDclean use AMDclean to build itself?
Is AMDclean only for libraries, or can I use it for my web app?
-
You can use it for both! The 0.6.0 release provided support for web apps.
-
By default, AMDclean is set up for use within a web app. If you are developing a JavaScript library with AMDclean, here are the things you should be aware of:
-
Make sure to set the transformAMDChecks
option to false
if you don't want your conditional UMD (Universal Module Definition) pattern affected.
-
If your JavaScript library depends on one or external libraries (libraries that will not be included in your library's source code), then you need to do a little hackery and make sure to hoist the local variables, that will hold the external library values, using the AMDclean wrap
option. For more details, take a look at how AMDclean itself handles this situation, or create a Github issue
My comments seem to be getting removed when I use AMDclean. What am I doing wrong?
- Before the
2.1.0
release, this was the default behavior. If you update to 2.1.0
or later, you should see your comments still there after the cleaning process. Also, if you would like your comments to be removed, then you can set the comment
escodegen option to false
.
What if I don't want all define() and require() method calls to be removed?
- If you don't want one or more define() and require() methods to be removed by AMDclean, you have a few options. If the module has a named module id associated with it, then you can add the associated module id to the
ignoreModules
option array. Like this:
var amdclean = require('amdclean');
amdclean.clean({
'code': 'define("randomExample", function() { console.log("I am a random example"); });',
'ignoreModules': ['randomExample']
});
If there is not an associated module id, then you must put a comment with only the words amdclean on the same line or one line above the method in question. For example, amdclean
would not remove the define()
method below:
define('example', [], function() {});
If you want to use different text than amdclean
, you can customize the comment name by using the commentCleanName
option.
Why are define() method placeholder functions inserted into my source?
- This is the default behavior of r.js when a module(s) is not wrapped in a define() method. Luckily, this behavior can be overridden by setting the
skipModuleInsertion
option to true
in your build configuration.
How would I expose one or more modules as a global window property?
- You can use the
globalModules
option to list all of the modules that you would like to expose as a window
property
I replaced Almond.js with AMDclean and my file is bigger. Why Is This?
I am building a JavaScript library and want to provide conditional AMD support, but AMDclean seems to be wiping away my if statement. How do I fix this?
-
You have two options:
-
Set the transformAMDChecks
option to false
-
Make sure that you have a comment (that matches your AMDclean commentCleanName
option) one line above your conditional AMD if statement
I am building a JavaScript library and want to create a conditional anonymous AMD module, but Require.js and AMDclean seems to always setting a module ID. How do I fix this?
- It's easy, just make sure to set the
createAnonymousAMDModule
option to true
,
I don't like the way AMDclean normalizes the names of my modules with underscores. Can I change this?
- You sure can. You can either use the
prefixMode
and change it to camelCase, or you can override all of the logic with your own logic by using the prefixTransform
option hook.
Require.js supports passing module information, to one or more modules, with the config
option. Does AMDclean support this?
I can't seem to get AMDclean 2.0 to work. What gives?
- Please make sure you are using the
onModuleBundleComplete
Require.js hook and NOT the onBuildWrite
Require.js hook. The onBuildWrite
hook has been deprecated for AMDclean versions >= 2.0
.
I'd like to use source map support. What to do?
- You can use the following minimum viable configuration to add source map support:
var amdclean = require('amdclean'),
cleaned = amdclean.clean({
'sourceMap: '{...}', // this is the source map that you already have for the code below
'code': 'define("randomExample", function() { console.log("I am a random example"); });',
'wrap': false, // do not use wrap together with escodegen.sourceMapWithCode since it breaks the logic
'esprima': {
'source': 'myfile.js' // name of your file to appear in sourcemap
},
'escodegen': {
'sourceMap': true,
'sourceMapWithCode': true
}
});
Attention! Result in variable cleaned
is an object {code: ..., map: ...}
where code
is your cleaned code and map
is a source map. Read Escodegen Wiki for more info.
License
Copyright (c) 2014 Greg Franko Licensed under the MIT license.