first commit

This commit is contained in:
Sebastian McKenzie 2014-09-28 23:39:22 +10:00
commit c97696c224
167 changed files with 2007 additions and 0 deletions

11
.editorconfig Normal file
View File

@ -0,0 +1,11 @@
root = true
[*]
insert_final_newline = true
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.js]
indent_style = space
indent_size = 2

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.DS_Store
node_modules
*.log
*.cache
/templates.json
coverage

2
.jshintignore Normal file
View File

@ -0,0 +1,2 @@
node_modules
test

22
.jshintrc Normal file
View File

@ -0,0 +1,22 @@
{
"esnext": true,
"indent": 2,
"maxlen": 80,
"freeze": true,
"camelcase": true,
"unused": true,
"eqnull": true,
"proto": true,
"newcap": true,
"supernew": true,
"noyield": true,
"evil": true,
"node": true,
"boss": true,
"expr": true,
"undef": true,
"loopfunc": true,
"white": true,
"maxparams": 5,
"maxdepth": 4
}

5
.npmignore Normal file
View File

@ -0,0 +1,5 @@
.DS_Store
node_modules
*.log
*.cache
lib/6to5/templates

5
.travis.yml Normal file
View File

@ -0,0 +1,5 @@
language: node_js
node_js:
- 0.8
- 0.10
- 0.11

22
LICENSE Normal file
View File

@ -0,0 +1,22 @@
Copyright (c) 2014 Sebastian McKenzie
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

21
Makefile Normal file
View File

@ -0,0 +1,21 @@
MOCHA_CMD = node_modules/mocha/bin/_mocha
export NODE_ENV = test
.PHONY: clean test test-cov test-travis publish
clean:
rm -rf coverage templates.json
test:
$(MOCHA_CMD)
test-cov:
make clean
node node_modules/istanbul/lib/cli.js cover $(MOCHA_CMD) --
publish:
make clean
node bin/cache-templates
npm publish
make clean

134
README.md Normal file
View File

