es6-module-transpiler
Advanced tools
Comparing version 0.7.0 to 0.8.0
@@ -8,2 +8,3 @@ /* jshint node:true, undef:true, unused:true */ | ||
var b = types.builders; | ||
var util = require('ast-util'); | ||
@@ -13,2 +14,3 @@ var extend = require('../utils').extend; | ||
var Formatter = require('./formatter'); | ||
var sourcePosition = require('../utils').sourcePosition; | ||
@@ -38,5 +40,8 @@ /** | ||
b.expressionStatement(b.literal('use strict')), | ||
self.buildExports(mod), | ||
self.buildEarlyExports(mod), | ||
self.buildRequires(mod) | ||
); | ||
body.push( | ||
self.buildLateExports(mod) | ||
); | ||
mod.ast.filename = mod.relativePath; | ||
@@ -48,82 +53,81 @@ return mod.ast; | ||
/** | ||
* Process all export bindings which may be exported before any module code is | ||
* actually run, i.e. function declarations. | ||
* | ||
* @param {Module} mod | ||
* @returns {AST.Statement} | ||
* @private | ||
*/ | ||
CommonJSFormatter.prototype.buildEarlyExports = function(mod) { | ||
var self = this; | ||
var assignments = []; | ||
var exportObject = b.identifier('exports'); | ||
this.forEachExportBinding(mod, function(specifier, name) { | ||
if (!n.FunctionDeclaration.check(specifier.declaration.node.declaration)) { | ||
// Only function declarations are handled as early exports. | ||
return; | ||
} | ||
assignments.push(b.assignmentExpression( | ||
'=', | ||
b.memberExpression( | ||
exportObject, | ||
b.identifier(name), | ||
false | ||
), | ||
b.identifier(specifier.from) | ||
)); | ||
}); | ||
return b.expressionStatement( | ||
b.sequenceExpression(assignments) | ||
); | ||
}; | ||
/** | ||
* Process all export bindings which were not exported at the beginning of the | ||
* module, i.e. everything except function declarations. | ||
* | ||
* @param {Module} mod | ||
* @return {Statement} | ||
* @returns {AST.Statement} | ||
* @private | ||
*/ | ||
CommonJSFormatter.prototype.buildExports = function(mod) { | ||
CommonJSFormatter.prototype.buildLateExports = function(mod) { | ||
var self = this; | ||
var properties = []; | ||
var assignments = []; | ||
var exportObject = b.identifier('exports'); | ||
mod.exports.names.forEach(function(name) { | ||
var specifier = mod.exports.findSpecifierByName(name); | ||
this.forEachExportBinding(mod, function(specifier, name) { | ||
if (n.FunctionDeclaration.check(specifier.declaration.node.declaration)) { | ||
// Function declarations are handled as early exports. | ||
return; | ||
} | ||
assert.ok( | ||
specifier, | ||
'no export specifier found for export name `' + | ||
name + '` from ' + mod.relativePath | ||
); | ||
var from = | ||
!specifier.from ? | ||
self.defaultExportReference(mod) : | ||
specifier.importSpecifier ? | ||
specifier.importSpecifier ? | ||
self.reference( | ||
specifier.importSpecifier.declaration.source, | ||
specifier.importSpecifier.from | ||
) : | ||
specifier.declaration.source ? | ||
self.reference( | ||
specifier.importSpecifier.declaration.source, | ||
specifier.importSpecifier.from | ||
specifier.declaration.source, | ||
specifier.name | ||
) : | ||
specifier.declaration.source ? | ||
self.reference( | ||
specifier.declaration.source, | ||
specifier.name | ||
) : | ||
b.identifier(specifier.from); | ||
b.identifier(specifier.from); | ||
properties.push(b.property( | ||
'init', | ||
b.identifier(name), | ||
b.objectExpression([ | ||
// Simulate named export bindings with a getter. | ||
b.property( | ||
'init', | ||
b.identifier('get'), | ||
b.functionExpression( | ||
null, | ||
[], | ||
b.blockStatement([b.returnStatement(from)]) | ||
) | ||
), | ||
b.property( | ||
'init', | ||
b.identifier('enumerable'), | ||
b.literal(true) | ||
) | ||
]) | ||
)); | ||
}); | ||
var exportObject = b.identifier('exports'); | ||
if (properties.length > 0) { | ||
exportObject = b.callExpression( | ||
assignments.push(b.assignmentExpression( | ||
'=', | ||
b.memberExpression( | ||
b.identifier('Object'), | ||
b.identifier('defineProperties'), | ||
exportObject, | ||
b.identifier(name), | ||
false | ||
), | ||
[ | ||
exportObject, | ||
b.objectExpression(properties) | ||
] | ||
); | ||
} | ||
from | ||
)); | ||
}); | ||
return b.expressionStatement( | ||
b.callExpression( | ||
b.memberExpression( | ||
b.identifier('Object'), | ||
b.identifier('seal'), | ||
false | ||
), | ||
[exportObject] | ||
) | ||
b.sequenceExpression(assignments) | ||
); | ||
@@ -133,2 +137,28 @@ }; | ||
/** | ||
* Iterates over each exported binding and calls `iterator` with its specifier. | ||
* | ||
* @param {Module} mod | ||
* @param {function(ModuleBindingSpecifier, string)} iterator | ||
* @private | ||
*/ | ||
CommonJSFormatter.prototype.forEachExportBinding = function(mod, iterator) { | ||
mod.exports.names.forEach(function(name) { | ||
var specifier = mod.exports.findSpecifierByName(name); | ||
assert.ok( | ||
specifier, | ||
'no export specifier found for export name `' + | ||
name + '` from ' + mod.relativePath | ||
); | ||
if (!specifier.from) { | ||
// Default exports are handled elsewhere. | ||
return; | ||
} | ||
iterator(specifier, name); | ||
}); | ||
}; | ||
/** | ||
* Build a series of requires based on the imports (and exports with sources) | ||
@@ -139,3 +169,3 @@ * in the given module. | ||
* @param {Module} mod | ||
* @return {VariableDeclaration} | ||
* @return {AST.VariableDeclaration|AST.EmptyStatement} | ||
*/ | ||
@@ -188,6 +218,11 @@ CommonJSFormatter.prototype.buildRequires = function(mod) { | ||
CommonJSFormatter.prototype.defaultExport = function(mod, declaration) { | ||
return b.variableDeclaration( | ||
'var', | ||
[b.variableDeclarator(this.defaultExportReference(mod), declaration)] | ||
); | ||
return b.expressionStatement(b.assignmentExpression( | ||
'=', | ||
b.memberExpression( | ||
b.identifier('exports'), | ||
b.literal('default'), | ||
true | ||
), | ||
declaration | ||
)); | ||
}; | ||
@@ -199,3 +234,3 @@ | ||
* @param {Module} mod | ||
* @return {Node} | ||
* @return {AST.Node} | ||
* @private | ||
@@ -289,17 +324,115 @@ */ | ||
/** | ||
* Since named export reassignment is just a local variable, we can ignore it. | ||
* e.g. | ||
* We explicitly disallow reassignment because we cannot propagate changes to | ||
* importing modules as we would in ES5, e.g. these are both disallowed: | ||
* | ||
* ```js | ||
* export var foo = 1; | ||
* foo = 2; | ||
* foo++; | ||
* ``` | ||
* export var foo = 1; | ||
* foo = 2; | ||
* | ||
* @param {Module} mod | ||
* @param {NodePath} nodePath | ||
* @return {?Replacement} | ||
* export var bar = 1; | ||
* bar++; | ||
* | ||
* @override | ||
*/ | ||
CommonJSFormatter.prototype.processExportReassignment = function(mod, nodePath) { | ||
return null; | ||
var node = nodePath.node; | ||
var exportName; | ||
if (n.AssignmentExpression.check(node)) { | ||
exportName = node.left.name; | ||
} else if (n.UpdateExpression.check(node)) { | ||
exportName = node.argument.name; | ||
} else { | ||
throw new Error('unexpected export reassignment type: ' + node.type); | ||
} | ||
if (n.UpdateExpression.check(node) && !node.prefix) { | ||
/** | ||
* The result of `a++` is the value of `a` before it is incremented, so we | ||
* can't just use the result as the new value for `exports.a`. The question | ||
* is whether we need to preserve the result of `a++` or not. In this case, | ||
* we do: | ||
* | ||
* ```js | ||
* foo(a++); | ||
* ``` | ||
* | ||
* But in this case we don't, since the result is ignored: | ||
* | ||
* ```js | ||
* a++; | ||
* ``` | ||
*/ | ||
if (n.ExpressionStatement.check(nodePath.parent.node)) { | ||
// The result is ignored here, so `a++` can become `a++, exports.a = a`. | ||
return Replacement.swaps( | ||
nodePath, | ||
b.sequenceExpression([ | ||
node, | ||
b.assignmentExpression( | ||
'=', | ||
b.memberExpression( | ||
b.identifier('exports'), | ||
b.identifier(exportName), | ||
false | ||
), | ||
b.identifier(exportName) | ||
) | ||
]) | ||
); | ||
} else { | ||
// The result is used here, so we need a temporary variable to store it. | ||
var result = util.injectVariable(nodePath.scope, util.uniqueIdentifier(nodePath.scope)); | ||
return Replacement.swaps( | ||
nodePath, | ||
b.sequenceExpression([ | ||
b.assignmentExpression( | ||
'=', | ||
result, | ||
node | ||
), | ||
b.assignmentExpression( | ||
'=', | ||
b.memberExpression( | ||
b.identifier('exports'), | ||
b.identifier(exportName), | ||
false | ||
), | ||
b.identifier(exportName) | ||
), | ||
result | ||
]) | ||
); | ||
} | ||
} | ||
/** | ||
* We can use the result of the update/assignment as-is in this case, e.g. | ||
* | ||
* ```js | ||
* foo(++a); | ||
* b = 9; | ||
* ``` | ||
* | ||
* Can become: | ||
* | ||
* ```js | ||
* foo(exports.a = ++a); | ||
* exports.b = b = 9; | ||
* ``` | ||
*/ | ||
return Replacement.swaps( | ||
nodePath, | ||
b.assignmentExpression( | ||
'=', | ||
b.memberExpression( | ||
b.identifier('exports'), | ||
b.identifier(exportName), | ||
false | ||
), | ||
node | ||
) | ||
); | ||
}; | ||
@@ -306,0 +439,0 @@ |
{ | ||
"name": "es6-module-transpiler", | ||
"version": "0.7.0", | ||
"version": "0.8.0", | ||
"description": "es6-module-transpiler is an experimental compiler that allows you to write your JavaScript using a subset of the current ES6 module syntax, and compile it into various formats.", | ||
@@ -5,0 +5,0 @@ "homepage": "http://square.github.com/es6-module-transpiler", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
102716
3139