@ -0,0 +1,134 @@
# 6to6
**6to5** turns ES6 code into vanilla ES5, so you can use ES6 features **today.**
6to5 is:
- Fast - [10x faster than Traceur](#comparison-to-traceur).
- Compact - maps directly to the equivalent ES5.
- Easy - with Browserify support, Node API, Connect Middleware and a CLI.
- Concise - we do not pollute any scope with unneccesary variables or functions declarations.
## Features
| Name | Implemented |
| -------------------------- | ----------- |
| Arrow functions | ✓ |
| Classes | ✓ |
| Default parameters | ✓ |
| Spread | ✓ |
| Block binding | ✓ |
| Property method assignment | ✓ |
| Rest parameters | ✓ |
| Template literals | ✓ |
| Modules | ✓ |
| Destructuring assignment | |
| Generators | |
## Installation
$ npm install -g 6to5
## Usage
### CLI
Compile the file `script.js` and output it to `script-compiled.js`.
$ 6to5 script.js -o script-compiled.js
Compile the entire `src` directory and output it to the `lib` directory.
$ 6to5 src -d lib
Compile the file `script.js` and output it to stdout.
$ 6to5 script.js
### Node
```javascript
var to5 = require("6to5");
to5.transform("code();");
to5.transformFileSync("script.js");
to5.transformFile("script.js", function (err, data) {
});
```
##### Options
```javascript
to5.transform("code();", {
// List of transformers to EXCLUDE
// See `features` for valid names.
blacklist: [],
// List of transformers to ONLY use.
// See `features` for valid names.
whitelist: [],
// Append source map and comment to bottom of returned output.
sourceMap: false,
// Filename for use in errors etc.
filename: "unknown",
// Format options
// See https://github.com/Constellation/escodegen/wiki/API for options.
format: {}
});
```
#### Require hook
All subsequent files required by node will be transformed into ES5 compatible
code.
```javascript
require("6to5/register");
```
#### Connect Middleware
```javascript
var to5 = require("6to5");
app.use(6to5.middleware({
transform: {
// options to use when transforming files
},
src: "assets",
dest: "cache"
}));
app.use(connect.static("cache"));
```
### Browserify
#### CLI
$ browserify script.js -t 6to5/browserify --outfile bundle.js
#### Node
```javascript
var to5 = require("6to5");
browserify()
.transform(to5.browserify)
.require("script.js", { entry: true })
.bundle({ debug: true })
.pipe(fs.createWriteStream("bundle.js"));
```
## Caveats
### Generators
### Let
## Comparison to Traceur

134
bin/6to5 Executable file
View File

@ -0,0 +1,134 @@
#!/usr/bin/env node
var commander = require("commander");
var chokidar = require("chokidar");
var readdir = require("fs-readdir-recursive");
var mkdirp = require("mkdirp");
var 6to5 = require("../lib/6to5/node");
var path = require("path");
var util = require("../lib/6to5/util");
var fs = require("fs");
var _ = require("lodash");
commander.option("-m, --source-maps", "Generate source maps alongside the compiled JavaScript files. Adds sourceMappingURL directives to the JavaScript as well.");
//commander.option("-w, --watch", "Watch, only works with --out-dir");
var list = function (val) {
return val ? val.split(",") : [];
};
commander.option("-w, --whitelist [whitelist]", "Whitelist", list);
commander.option("-b, --blacklist [blacklist]", "Blacklist", list);
commander.option("-o, --out-file [out]", "Compile all input files into a single file");
commander.option("-d, --out-dir [out]", "Compile an input directory of modules into an output directory");
var pkg = require("../package.json");
commander.version(pkg.version);
commander.usage("[options] <files ...>");
commander.parse(process.argv);
//
var errors = [];
var filenames = commander.args;
if (!filenames.length) errors.push("no filenames input");
_.each(filenames, function (filename) {
if (!fs.existsSync(filename)) {
errors.push(filename + " doesn't exist");
}
});
if (commander.outFile && commander.outDir) {
errors.push("cannot have --out-file and --out-dir");
}
if (errors.length) {
console.error(errors.join(". "));
process.exit(2);
}
//
var readdirFilter = function (filename) {
return readdir(filename).filter(util.canCompile);
};
var mainOpts = {
blacklist: commander.blacklist,
whitelist: commander.whitelist,
sourceMap: commander.sourceMaps,
tolerant: commander.tolerant
};
var data = [];
var compile = function (filename) {
var opts = _.clone(mainOpts);
opts.filename = filename;
var file = fs.readFileSync(filename);
return 6to5.transform(file, opts);
};
if (commander.outDir) {
var write = function (src, relative) {
var data = compile(src);
var dest = path.join(commander.outDir, relative);
var up = path.normalize(dest + "/..");
mkdirp.sync(up);
fs.writeFileSync(dest, data);
console.log(src + " -> " + dest);
};
var handle = function (filename) {
var stat = fs.statSync(filename);
if (stat.isDirectory(filename)) {
var dirname = filename;
_.each(readdirFilter(dirname), function (filename) {
write(path.join(dirname, filename), filename);
});
} else {
write(filename, filename);
}
};
_.each(filenames, handle);
} else {
var data = [];
var _filenames = filenames;
filenames = [];
_.each(_filenames, function (filename) {
var stat = fs.statSync(filename);
if (stat.isDirectory()) {
var dirname = filename;
_.each(readdirFilter(filename), function (filename) {
filenames.push(path.join(dirname, filename));
});
} else {
filenames.push(filename);
}
});
_.each(filenames, function (filename) {
data.push(compile(filename) + "\n");
});
data = data.join("");
if (commander.outFile) {
fs.writeFileSync(commander.out, data);
} else {
console.log(data);
}
}

5
bin/cache-templates Executable file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env node
var fs = require("fs");
var templates = require("../lib/6to5/util").templates;
fs.writeFileSync("templates.json", JSON.stringify(templates));

1
browserify.js Normal file
View File

@ -0,0 +1 @@
module.exports = require("./lib/6to5/browserify");

19
lib/6to5/browserify.js Normal file
View File

@ -0,0 +1,19 @@
var transform = require("./transform");
var through = require("through");
module.exports = function (filename) {
var data = "";
var write = function (buf) {
data += buf;
};
var end = function () {
var out = transform(data, { filename: filename });
stream.queue(out);
stream.queue(null);
};
var stream = through(write, end);
return stream;
};

66
lib/6to5/middleware.js Normal file
View File

@ -0,0 +1,66 @@
var util = require("./util");
var path = require("path");
var api = require("./node");
var fs = require("fs");
var _ = require("lodash");
module.exports = function (opts) {
opts = _.defaults(opts || {}, {
src: "assets",
dest: "cache"
});
var cache = {};
return function (req, res, next) {
var url = req.url;
if (!util.canCompile(url)) return next();
var dest = path.join(opts.dest, url);
var src = path.join(opts.src, url);
var write = function (transformed) {
fs.writeFile(dest, transformed, function (err) {
if (err) {
next(err);
} else {
cache[url] = Date.now();
next();
}
});
};
var compile = function () {
var transformOpts = _.clone(opts.transform || {});
api.transformFile(opts.dest, transformOpts, function (err, transformed) {
if (err) return next(err);
write(transformed);
});
};
var destExists = function () {
fs.stat(dest, function (err, stat) {
if (err) return next(err);
if (cache[url] < +stat.mtime) {
compile();
} else {
next();
}
});
};
fs.exists(src, function (exists) {
if (!exists) return next();
fs.exists(dest, function (exists) {
if (exists && cache[dest]) {
destExists();
} else {
compile();
}
});
});
};
};

45
lib/6to5/node.js Normal file
View File

@ -0,0 +1,45 @@
var transform = require("./transform");
var fs = require("fs");
var _ = require("lodash");
exports.browserify = require("./browserify");
exports.middleware = require("./middleware");
exports.register = function () {
require.extensions[".js"] = function (m, filename) {
m._compile(exports.transformFileSync(filename, {
sourceMap: true
}), filename);
};
};
exports.transform = transform;
exports.transformFile = function (filename, opts, callback) {
if (_.isFunction(opts)) {
callback = opts;
opts = {};
}
opts.filename = filename;
fs.readFile(filename, function (err, raw) {
if (err) return callback(err);
var code;
try {
code = transform(raw, opts);
} catch (err) {
return callback(err);
}
callback(null, code);
});
};
exports.transformFileSync = function (filename, opts) {
opts = opts || {};
opts.filename = filename;
return transform(fs.readFileSync(filename), opts);
};

View File

@ -0,0 +1 @@
var VARIABLE_NAME = Array.prototype.slice.call(arguments, SLICE_ARG);

View File

@ -0,0 +1 @@
var VARIABLE_NAME = Array.prototype.slice.call(arguments);

View File

@ -0,0 +1 @@
Array.prototype.slice.call(arguments);

View File

@ -0,0 +1 @@
[].concat(ARGUMENT);

View File

@ -0,0 +1 @@
CLASS_NAME.prototype.METHOD_NAME = FUNCTION;

View File

@ -0,0 +1,7 @@
var CLASS_NAME = (function () {
function CLASS_NAME() {
}
return CLASS_NAME;
})();

View File

@ -0,0 +1,5 @@
Object.defineProperty(exports, STRING_KEY, {
get: function () {
return KEY;
}
});

View File

@ -0,0 +1 @@
exports.KEY = VALUE;

View File

@ -0,0 +1 @@
exports = module.exports = VALUE;

View File

@ -0,0 +1 @@
exports.VARIABLE_NAME = require(MODULE_NAME).KEY;

View File

@ -0,0 +1,5 @@
(function (obj) {
for (var i in obj) {
exports[i] = obj[i];
}
}(require(MODULE_NAME)));

View File

@ -0,0 +1 @@
FUNCTION.bind(this);

View File

@ -0,0 +1 @@
FUNCTION.call(this);

View File

@ -0,0 +1 @@
FUNCTION();

View File

@ -0,0 +1 @@
if (VARIABLE === undefined) VARIABLE = DEFAULT;

View File

@ -0,0 +1,8 @@
CLASS_NAME.prototype = Object.create(SUPER_NAME.prototype, {
constructor: {
value: CLASS_NAME,
enumerable: false,
writable: true,
configurable: true
}
});

View File

@ -0,0 +1,4 @@
(function (obj) {
CONTENT;
return obj;
})(OBJECT);

View File

@ -0,0 +1 @@
Object.defineProperties(OBJECT, PROPS);

View File

@ -0,0 +1 @@
CLASS_NAME.prototype

View File

@ -0,0 +1 @@
var VARIABLE_NAME = require(MODULE_NAME).KEY;

View File

@ -0,0 +1 @@
var VARIABLE_NAME = require(MODULE_NAME);

107
lib/6to5/transform.js Normal file
View File

@ -0,0 +1,107 @@
var escodegen = require("escodegen");
var traverse = require("./traverse");
var assert = require("assert");
var util = require("./util");
var _ = require("lodash");
var transform = module.exports = function (code, opts) {
opts = opts || {};
_.defaults(opts, {
blacklist: [],
whitelist: [],
sourceMap: false,
filename: "unknown",
format: {}
});
try {
var tree = util.parse(code);
} catch (err) {
err.message = opts.filename + ": " + err.message;
throw err;
}
traverse.replace(tree, function (node) {
if (node.type === "EmptyStatement") {
return traverse.Delete;
}
});
_.each(transform.transformers, function (transformer, name) {
var blacklist = opts.blacklist;
if (blacklist.length && _.contains(blacklist, name)) return;
var whitelist = opts.whitelist;
if (whitelist.length && !_.contains(whitelist, name)) return;
transform._runTransformer(transformer, tree, opts);
});
var genOpts = {
comment: true,
format: _.merge(opts.format, {
indent: {
style: " "
}
})
};
if (opts.sourceMap) {
genOpts.sourceMap = true;
genOpts.sourceContent = code;
genOpts.sourceMapWithCode = true;
}
var result = escodegen.generate(tree, genOpts);
if (genOpts.sourceMapWithCode) {
return result.code + "\n" + util.sourceMapToComment(result.map) + "\n";
} else {
return result + "\n";
}
};
transform._runTransformer = function (transformer, tree, opts) {
if (transformer.Program) transformer.Program(tree, opts);
traverse.replace(tree, function (node, parent) {
var fn = transformer[node.type] || transformer.all;
if (!fn) return;
return fn(node, parent, opts);
});
};
transform.test = function (actual, expect, opts) {
expect = [].concat(expect).join("\n");
actual = [].concat(actual).join("\n");
opts = opts || {};
_.defaults(opts, { filename: "test" });
actual = util.parse(transform(actual, opts));
expect = util.parse(expect);
try {
assert.deepEqual(actual, expect);
} catch (err) {
actual = escodegen.generate(actual);
expect = escodegen.generate(expect);
assert.equal(actual, expect);
}
};
transform.transformers = {
arrowFunctions: require("./transformers/arrow-functions"),
classes: require("./transformers/classes"),
spread: require("./transformers/spread"),
templateLiterals: require("./transformers/template-literals"),
propertyMethodAssignment: require("./transformers/property-method-assignment"),
defaultParameters: require("./transformers/default-parameters"),
destructuringAssignment: require("./transformers/destructuring-assignment"),
generators: require("./transformers/generators"),
blockBinding: require("./transformers/block-binding"),
modules: require("./transformers/modules"),
restParameters: require("./transformers/rest-parameters")
};

View File

@ -0,0 +1,27 @@
var traverse = require("../traverse");
var util = require("../util");
exports.ArrowFunctionExpression = function (node) {
var body = node.body;
if (body.type !== "BlockStatement") {
body = {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: body
}]
};
}
node.expression = false;
node.body = body;
node.type = "FunctionExpression";
if (traverse.hasType(node, "ThisExpression")) {
return util.template("function-bind-this", {
FUNCTION: node
});
} else {
return node;
}
};

View File

@ -0,0 +1,59 @@
var transform = require("../transform");
var traverse = require("../traverse");
var util = require("../util");
var _ = require("lodash");
var isLet = function (node) {
if (node.type === "VariableDeclaration" && node.kind === "let") {
node.kind = "var";
return true;
}
};
exports.Program = function (node) {
_.each(node.body, isLet);
};
exports.BlockStatement = function (node, parent) {
var hasLet = false;
_.each(node.body, function (bodyNode) {
if (isLet(bodyNode)) hasLet = true;
});
if (!hasLet) return;
// ignore if we're direct children of a closure already
if (parent.type === "FunctionExpression") return;
node.body = [buildNode(node.body)];
};
exports.ForInStatement = function (node) {
if (isLet(node.left)) return buildNode(node);
};
exports.ForStatement = function (node) {
if (isLet(node.init)) return buildNode(node);
};
var buildNode = function (node) {
var func = {
type: "FunctionExpression",
params: [],
defaults: [],
body: {
type: "BlockStatement",
body: [].concat(node)
}
};
var templateName = "function-call";
if (traverse.hasType(node, "ThisExpression")) {
templateName = "function-call-this";
}
return util.template(templateName, {
FUNCTION: func
}, true);
};

View File

@ -0,0 +1,132 @@
var traverse = require("../traverse");
var util = require("../util");
var _ = require("lodash");
exports.ClassDeclaration = function (node, parent, opts) {
var superName = node.superClass;
var className = node.id;
var root = util.template("class", {
CLASS_NAME: className
}, true);
var container = root.declarations[0].init;
var block = container.callee.body;
var body = block.body;
var returnStatement = body.pop();
if (superName) {
body.push(util.template("inherits", {
SUPER_NAME: superName,
CLASS_NAME: className
}, true));
container.arguments.push(superName);
container.callee.params.push(superName);
}
buildClassBody(body, className, superName, node);
body.push(returnStatement);
return root;
};
var buildClassBody = function (body, className, superName, node) {
var mutatorMap = {};
var classBody = node.body.body;
_.each(classBody, function (bodyNode) {
if (bodyNode.type !== "MethodDefinition") return;
var methodName = bodyNode.key.name;
var method = bodyNode.value;
replaceInstanceSuperReferences(superName, method);
if (methodName === "constructor") {
if (bodyNode.kind === "") {
addConstructor(body[0], method);
} else {
throw new Error("unknown kind for constructor method");
}
} else {
if (bodyNode.kind === "") {
addInstanceMethod(body, className, methodName, method);
} else {
util.pushMutatorMap(mutatorMap, methodName, bodyNode.kind, bodyNode);
}
}
});
if (!_.isEmpty(mutatorMap)) {
var protoId = util.template("prototype-identifier", {
CLASS_NAME: className
});
body.push(util.buildDefineProperties(mutatorMap, protoId));
}
};
var superIdentifier = function (superName, node, parent) {
if (parent.property === node) return;
if (!superName) return;
node.name = superName.name;
// super(); -> ClassName.call(this);
if (parent.type === "CallExpression" && parent.callee === node) {
node.name += ".call";
parent.arguments.unshift({
type: "ThisExpression"
});
}
};
var replaceInstanceSuperReferences = function (superName, method) {
traverse(method, function (node, parent) {
if (node.type === "Identifier" && node.name === "super") {
superIdentifier(superName, node, parent);
} else if (node.type === "MemberExpression") {
// no accessing of super properties
var obj = node.object;
if (parent.object === node && obj.type === "Identifier" && obj.name === "super") {
throw new Error("cannot access super properties");
} else {
return;
}
} else if (node.type === "CallExpression") {
var callee = node.callee;
if (callee.type !== "MemberExpression") return;
if (callee.object.name !== "super") return;
callee.property.name = "prototype." + callee.property.name + ".call";
node.arguments.unshift({
type: "ThisExpression"
});
} else {
return;
}
if (!superName) {
throw new Error("cannot access super as this class has none");
}
});
};
var addConstructor = function (construct, method) {
construct.defaults = method.defaults;
construct.params = method.params;
construct.body = method.body;
};
var addInstanceMethod = function (body, className, methodName, method) {
body.push(util.template("class-method", {
METHOD_NAME: methodName,
CLASS_NAME: className,
FUNCTION: method
}, true));
};

View File

@ -0,0 +1,17 @@
var util = require("../util");
var _ = require("lodash");
exports.FunctionExpression = function (node) {
_.each(node.defaults, function (def, i) {
if (!def) return;
var param = node.params[i];
node.body.body.unshift(util.template("if-undefined-set-to", {
VARIABLE: param,
DEFAULT: def
}, true));
});
node.defaults = [];
};

View File

View File

@ -0,0 +1,85 @@
var util = require("../util");
var _ = require("lodash");
exports.ImportDeclaration = function (node) {
var nodes = [];
_.each(node.specifiers, function (specifier) {
var variableName = specifier.name || specifier.id;
var key = specifier.id.name;
var templateName = "require-assign";
if (node.kind !== "default") templateName += "-key";
nodes.push(util.template(templateName, {
VARIABLE_NAME: variableName.name,
MODULE_NAME: node.source,
KEY: key
}));
});
return nodes;
};
exports.ModuleDeclaration = function (node) {
return util.template("require-assign", {
VARIABLE_NAME: node.id,
MODULE_NAME: node.source
});
};
exports.ExportDeclaration = function (node) {
var nodes = [];
_.each(node.specifiers, function (specifier) {
var variableName = specifier.name || specifier.id;
if (specifier.type === "ExportBatchSpecifier") {
nodes.push(util.template("exports-wildcard", {
MODULE_NAME: node.source
}, true));
} else {
nodes.push(util.template("exports-require-assign-key", {
VARIABLE_NAME: variableName.name,
MODULE_NAME: node.source,
KEY: specifier.id.name
}, true));
}
});
var declar = node.declaration;
if (declar) {
if (node.default) {
nodes.push(util.template("exports-default", {
VALUE: declar
}, true));
} else {
if (declar.type === "VariableDeclaration") {
nodes.push(declar);
_.each(declar.declarations, function (declar) {
nodes.push(util.template("exports-alias-var", {
STRING_KEY: {
type: "Literal",
value: declar.id.name
},
KEY: declar.id
}, true));
});
} else if (declar.type === "FunctionDeclaration") {
declar.type = "FunctionExpression";
nodes.push(util.template("exports-assign", {
KEY: declar.id,
VALUE: declar
}, true));
} else {
throw new Error("unsupported export declaration type " + declar.type);
}
}
}
return nodes;
};

View File

@ -0,0 +1,32 @@
var util = require("../util");
var _ = require("lodash");
exports.Property = function (node) {
if (node.method) node.method = false;
};
exports.ObjectExpression = function (node) {
//if (node.ignorePropertyMethods) return;
//node.ignorePropertyMethods = true;
var mutatorMap = {};
node.properties = node.properties.filter(function (prop) {
if (prop.kind === "get" || prop.kind === "set") {
util.pushMutatorMap(mutatorMap, prop.key.name, prop.kind, prop.value);
return false;
} else {
return true;
}
});
if (_.isEmpty(mutatorMap)) return;
return util.template("object-define-properties-closure", {
OBJECT: node,
CONTENT: util.buildDefineProperties(mutatorMap, {
type: "Identifier",
name: "obj"
}).expression
});
};

View File

@ -0,0 +1,19 @@
var util = require("../util");
exports.FunctionExpression = function (node, parent) {
if (!node.rest) return;
var rest = node.rest;
delete node.rest;
var templateName = "arguments-slice-assign";
if (node.params.length) templateName += "-arg";
node.body.body.unshift(util.template(templateName, {
VARIABLE_NAME: rest,
SLICE_ARG: {
type: "Literal",
value: node.params.length
}
}));
};

View File

@ -0,0 +1,64 @@
var util = require("../util");
var _ = require("lodash");
exports.ArrayExpression = function (node, parent) {
//if (node.ignoreSpread) return;
var elements = node.elements;
if (!elements.length) return;
var spread = elements.pop();
if (spread.type !== "SpreadElement") {
elements.push(spread);
return;
}
var concat = util.template("array-concat", {
ARGUMENT: spread.argument
});
concat.callee.object.elements = elements;
return concat;
};
exports.CallExpression = function (node, parent) {
var args = node.arguments;
if (args.length && _.last(args).type === "SpreadElement") {
var spread = args.pop();
var spreadLiteral = spread.argument;
var contextLiteral = {
type: "Literal",
value: null
};
node.arguments = [];
if (args.length) {
if (spreadLiteral.name === "arguments") {
spreadLiteral = util.template("arguments-slice");
}
var concat = util.template("array-concat");
concat.arguments = [spreadLiteral];
concat.callee.object.elements = args;
node.arguments.push(concat);
} else {
node.arguments.push(spreadLiteral);
}
var callee = node.callee;
if (callee.type === "MemberExpression") {
contextLiteral = callee.object;
callee.property.name += ".apply";
} else {
node.callee.name += ".apply";
}
node.arguments.unshift(contextLiteral);
}
};

View File

@ -0,0 +1,40 @@
var _ = require("lodash");
var buildBinaryExpression = function (left, right) {
return {
type: "BinaryExpression",
operator: "+",
left: left,
right: right
};
};
exports.TemplateLiteral = function (node) {
var nodes = [];
_.each(node.quasis, function (elem) {
nodes.push({
type: "Literal",
value: elem.value.raw
});
var expr = node.expressions.shift();
if (expr) nodes.push(expr);
});
if (nodes.length > 1) {
// remove redundant '' at the end of the expression
var last = _.last(nodes);
if (last.type === "Literal" && last.value === "") nodes.pop();
var root = buildBinaryExpression(nodes.shift(), nodes.shift());
_.each(nodes, function (node) {
root = buildBinaryExpression(root, node);
});
return root;
} else {
return nodes[0];
}
};

View File

@ -0,0 +1,68 @@
var VISITOR_KEYS = require("./visitor-keys");
var _ = require("lodash");
var traverse = module.exports = function (parent, callback) {
if (_.isArray(parent)) {
_.each(parent, function (node) {
traverse(node, callback);
});
return;
}
var keys = VISITOR_KEYS[parent.type] || [];
_.each(keys, function (key) {
var nodes = parent[key];
if (!nodes) return;
var handle = function (obj, key) {
if (!obj[key]) return;
// strict references in case the callback modified/replaced the node
var result = callback(obj[key], parent, obj, key);
if (result === false) return;
traverse(obj[key], callback);
};
if (_.isArray(nodes)) {
_.each(nodes, function (node, i) {
handle(nodes, i);
});
parent[key] = _.flatten(nodes).filter(function (node) {
return node !== traverse.Delete;
});
} else {
handle(parent, key);
}
});
};
traverse.Delete = {};
traverse.hasType = function (tree, type) {
var has = false;
if (_.isArray(tree)) {
return !!_.find(tree, function (node) {
return traverse.hasType(node, type);
});
} else {
traverse(tree, function (node) {
if (node.type === type) {
has = true;
return false;
}
});
}
return has;
};
traverse.replace = function (node, callback) {
traverse(node, function (node, parent, obj, key) {
var result = callback(node, parent);
if (result != null) obj[key] = result;
});
};

View File

@ -0,0 +1,46 @@
{
"AssignmentExpression": ["left", "right"],
"ArrayExpression": ["elements"],
"ArrayPattern": ["elements"],
"ArrowFunctionExpression": ["params", "defaults", "rest", "body"],
"BlockStatement": ["body"],
"BinaryExpression": ["left", "right"],
"BreakStatement": ["label"],
"CallExpression": ["callee", "arguments"],
"CatchClause": ["param", "body"],
"ClassBody": ["body"],
"ClassDeclaration": ["id", "body", "superClass"],
"ClassExpression": ["id", "body", "superClass"],
"ConditionalExpression": ["test", "consequent", "alternate"],
"ContinueStatement": ["label"],
"DoWhileStatement": ["body", "test"],
"ExpressionStatement": ["expression"],
"ForStatement": ["init", "test", "update", "body"],
"ForInStatement": ["left", "right", "body"],
"ForOfStatement": ["left", "right", "body"],
"FunctionDeclaration": ["id", "params", "defaults", "rest", "body"],
"FunctionExpression": ["id", "params", "defaults", "rest", "body"],
"IfStatement": ["test", "consequent", "alternate"],
"LabeledStatement": ["label", "body"],
"LogicalExpression": ["left", "right"],
"MemberExpression": ["object", "property"],
"MethodDefinition": ["key", "value"],
"NewExpression": ["callee", "arguments"],
"ObjectExpression": ["properties"],
"ObjectPattern": ["properties"],
"Program": ["body"],
"Property": ["key", "value"],
"ReturnStatement": ["argument"],
"SequenceExpression": ["expressions"],
"SwitchStatement": ["discriminant", "cases"],
"SwitchCase": ["test", "consequent"],
"ThrowStatement": ["argument"],
"TryStatement": ["block", "handlers", "handler", "guardedHandlers", "finalizer"],
"UnaryExpression": ["argument"],
"UpdateExpression": ["argument"],
"VariableDeclaration": ["declarations"],
"VariableDeclarator": ["id", "init"],
"WhileStatement": ["test", "body"],
"WithStatement": ["object", "body"],
"YieldExpression": ["argument"]
}

182
lib/6to5/util.js Normal file
View File

@ -0,0 +1,182 @@
var estraverse = require("estraverse");
var traverse = require("./traverse");
var esprima = require("esprima");
var path = require("path");
var fs = require("fs");
var _ = require("lodash");
exports.parse = function (code, opts) {
opts = _.defaults(opts || {}, {
comment: true,
range: true,
loc: true
});
code = [].concat(code).join("");
try {
var tree = esprima.parse(code);
if (tree.tokens && tree.comments) {
estraverse.attachComments(tree, tree.comments, tree.tokens);
}
return tree;
} catch (err) {
if (err.lineNumber) {
err.message = err.message + exports.codeFrame(code, err.lineNumber, err.column);
}
throw err;
}
};
exports.canCompile = function (filename) {
return path.extname(filename) === ".js";
};
exports.sourceMapToComment = function (map) {
var json = JSON.stringify(map);
var base64 = new Buffer(json).toString("base64");
return "//# sourceMappingURL=data:application/json;base64," + base64;
};
exports.pushMutatorMap = function (mutatorMap, key, kind, method) {
var map = mutatorMap[key] = mutatorMap[key] || {};
if (map[kind]) {
throw new Error("a " + kind + " already exists for this property");
} else {
map[kind] = method;
}
};
exports.buildDefineProperties = function (mutatorMap, keyNode) {
var objExpr = {
type: "ObjectExpression",
properties: []
};
_.each(mutatorMap, function (map, key) {
var mapNode = {
type: "ObjectExpression",
properties: []
};
var propNode = {
type: "Property",
key: {
type: "Identifier",
name: key
},
value: mapNode,
kind: "init"
};
_.each(map, function (methodNode, type) {
if (methodNode.type === "MethodDefinition") methodNode = methodNode.value;
mapNode.properties.push({
type: "Property",
key: {
type: "Identifier",
name: type
},
value: methodNode,
kind: "init"
});
});
objExpr.properties.push(propNode);
});
return exports.template("object-define-properties", {
OBJECT: keyNode,
PROPS: objExpr
}, true);
};
exports.template = function (name, nodes, keepExpression) {
var template = _.cloneDeep(exports.templates[name]);
if (!_.isEmpty(nodes)) {
traverse.replace(template, function (node) {
if (node.type === "Identifier" && _.has(nodes, node.name)) {
var newNode = nodes[node.name];
if (_.isString(newNode)) {
node.name = newNode;
} else {
return newNode;
}
}
});
}
var normaliseNode = function (node) {
if (!keepExpression && node.type === "ExpressionStatement") {
return node.expression;
} else {
return node;
}
};
var body = template.body;
if (body.length <= 1) {
return normaliseNode(body[0]);
} else {
return body.map(normaliseNode);
}
};
exports.codeFrame = function (lines, lineNumber, colNumber) {
if (!lineNumber) return "";
colNumber = Math.max(colNumber, 0);
lines = lines.split("\n");
var start = Math.max(lineNumber - 3, 0);
var end = Math.min(lines.length, lineNumber + 3);
var width = (end + "").length;
return "\n" + lines.slice(start, end).map(function (line, i) {
var curr = i + start + 1;
var gutter = curr === lineNumber ? "> " : " ";
var sep = curr + exports.repeat(width + 1);
gutter += sep + "| ";
var str = gutter + line;
if (colNumber && curr === lineNumber) {
str += "\n";
str += exports.repeat(gutter.length - 2);
str += "|" + exports.repeat(colNumber) + "^";
}
return str;
}).join("\n");
};
exports.repeat = function (width, cha) {
cha = cha || " ";
return Array(width + 1).join(cha);
};
var templatesCacheLoc = __dirname + "/../../templates.json";
if (fs.existsSync(templatesCacheLoc)) {
exports.templates = require(templatesCacheLoc);
} else {
exports.templates = {};
var templatesLoc = __dirname + "/templates";
_.each(fs.readdirSync(templatesLoc), function (name) {
var key = path.basename(name, path.extname(name));
var code = fs.readFileSync(templatesLoc + "/" + name, "utf8");
exports.templates[key] = exports.parse(code, {
range: false,
loc: false
});
});
}

35
package.json Normal file
View File

@ -0,0 +1,35 @@
{
"name": "6to5",
"description": "Turn ES6 code into vanilla ES5 with no runtime required",
"version": "0.0.1",
"repository": {
"type": "git",
"url": "https://github.com/sebmck/6to5.git"
},
"bugs": {
"url": "https://github.com/sebmck/6to5/issues"
},
"preferGlobal": true,
"main": "lib/6to5/node.js",
"bin": {
"6to5": "./bin/6to5"
},
"scripts": {
"test": "mocha"
},
"dependencies": {
"chokidar": "^0.8.4",
"commander": "^2.3.0",
"escodegen": "https://github.com/Constellation/escodegen/tarball/master",
"esprima": "https://github.com/esnext/esprima/tarball/harmony-esnext",
"estraverse": "^1.5.1",
"fs-readdir-recursive": "0.0.2",
"lodash": "^2.4.1",
"mkdirp": "^0.5.0",
"through": "^2.3.4"
},
"devDependencies": {
"istanbul": "0.3.2",
"mocha": "^1.21.4"
}
}

1
register.js Normal file
View File

@ -0,0 +1 @@
require("6to5").register();

7
test/api.js Normal file
View File

@ -0,0 +1,7 @@
suite("api", function () {
test("whitelist");
test("blacklist");
test("tolerant");
});

51
test/classes.js Normal file
View File

@ -0,0 +1,51 @@
var transform = require("../lib/6to5/transform");
var assert = require("assert");
suite("classes", function () {
test("no calling super properties", function () {
assert.throws(function () {
transform.test([
"class Test extends Foo {",
" constructor() {",
" super.test.whatever();",
" }",
"}"
]);
}, /cannot access super properties/, "unexpected error");
});
test("no accessing super properties", function () {
assert.throws(function () {
transform.test([
"class Test extends Foo {",
" constructor() {",
" super.test.whatever;",
" }",
"}"
]);
}, /cannot access super properties/, "unexpected error");
});
test("accessing super without having one", function () {
assert.throws(function () {
transform.test([
"class Test {",
" constructor() {",
" super();",
" }",
"}"
]);
}, /cannot access super as this class has none/, "unexpected error");
});
test("defining constructor as a mutator", function () {
assert.throws(function () {
transform.test([
"class Test {",
" get constructor() {",
" }",
"}"
]);
}, /unknown kind for constructor method/, "unexpected error");
});
});

3
test/errors.js Normal file
View File

@ -0,0 +1,3 @@
suite("errors", function () {
});

View File

@ -0,0 +1 @@
var t = () => 5 + 5;

View File

@ -0,0 +1,3 @@
var t = function () {
return 5 + 5;
};

View File

@ -0,0 +1 @@
var t = () => {};

View File

@ -0,0 +1,2 @@
var t = function () {
};

View File

@ -0,0 +1 @@
arr.map(i => i + 1);

View File

@ -0,0 +1,3 @@
arr.map(function (i) {
return i + 1;
});

View File

@ -0,0 +1 @@
var t = (i, x) => i * x;

View File

@ -0,0 +1,3 @@
var t = function (i, x) {
return i * x;
};

View File

@ -0,0 +1 @@
var t = i => i * 5;

View File

@ -0,0 +1,3 @@
var t = function (i) {
return i * 5;
};

View File

@ -0,0 +1 @@
var t = (i) => i * 5;

View File

@ -0,0 +1,3 @@
var t = function (i) {
return i * 5;
};

View File

@ -0,0 +1 @@
var t = x => this.x + x;

View File

@ -0,0 +1,3 @@
var t = function (x) {
return this.x + x;
}.bind(this);

View File

@ -0,0 +1,5 @@
{
let val = 1;
let multiplier = 5;
console.log(val * multiplier);
}

View File

@ -0,0 +1,7 @@
{
(function () {
var val = 1;
var multiplier = 5;
console.log(val * multiplier);
}());
}

View File

@ -0,0 +1,4 @@
{
let val = 1;
console.log(val * 2);
}

View File

@ -0,0 +1,6 @@
{
(function () {
var val = 1;
console.log(val * 2);
}());
}

View File

@ -0,0 +1,4 @@
(function () {
let i = 5;
console.log(i);
}());

View File

@ -0,0 +1,4 @@
(function () {
var i = 5;
console.log(i);
}());

View File

@ -0,0 +1,3 @@
for (let i = 0; i < 9; i++) {
console.log(i);
}

View File

@ -0,0 +1,5 @@
(function () {
for (var i = 0; i < 9; i++) {
console.log(i);
}
}());

View File

@ -0,0 +1,5 @@
var arr = [1, 2, 3];
for (let i in arr) {
let val = arr[i];
console.log(val * 2);
}

View File

@ -0,0 +1,13 @@
var arr = [
1,
2,
3
];
(function () {
for (var i in arr) {
(function () {
var val = arr[i];
console.log(val * 2);
}());
}
}());

View File

@ -0,0 +1 @@
let arr = [1, 2, 3];

View File

@ -0,0 +1,5 @@
var arr = [
1,
2,
3
];

View File

@ -0,0 +1,5 @@
var arr = [1, 2, 3];
for (let i in arr) {
let val = arr[i];
console.log(val * this.multiplier);
}

View File

@ -0,0 +1,13 @@
var arr = [
1,
2,
3
];
(function () {
for (var i in arr) {
(function () {
var val = arr[i];
console.log(val * this.multiplier);
}.call(this));
}
}.call(this));

View File

@ -0,0 +1,8 @@
class Test extends Foo {
constructor() {
woops.super.test();
super();
super.test();
foob(super);
}
}

View File

@ -0,0 +1,17 @@
var Test = function (Foo) {
function Test() {
woops.super.test();
Foo.call(this);
Foo.prototype.test.call(this);
foob(Foo);
}
Test.prototype = Object.create(Foo.prototype, {
constructor: {
value: Test,
enumerable: false,
writable: true,
configurable: true
}
});
return Test;
}(Foo);

View File

@ -0,0 +1,5 @@
class Test {
constructor() {
this.state = "test";
}
}

View File

@ -0,0 +1,6 @@
var Test = function () {
function Test() {
this.state = "test";
}
return Test;
}();

View File

@ -0,0 +1,8 @@
class Test {
get test() {
return 5 + 5;
}
set test(val) {
this._test = val;
}
}

View File

@ -0,0 +1,15 @@
var Test = function () {
function Test() {
}
Object.defineProperties(Test.prototype, {
test: {
get: function () {
return 5 + 5;
},
set: function (val) {
this._test = val;
}
}
});
return Test;
}();

View File

@ -0,0 +1,5 @@
class Test {
get test() {
return 5 + 5;
}
}

View File

@ -0,0 +1,12 @@
var Test = function () {
function Test() {
}
Object.defineProperties(Test.prototype, {
test: {
get: function () {
return 5 + 5;
}
}
});
return Test;
}();

View File

@ -0,0 +1,5 @@
class Test {
test() {
return 5 + 5;
}
}

View File

@ -0,0 +1,8 @@
var Test = function () {
function Test() {
}
Test.prototype.test = function () {
return 5 + 5;
};
return Test;
}();

View File

@ -0,0 +1,5 @@
class Test {
set test(val) {
this._test = val;
}
}

View File

@ -0,0 +1,12 @@
var Test = function () {
function Test() {
}
Object.defineProperties(Test.prototype, {
test: {
set: function (val) {
this._test = val;
}
}
});
return Test;
}();

View File

@ -0,0 +1 @@
class Test { }

View File

@ -0,0 +1,5 @@
var Test = function () {
function Test() {
}
return Test;
}();

View File

@ -0,0 +1 @@
class Test extends Foo { }

View File

@ -0,0 +1,13 @@
var Test = function (Foo) {
function Test() {
}
Test.prototype = Object.create(Foo.prototype, {
constructor: {
value: Test,
enumerable: false,
writable: true,
configurable: true
}
});
return Test;
}(Foo);

Some files were not shown because too many files have changed in this diff Show More