es6-module-transpiler
Advanced tools
Comparing version 0.6.2 to 0.7.0
@@ -9,3 +9,2 @@ /* jshint node:true, undef:true, unused:true */ | ||
var formatters = require('../formatters'); | ||
var FileResolver = require('../file_resolver'); | ||
var Container = require('../container'); | ||
@@ -12,0 +11,0 @@ var FileResolver = require('../file_resolver'); |
@@ -8,2 +8,5 @@ /* jshint node:true, undef:true, unused:true */ | ||
/** @typedef {{resolveModule: function(string, Module, Container): Module}} */ | ||
var Resolver; | ||
/** | ||
@@ -13,3 +16,3 @@ * Represents a container of modules for the given options. | ||
* @constructor | ||
* @param {{resolver: Resolver}} options | ||
* @param {{resolvers: Resolver[], formatter: Formatter}} options | ||
*/ | ||
@@ -26,10 +29,2 @@ function Container(options) { | ||
); | ||
assert.equal( | ||
typeof formatter.reference, 'function', | ||
'option `formatter` must have function `reference`' | ||
); | ||
assert.equal( | ||
typeof formatter.build, 'function', | ||
'option `formatter` must have function `build`' | ||
); | ||
@@ -73,3 +68,3 @@ var resolvers = options && options.resolvers; | ||
* @param {string} importedPath | ||
* @param {?Module} fromModule | ||
* @param {Module=} fromModule | ||
* @return {Module} | ||
@@ -114,3 +109,3 @@ */ | ||
var counter = 0; | ||
var nameExists = false; | ||
var nameExists; | ||
@@ -145,2 +140,7 @@ while (true) { | ||
/** | ||
* Writes the contents of this container to the given path. | ||
* | ||
* @param {string} target | ||
*/ | ||
Container.prototype.write = function(target) { | ||
@@ -152,2 +152,7 @@ var files = this.convert(); | ||
/** | ||
* Converts the contents of this container using the current formatter. | ||
* | ||
* @return {File[]} | ||
*/ | ||
Container.prototype.convert = function() { | ||
@@ -167,2 +172,5 @@ if (this.formatter.beforeConvert) { | ||
/** | ||
* Follows all imports/exports looking for new modules to add to this container. | ||
*/ | ||
Container.prototype.findImportedModules = function() { | ||
@@ -176,5 +184,3 @@ var knownModules; | ||
// Force loading of imported modules. | ||
/* jshint expr:true */ | ||
knownModules[i].imports.modules; | ||
/* jshint expr:false */ | ||
noop(knownModules[i].imports.modules); | ||
} | ||
@@ -184,2 +190,7 @@ } | ||
/** | ||
* Gets the modules in this container in no particular order. | ||
* | ||
* @return {Module[]} | ||
*/ | ||
Container.prototype.getModules = function() { | ||
@@ -192,2 +203,8 @@ var modules = this.modules; | ||
/** | ||
* Does nothing. This is only here to make JSHint/other static analysis | ||
* tools happy about using getters for side effects. | ||
*/ | ||
function noop() {} | ||
module.exports = Container; |
@@ -23,14 +23,14 @@ /* jshint node:true, undef:true, unused:true */ | ||
* @constructor | ||
* @param {ast-types.Node} declaration | ||
* @param {ast-types.Identifier} identifier | ||
* @param {Node} declaration | ||
* @param {Identifier} identifier | ||
*/ | ||
function DeclarationInfo(declaration, identifier) { | ||
/** | ||
* @type {ast-types.Node} | ||
* @property declaration | ||
* @type {Node} | ||
* @name DeclarationInfo#declaration | ||
*/ | ||
this.declaration = declaration; | ||
/** | ||
* @type {ast-types.Identifier} | ||
* @property identifier | ||
* @type {Identifier} | ||
* @name DeclarationInfo#identifier | ||
*/ | ||
@@ -44,3 +44,3 @@ this.identifier = identifier; | ||
* | ||
* @param {ast-types.NodePath} identifierPath | ||
* @param {NodePath} identifierPath | ||
* @return {?DeclarationInfo} | ||
@@ -47,0 +47,0 @@ */ |
@@ -23,2 +23,3 @@ /* jshint node:true, undef:true, unused:true */ | ||
* @constructor | ||
* @extends ModuleBindingList | ||
* @param {Module} mod | ||
@@ -33,3 +34,3 @@ */ | ||
* @private | ||
* @param {ast-types.Node} node | ||
* @param {AST.Declaration} node | ||
* @return {boolean} | ||
@@ -45,4 +46,4 @@ */ | ||
* @private | ||
* @param {ast-types.ExportDeclaration} node | ||
* @return {Import} | ||
* @param {AST.ExportDeclaration} node | ||
* @return {ExportDeclaration} | ||
*/ | ||
@@ -67,3 +68,3 @@ ExportDeclarationList.prototype.declarationForNode = function(node) { | ||
/** | ||
* @param {ast-types.NodePath} referencePath | ||
* @param {NodePath} referencePath | ||
* @return {?ExportSpecifier} | ||
@@ -74,3 +75,3 @@ */ | ||
// This is a direct export from another module, e.g. `export { foo } from 'foo'`. | ||
return this.findSpecifierByIdentifier(referencePath.node); | ||
return /** @type {ExportSpecifier} */this.findSpecifierByIdentifier(referencePath.node); | ||
} | ||
@@ -84,3 +85,3 @@ | ||
var specifier = this.findSpecifierByName(declaration.node.name); | ||
var specifier = /** @type {ExportSpecifier} */this.findSpecifierByName(declaration.node.name); | ||
assert.ok( | ||
@@ -100,3 +101,3 @@ specifier, | ||
* @param {Module} mod | ||
* @param {ast-types.ExportDeclaration} node | ||
* @param {ExportDeclaration} node | ||
*/ | ||
@@ -123,3 +124,3 @@ function ExportDeclaration(mod, node) { | ||
/** | ||
* @alias inspect | ||
* @see ExportDeclaration#inspect | ||
*/ | ||
@@ -136,3 +137,3 @@ ExportDeclaration.prototype.toString = ExportDeclaration.prototype.inspect; | ||
* @param {Module} mod | ||
* @param {ast-types.ExportDeclaration} node | ||
* @param {AST.ExportDeclaration} node | ||
*/ | ||
@@ -147,6 +148,6 @@ function DefaultExportDeclaration(mod, node) { | ||
* | ||
* @type {Array.<ExportSpecifier>} | ||
* @property specifiers | ||
* @type {ExportSpecifier[]} | ||
* @name DefaultExportSpecifier#specifiers | ||
*/ | ||
memo(DefaultExportDeclaration.prototype, 'specifiers', function() { | ||
memo(DefaultExportDeclaration.prototype, 'specifiers', /** @this DefaultExportDeclaration */function() { | ||
var specifier = new DefaultExportSpecifier(this, this.node.declaration); | ||
@@ -164,3 +165,3 @@ return [specifier]; | ||
* @param {Module} mod | ||
* @param {ast-types.ExportDeclaration} node | ||
* @param {AST.ExportDeclaration} node | ||
*/ | ||
@@ -175,6 +176,6 @@ function NamedExportDeclaration(mod, node) { | ||
* | ||
* @type {Array.<ExportSpecifier>} | ||
* @property specifiers | ||
* @type {ExportSpecifier[]} | ||
* @name NamedExportDeclaration#specifiers | ||
*/ | ||
memo(NamedExportDeclaration.prototype, 'specifiers', function() { | ||
memo(NamedExportDeclaration.prototype, 'specifiers', /** @this NamedExportDeclaration */function() { | ||
var self = this; | ||
@@ -194,3 +195,3 @@ return this.node.specifiers.map(function(specifier) { | ||
* @param {Module} mod | ||
* @param {ast-types.ExportDeclaration} node | ||
* @param {AST.ExportDeclaration} node | ||
*/ | ||
@@ -203,4 +204,8 @@ function VariableExportDeclaration(mod, node) { | ||
/** | ||
* Gets the list of export specifiers for this declaration. | ||
* | ||
* @type {ExportSpecifier[]} | ||
* @name VariableExportDeclaration#specifiers | ||
*/ | ||
memo(VariableExportDeclaration.prototype, 'specifiers', function() { | ||
memo(VariableExportDeclaration.prototype, 'specifiers', /** @this VariableExportDeclaration */function() { | ||
var self = this; | ||
@@ -220,3 +225,3 @@ return this.node.declaration.declarations.map(function(declarator) { | ||
* @param {Module} mod | ||
* @param {ast-types.ExportDeclaration} node | ||
* @param {AST.ExportDeclaration} node | ||
*/ | ||
@@ -229,4 +234,8 @@ function FunctionExportDeclaration(mod, node) { | ||
/** | ||
* Gets the list of export specifiers for this declaration. | ||
* | ||
* @type {ExportSpecifier[]} | ||
* @name FunctionExportDeclaration#specifiers | ||
*/ | ||
memo(FunctionExportDeclaration.prototype, 'specifiers', function() { | ||
memo(FunctionExportDeclaration.prototype, 'specifiers', /** @this FunctionExportDeclaration */function() { | ||
return [new ExportSpecifier(this, this.node.declaration)]; | ||
@@ -241,3 +250,3 @@ }); | ||
* @param {ExportDeclaration} declaration | ||
* @param {ast-types.ExportSpecifier} node | ||
* @param {AST.Node} node | ||
*/ | ||
@@ -259,6 +268,6 @@ function ExportSpecifier(declaration, node) { | ||
* | ||
* @type {DeclarationInfo} | ||
* @property moduleDeclaration | ||
* @type {?DeclarationInfo} | ||
* @name ExportSpecifier#moduleDeclaration | ||
*/ | ||
memo(ExportSpecifier.prototype, 'moduleDeclaration', function() { | ||
memo(ExportSpecifier.prototype, 'moduleDeclaration', /** @this ExportSpecifier */function() { | ||
if (this.declaration.source) { | ||
@@ -298,3 +307,3 @@ // This is part of a direct export, e.g. `export { ... } from '...'`, so | ||
* @param {ExportDeclaration} declaration | ||
* @param {ast-types.ExportSpecifier} node | ||
* @param {AST.Expression} node | ||
*/ | ||
@@ -307,6 +316,14 @@ function DefaultExportSpecifier(declaration, node) { | ||
/** | ||
* The node of a default export specifier is an expression, not a specifier. | ||
* | ||
* @type {AST.Expression} | ||
*/ | ||
DefaultExportSpecifier.prototype.node = null; | ||
/** | ||
* Default export specifier names are always "default". | ||
* | ||
* @type {string} | ||
* @property name | ||
* @name DefaultExportSpecifier#name | ||
* @default "default" | ||
*/ | ||
@@ -318,4 +335,5 @@ DefaultExportSpecifier.prototype.name = 'default'; | ||
* | ||
* @type {?ast-types.Identifier} | ||
* @property identifier | ||
* @type {?Identifier} | ||
* @name DefaultExportSpecifier#identifier | ||
* @default null | ||
*/ | ||
@@ -328,3 +346,4 @@ DefaultExportSpecifier.prototype.identifier = null; | ||
* @type {?string} | ||
* @property from | ||
* @name DefaultExportSpecifier#from | ||
* @default null | ||
*/ | ||
@@ -337,3 +356,4 @@ DefaultExportSpecifier.prototype.from = null; | ||
* @type {?DeclarationInfo} | ||
* @property moduleDeclaration | ||
* @name DefaultExportSpecifier#moduleDeclaration | ||
* @default null | ||
*/ | ||
@@ -340,0 +360,0 @@ DefaultExportSpecifier.prototype.moduleDeclaration = null; |
@@ -11,3 +11,5 @@ /* jshint node:true, undef:true, unused:true */ | ||
var IFFE = utils.IFFE; | ||
var extend = utils.extend; | ||
var sort = require('../sorting').sort; | ||
var Formatter = require('./formatter'); | ||
@@ -20,2 +22,3 @@ | ||
* | ||
* ```js | ||
* // a.js | ||
@@ -28,5 +31,7 @@ * import { b } from './b'; | ||
* export var b2 = 6; | ||
* ``` | ||
* | ||
* The final output will be a single file looking something like this: | ||
* | ||
* ```js | ||
* (function() { | ||
@@ -41,6 +46,10 @@ * "use strict"; | ||
* }).call(this); | ||
* ``` | ||
* | ||
* @constructor | ||
*/ | ||
function BundleFormatter() {} | ||
function BundleFormatter() { | ||
Formatter.call(this); | ||
} | ||
extend(BundleFormatter, Formatter); | ||
@@ -59,41 +68,26 @@ /** | ||
/** | ||
* Returns an expression which globally references the export named by | ||
* `identifier` for the given module `mod`. | ||
* | ||
* @param {Module} mod | ||
* @param {ast-types.Identifier|string} identifier | ||
* @return {ast-types.MemberExpression} | ||
* @override | ||
*/ | ||
BundleFormatter.prototype.reference = function(/* mod, identifier */) { | ||
throw new Error('#reference must be implemented in subclasses'); | ||
BundleFormatter.prototype.build = function(modules) { | ||
modules = sort(modules); | ||
return [b.file(b.program([b.expressionStatement(IFFE( | ||
b.expressionStatement(b.literal('use strict')), | ||
modules.length === 1 ? | ||
modules[0].ast.program.body : | ||
modules.reduce(function(statements, mod) { | ||
return statements.concat(mod.ast.program.body); | ||
}, []) | ||
))]))]; | ||
}; | ||
/** | ||
* Process a variable declaration found at the top level of the module. We need | ||
* to ensure that exported variables are rewritten appropriately, so we may | ||
* need to rewrite some or all of this variable declaration. For example: | ||
* | ||
* var a = 1, b, c = 3; | ||
* ... | ||
* export { a, b }; | ||
* | ||
* We turn those being exported into assignments as needed, e.g. | ||
* | ||
* var c = 3; | ||
* mod$$a = 1; | ||
* | ||
* @param {Module} mod | ||
* @param {ast-types.NodePath} nodePath | ||
* @return {?Replacement} | ||
* @override | ||
*/ | ||
BundleFormatter.prototype.processVariableDeclaration = function(mod, nodePath) { | ||
var self = this; | ||
return Replacement.map( | ||
nodePath.get('declarations'), | ||
function(declaratorPath) { | ||
return Replacement.swaps( | ||
declaratorPath.get('id'), | ||
self.reference(mod, declaratorPath.get('id').node) | ||
); | ||
} | ||
BundleFormatter.prototype.defaultExport = function(mod, declaration) { | ||
return b.variableDeclaration( | ||
'var', | ||
[b.variableDeclarator( | ||
this.reference(mod, 'default'), | ||
declaration | ||
)] | ||
); | ||
@@ -103,94 +97,2 @@ }; | ||
/** | ||
* Rename the top-level function declaration to a unique name. | ||
* | ||
* function foo() {} | ||
* | ||
* Becomes e.g. | ||
* | ||
* function mod$$foo() {} | ||
* | ||
* @param {Module} mod | ||
* @param {ast-types.NodePath} nodePath | ||
* @return {?Replacement} | ||
*/ | ||
BundleFormatter.prototype.processFunctionDeclaration = function(mod, nodePath) { | ||
return Replacement.swaps( | ||
nodePath.get('id'), | ||
this.reference(mod, nodePath.node.id) | ||
); | ||
}; | ||
/** | ||
* Replaces non-default exports. Exported bindings do not need to be | ||
* replaced with actual statements since they only control how local references | ||
* are renamed within the module. | ||
* | ||
* @param {Module} mod | ||
* @param {ast-types.NodePath} nodePath | ||
* @return {?Replacement} | ||
*/ | ||
BundleFormatter.prototype.processExportDeclaration = function(mod, nodePath) { | ||
var node = nodePath.node; | ||
if (n.FunctionDeclaration.check(node.declaration)) { | ||
return Replacement.swaps( | ||
// drop `export` | ||
nodePath, node.declaration | ||
).and( | ||
// transform the function | ||
this.processFunctionDeclaration(mod, nodePath.get('declaration')) | ||
); | ||
} else if (n.VariableDeclaration.check(node.declaration)) { | ||
return Replacement.swaps( | ||
// drop `export` | ||
nodePath, node.declaration | ||
).and( | ||
// transform the variables | ||
this.processVariableDeclaration(mod, nodePath.get('declaration')) | ||
); | ||
} else if (node.declaration) { | ||
throw new Error( | ||
'unexpected export style, found a declaration of type: ' + | ||
node.declaration.type | ||
); | ||
} else { | ||
/** | ||
* This node looks like this: | ||
* | ||
* export { foo, bar }; | ||
* | ||
* Which means that it has no value in the generated code as its only | ||
* function is to control how imports are rewritten. | ||
*/ | ||
return Replacement.removes(nodePath); | ||
} | ||
}; | ||
/** | ||
* Since import declarations only control how we rewrite references we can just | ||
* remove them -- they don't turn into any actual statements. | ||
* | ||
* @param {Module} mod | ||
* @param {ast-types.NodePath} nodePath | ||
* @return {?Replacement} | ||
*/ | ||
BundleFormatter.prototype.processImportDeclaration = function(mod, nodePath) { | ||
return Replacement.removes(nodePath); | ||
}; | ||
/** | ||
* Since named export reassignment is just a local variable, we can ignore it. | ||
* e.g. | ||
* | ||
* export var foo = 1; | ||
* foo = 2; | ||
* | ||
* @param {Module} mod | ||
* @param {ast-types.NodePath} nodePath | ||
* @return {?Replacement} | ||
*/ | ||
BundleFormatter.prototype.processExportReassignment = function (mod, nodePath) { | ||
return null; | ||
}; | ||
/** | ||
* Get a reference to the original exported value referenced in `mod` at | ||
@@ -201,11 +103,15 @@ * `referencePath`. If the given reference path does not correspond to an | ||
* | ||
* ```js | ||
* // a.js | ||
* var value = 99; | ||
* console.log(value); | ||
* ``` | ||
* | ||
* If `value` was exported then we would need to rewrite it: | ||
* | ||
* ```js | ||
* // a.js | ||
* export var value = 3; | ||
* console.log(value); | ||
* ``` | ||
* | ||
@@ -215,2 +121,3 @@ * In this case we re-write both `value` references to something like | ||
* | ||
* ```js | ||
* // a.js | ||
@@ -226,2 +133,3 @@ * export var value = 11; | ||
* console.log(value); | ||
* ``` | ||
* | ||
@@ -235,5 +143,3 @@ * The `value` reference in a.js will be rewritten as something like `a$$value` | ||
* | ||
* @param {Module} mod | ||
* @param {ast-types.NodePath} referencePath | ||
* @return {ast-types.Expression} | ||
* @override | ||
*/ | ||
@@ -252,9 +158,6 @@ BundleFormatter.prototype.exportedReference = function(mod, referencePath) { | ||
* Get a reference to the original exported value referenced in `mod` at | ||
* `referencePath`. This is very similar to {#exportedReference} in its | ||
* approach. | ||
* `referencePath`. This is very similar to BundleFormatter#exportedReference | ||
* in its approach. | ||
* | ||
* @param {Module} mod | ||
* @param {ast-types.NodePath} referencePath | ||
* @return {ast-types.Expression} | ||
* @see {#exportedReference} | ||
* @override | ||
*/ | ||
@@ -276,4 +179,4 @@ BundleFormatter.prototype.importedReference = function(mod, referencePath) { | ||
* @param {Module} mod | ||
* @param {ast-types.NodePath} referencePath | ||
* @returns {?ast-types.Node} | ||
* @param {NodePath} referencePath | ||
* @return {?Node} | ||
*/ | ||
@@ -290,31 +193,80 @@ BundleFormatter.prototype.localReference = function(mod, referencePath) { | ||
/** | ||
* Convert a list of ordered modules into a list of files. | ||
* Replaces non-default exports. Exported bindings do not need to be | ||
* replaced with actual statements since they only control how local references | ||
* are renamed within the module. | ||
* | ||
* @param {Array.<Module>} modules Modules in execution order. | ||
* @return {Array.<ast-types.File} | ||
* @override | ||
*/ | ||
BundleFormatter.prototype.build = function(modules) { | ||
modules = sort(modules); | ||
return [b.file(b.program([b.expressionStatement(IFFE( | ||
b.expressionStatement(b.literal('use strict')), | ||
modules.length === 1 ? | ||
modules[0].ast.program.body : | ||
modules.reduce(function(statements, mod) { | ||
return statements.concat(mod.ast.program.body); | ||
}, []) | ||
))]))]; | ||
BundleFormatter.prototype.processExportDeclaration = function(mod, nodePath) { | ||
var node = nodePath.node; | ||
if (n.FunctionDeclaration.check(node.declaration)) { | ||
return Replacement.swaps( | ||
// drop `export` | ||
nodePath, node.declaration | ||
).and( | ||
// transform the function | ||
this.processFunctionDeclaration(mod, nodePath.get('declaration')) | ||
); | ||
} else if (n.VariableDeclaration.check(node.declaration)) { | ||
return Replacement.swaps( | ||
// drop `export` | ||
nodePath, node.declaration | ||
).and( | ||
// transform the variables | ||
this.processVariableDeclaration(mod, nodePath.get('declaration')) | ||
); | ||
} else if (node.declaration) { | ||
throw new Error( | ||
'unexpected export style, found a declaration of type: ' + | ||
node.declaration.type | ||
); | ||
} else { | ||
/** | ||
* This node looks like this: | ||
* | ||
* ```js | ||
* export { foo, bar }; | ||
* ``` | ||
* | ||
* Which means that it has no value in the generated code as its only | ||
* function is to control how imports are rewritten. | ||
*/ | ||
return Replacement.removes(nodePath); | ||
} | ||
}; | ||
/** | ||
* @param {Module} mod | ||
* @param {ast-types.Expression} declaration | ||
* @return {ast-types.Statement} | ||
* Since named export reassignment is just a local variable, we can ignore it. | ||
* e.g. | ||
* | ||
* ```js | ||
* export var foo = 1; | ||
* foo = 2; | ||
* ``` | ||
* | ||
* @override | ||
*/ | ||
BundleFormatter.prototype.defaultExport = function(mod, declaration) { | ||
return b.variableDeclaration( | ||
'var', | ||
[b.variableDeclarator( | ||
this.reference(mod, 'default'), | ||
declaration | ||
)] | ||
BundleFormatter.prototype.processExportReassignment = function(mod, nodePath) { | ||
return null; | ||
}; | ||
/** | ||
* Rename the top-level function declaration to a unique name. | ||
* | ||
* ```js | ||
* function foo() {} | ||
* ``` | ||
* | ||
* Becomes e.g. | ||
* | ||
* ```js | ||
* function mod$$foo() {} | ||
* ``` | ||
* | ||
* @override | ||
*/ | ||
BundleFormatter.prototype.processFunctionDeclaration = function(mod, nodePath) { | ||
return Replacement.swaps( | ||
nodePath.get('id'), | ||
this.reference(mod, nodePath.node.id) | ||
); | ||
@@ -324,5 +276,49 @@ }; | ||
/** | ||
* Since import declarations only control how we rewrite references we can just | ||
* remove them -- they don't turn into any actual statements. | ||
* | ||
* @override | ||
*/ | ||
BundleFormatter.prototype.processImportDeclaration = function(mod, nodePath) { | ||
return Replacement.removes(nodePath); | ||
}; | ||
/** | ||
* Process a variable declaration found at the top level of the module. We need | ||
* to ensure that exported variables are rewritten appropriately, so we may | ||
* need to rewrite some or all of this variable declaration. For example: | ||
* | ||
* ```js | ||
* var a = 1, b, c = 3; | ||
* ... | ||
* export { a, b }; | ||
* ``` | ||
* | ||
* We turn those being exported into assignments as needed, e.g. | ||
* | ||
* ```js | ||
* var c = 3; | ||
* mod$$a = 1; | ||
* ``` | ||
* | ||
* @override | ||
*/ | ||
BundleFormatter.prototype.processVariableDeclaration = function(mod, nodePath) { | ||
var self = this; | ||
return Replacement.map( | ||
nodePath.get('declarations'), | ||
function(declaratorPath) { | ||
return Replacement.swaps( | ||
declaratorPath.get('id'), | ||
self.reference(mod, declaratorPath.get('id').node) | ||
); | ||
} | ||
); | ||
}; | ||
/** | ||
* Returns an expression which globally references the export named by | ||
* `identifier` for the given module `mod`. For example: | ||
* | ||
* ```js | ||
* // rsvp/defer.js, export default | ||
@@ -333,6 +329,8 @@ * rsvp$defer$$default | ||
* rsvp$utils$$isFunction | ||
* ``` | ||
* | ||
* @param {Module} mod | ||
* @param {ast-types.Identifier|string} identifier | ||
* @return {ast-types.Identifier} | ||
* @param {Identifier|string} identifier | ||
* @return {Identifier} | ||
* @private | ||
*/ | ||
@@ -339,0 +337,0 @@ BundleFormatter.prototype.reference = function(mod, identifier) { |
/* jshint node:true, undef:true, unused:true */ | ||
var assert = require('assert'); | ||
var recast = require('recast'); | ||
@@ -10,3 +9,5 @@ var types = recast.types; | ||
var extend = require('../utils').extend; | ||
var Replacement = require('../replacement'); | ||
var Formatter = require('./formatter'); | ||
@@ -19,23 +20,108 @@ /** | ||
*/ | ||
function CommonJSFormatter() {} | ||
function CommonJSFormatter() { | ||
Formatter.call(this); | ||
} | ||
extend(CommonJSFormatter, Formatter); | ||
/** | ||
* Returns an expression which globally references the export named by | ||
* `identifier` for the given module `mod`. For example: | ||
* Convert a list of ordered modules into a list of files. | ||
* | ||
* // rsvp/defer.js, export default | ||
* rsvp$defer$$.default | ||
* | ||
* // rsvp/utils.js, export function isFunction | ||
* rsvp$utils$$.isFunction | ||
* | ||
* @param {Module[]} modules Modules in execution order. | ||
* @return {File[]} | ||
*/ | ||
CommonJSFormatter.prototype.build = function(modules) { | ||
var self = this; | ||
return modules.map(function(mod) { | ||
var body = mod.ast.program.body; | ||
body.unshift( | ||
b.expressionStatement(b.literal('use strict')), | ||
self.buildExports(mod), | ||
self.buildRequires(mod) | ||
); | ||
mod.ast.filename = mod.relativePath; | ||
return mod.ast; | ||
}); | ||
}; | ||
/** | ||
* @private | ||
* @param {Module} mod | ||
* @param {ast-types.Identifier} identifier | ||
* @return {ast-types.MemberExpression} | ||
* @return {Statement} | ||
*/ | ||
CommonJSFormatter.prototype.reference = function(mod, identifier) { | ||
return b.memberExpression( | ||
b.identifier(mod.id), | ||
n.Identifier.check(identifier) ? identifier : b.identifier(identifier), | ||
false | ||
CommonJSFormatter.prototype.buildExports = function(mod) { | ||
var self = this; | ||
var properties = []; | ||
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 | ||
); | ||
var from = | ||
!specifier.from ? | ||
self.defaultExportReference(mod) : | ||
specifier.importSpecifier ? | ||
self.reference( | ||
specifier.importSpecifier.declaration.source, | ||
specifier.importSpecifier.from | ||
) : | ||
specifier.declaration.source ? | ||
self.reference( | ||
specifier.declaration.source, | ||
specifier.name | ||
) : | ||
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( | ||
b.memberExpression( | ||
b.identifier('Object'), | ||
b.identifier('defineProperties'), | ||
false | ||
), | ||
[ | ||
exportObject, | ||
b.objectExpression(properties) | ||
] | ||
); | ||
} | ||
return b.expressionStatement( | ||
b.callExpression( | ||
b.memberExpression( | ||
b.identifier('Object'), | ||
b.identifier('seal'), | ||
false | ||
), | ||
[exportObject] | ||
) | ||
); | ||
@@ -45,25 +131,70 @@ }; | ||
/** | ||
* Process a variable declaration found at the top level of the module. Since | ||
* we do not need to rewrite exported variables, we can leave variable | ||
* declarations alone. | ||
* Build a series of requires based on the imports (and exports with sources) | ||
* in the given module. | ||
* | ||
* @private | ||
* @param {Module} mod | ||
* @param {ast-types.NodePath} nodePath | ||
* @return {?Array.<ast-types.Node>} | ||
* @return {VariableDeclaration} | ||
*/ | ||
CommonJSFormatter.prototype.processVariableDeclaration = function(/* mod, nodePath */) { | ||
return null; | ||
CommonJSFormatter.prototype.buildRequires = function(mod) { | ||
var declarators = []; | ||
var requiredModules = []; | ||
[mod.imports, mod.exports].forEach(function(declarations) { | ||
declarations.modules.forEach(function(sourceModule) { | ||
if (requiredModules.indexOf(sourceModule) >= 0) { | ||
return; | ||
} | ||
requiredModules.push(sourceModule); | ||
var matchingDeclaration; | ||
declarations.declarations.some(function(declaration) { | ||
if (declaration.source === sourceModule) { | ||
matchingDeclaration = declaration; | ||
return true; | ||
} | ||
}); | ||
assert.ok( | ||
matchingDeclaration, | ||
'no matching declaration for source module: ' + sourceModule.relativePath | ||
); | ||
// `(import|export) { ... } from 'math'` -> `math$$ = require('math')` | ||
declarators.push(b.variableDeclarator( | ||
b.identifier(sourceModule.id), | ||
b.callExpression( | ||
b.identifier('require'), | ||
[b.literal(matchingDeclaration.sourcePath)] | ||
) | ||
)); | ||
}); | ||
}); | ||
if (declarators.length > 0) { | ||
return b.variableDeclaration('var', declarators); | ||
} else { | ||
return b.emptyStatement(); | ||
} | ||
}; | ||
/** | ||
* Process a variable declaration found at the top level of the module. Since | ||
* we do not need to rewrite exported functions, we can leave function | ||
* declarations alone. | ||
* @override | ||
*/ | ||
CommonJSFormatter.prototype.defaultExport = function(mod, declaration) { | ||
return b.variableDeclaration( | ||
'var', | ||
[b.variableDeclarator(this.defaultExportReference(mod), declaration)] | ||
); | ||
}; | ||
/** | ||
* Gets a reference to the default export of this module. | ||
* | ||
* @param {Module} mod | ||
* @param {ast-types.NodePath} nodePath | ||
* @returns {Array.<ast-types.Node>} | ||
* @return {Node} | ||
* @private | ||
*/ | ||
CommonJSFormatter.prototype.processFunctionDeclaration = function(mod, nodePath) { | ||
return null; | ||
CommonJSFormatter.prototype.defaultExportReference = function(mod) { | ||
return b.identifier(mod.id + 'default'); | ||
}; | ||
@@ -79,7 +210,5 @@ | ||
* | ||
* @param {Module} mod | ||
* @param {ast-types.NodePath} referencePath | ||
* @return {ast-types.Expression} | ||
* @override | ||
*/ | ||
CommonJSFormatter.prototype.exportedReference = function(/* mod, referencePath */) { | ||
CommonJSFormatter.prototype.exportedReference = function(mod, referencePath) { | ||
return null; | ||
@@ -104,5 +233,3 @@ }; | ||
* | ||
* @param {Module} mod | ||
* @param {ast-types.NodePath} referencePath | ||
* @return {?ast-types.Expression} | ||
* @override | ||
*/ | ||
@@ -125,5 +252,3 @@ CommonJSFormatter.prototype.importedReference = function(mod, referencePath) { | ||
* | ||
* @param {Module} mod | ||
* @param {ast-types.NodePath} referencePath | ||
* @returns {?ast-types.Node} | ||
* @override | ||
*/ | ||
@@ -135,24 +260,2 @@ CommonJSFormatter.prototype.localReference = function(mod, referencePath) { | ||
/** | ||
* @param {Module} mod | ||
* @param {ast-types.Expression} declaration | ||
* @return {ast-types.Statement} | ||
*/ | ||
CommonJSFormatter.prototype.defaultExport = function(mod, declaration) { | ||
return b.variableDeclaration( | ||
'var', | ||
[b.variableDeclarator(this.defaultExportReference(mod), declaration)] | ||
); | ||
}; | ||
/** | ||
* Gets a reference to the default export of this module. | ||
* | ||
* @param {Module} mod | ||
* @returns {ast-types.Node} | ||
*/ | ||
CommonJSFormatter.prototype.defaultExportReference = function(mod) { | ||
return b.identifier(mod.id + 'default'); | ||
}; | ||
/** | ||
* Replaces non-default exports. For declarations we simply remove the `export` | ||
@@ -166,5 +269,3 @@ * keyword. For export declarations that just specify bindings, e.g. | ||
* | ||
* @param {Module} mod | ||
* @param {ast-types.NodePath} nodePath | ||
* @return {?Replacement} | ||
* @override | ||
*/ | ||
@@ -188,25 +289,27 @@ CommonJSFormatter.prototype.processExportDeclaration = function(mod, nodePath) { | ||
/** | ||
* Since import declarations only control how we rewrite references we can just | ||
* remove them -- they don't turn into any actual statements. | ||
* Since named export reassignment is just a local variable, we can ignore it. | ||
* e.g. | ||
* | ||
* ```js | ||
* export var foo = 1; | ||
* foo = 2; | ||
* foo++; | ||
* ``` | ||
* | ||
* @param {Module} mod | ||
* @param {ast-types.NodePath} nodePath | ||
* @param {NodePath} nodePath | ||
* @return {?Replacement} | ||
*/ | ||
CommonJSFormatter.prototype.processImportDeclaration = function(mod, nodePath) { | ||
return Replacement.removes(nodePath); | ||
CommonJSFormatter.prototype.processExportReassignment = function(mod, nodePath) { | ||
return null; | ||
}; | ||
/** | ||
* Since named export reassignment is just a local variable, we can ignore it. | ||
* e.g. | ||
* Process a variable declaration found at the top level of the module. Since | ||
* we do not need to rewrite exported functions, we can leave function | ||
* declarations alone. | ||
* | ||
* export var foo = 1; | ||
* foo = 2; | ||
* | ||
* @param {Module} mod | ||
* @param {ast-types.NodePath} nodePath | ||
* @return {?Replacement} | ||
* @override | ||
*/ | ||
CommonJSFormatter.prototype.processExportReassignment = function (mod, nodePath) { | ||
CommonJSFormatter.prototype.processFunctionDeclaration = function(mod, nodePath) { | ||
return null; | ||
@@ -216,152 +319,42 @@ }; | ||
/** | ||
* Convert a list of ordered modules into a list of files. | ||
* Since import declarations only control how we rewrite references we can just | ||
* remove them -- they don't turn into any actual statements. | ||
* | ||
* @param {Array.<Module>} modules Modules in execution order. | ||
* @return {Array.<ast-types.File} | ||
* @override | ||
*/ | ||
CommonJSFormatter.prototype.build = function(modules) { | ||
var self = this; | ||
return modules.map(function(mod) { | ||
var body = mod.ast.program.body; | ||
body.unshift( | ||
b.expressionStatement(b.literal('use strict')), | ||
self.buildExports(mod), | ||
self.buildRequires(mod) | ||
); | ||
mod.ast.filename = mod.relativePath; | ||
return mod.ast; | ||
}); | ||
CommonJSFormatter.prototype.processImportDeclaration = function(mod, nodePath) { | ||
return Replacement.removes(nodePath); | ||
}; | ||
/** | ||
* Build a series of requires based on the imports (and exports with sources) | ||
* in the given module. | ||
* Process a variable declaration found at the top level of the module. Since | ||
* we do not need to rewrite exported variables, we can leave variable | ||
* declarations alone. | ||
* | ||
* @private | ||
* @param {Module} mod | ||
* @return {ast-types.VariableDeclaration} | ||
* @override | ||
*/ | ||
CommonJSFormatter.prototype.buildRequires = function(mod) { | ||
var declarators = []; | ||
var requiredModules = []; | ||
[mod.imports, mod.exports].forEach(function(declarations) { | ||
declarations.modules.forEach(function(sourceModule) { | ||
if (requiredModules.indexOf(sourceModule) >= 0) { | ||
return; | ||
} | ||
requiredModules.push(sourceModule); | ||
var matchingDeclaration; | ||
declarations.declarations.some(function(declaration) { | ||
if (declaration.source === sourceModule) { | ||
matchingDeclaration = declaration; | ||
return true; | ||
} | ||
}); | ||
assert.ok( | ||
matchingDeclaration, | ||
'no matching declaration for source module: ' + sourceModule.relativePath | ||
); | ||
// `(import|export) { ... } from 'math'` -> `math$$ = require('math')` | ||
declarators.push(b.variableDeclarator( | ||
b.identifier(sourceModule.id), | ||
b.callExpression( | ||
b.identifier('require'), | ||
[b.literal(matchingDeclaration.sourcePath)] | ||
) | ||
)); | ||
}); | ||
}); | ||
if (declarators.length > 0) { | ||
return b.variableDeclaration('var', declarators); | ||
} else { | ||
return b.emptyStatement(); | ||
} | ||
CommonJSFormatter.prototype.processVariableDeclaration = function(mod, nodePath) { | ||
return null; | ||
}; | ||
/** | ||
* Returns an expression which globally references the export named by | ||
* `identifier` for the given module `mod`. For example: | ||
* | ||
* // rsvp/defer.js, export default | ||
* rsvp$defer$$.default | ||
* | ||
* // rsvp/utils.js, export function isFunction | ||
* rsvp$utils$$.isFunction | ||
* | ||
* @param {Module} mod | ||
* @param {Identifier} identifier | ||
* @return {MemberExpression} | ||
* @private | ||
* @param {Module} mod | ||
* @return {ast-types.Statement} | ||
*/ | ||
CommonJSFormatter.prototype.buildExports = function(mod) { | ||
var self = this; | ||
var properties = []; | ||
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 | ||
); | ||
var from = | ||
!specifier.from ? | ||
self.defaultExportReference(mod) : | ||
specifier.importSpecifier ? | ||
self.reference( | ||
specifier.importSpecifier.declaration.source, | ||
specifier.importSpecifier.from | ||
) : | ||
specifier.declaration.source ? | ||
self.reference( | ||
specifier.declaration.source, | ||
specifier.name | ||
) : | ||
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( | ||
b.memberExpression( | ||
b.identifier('Object'), | ||
b.identifier('defineProperties'), | ||
false | ||
), | ||
[ | ||
exportObject, | ||
b.objectExpression(properties) | ||
] | ||
); | ||
} | ||
return b.expressionStatement( | ||
b.callExpression( | ||
b.memberExpression( | ||
b.identifier('Object'), | ||
b.identifier('seal'), | ||
false | ||
), | ||
[exportObject] | ||
) | ||
CommonJSFormatter.prototype.reference = function(mod, identifier) { | ||
return b.memberExpression( | ||
b.identifier(mod.id), | ||
n.Identifier.check(identifier) ? identifier : b.identifier(identifier), | ||
false | ||
); | ||
@@ -368,0 +361,0 @@ }; |
@@ -22,2 +22,3 @@ /* jshint node:true, undef:true, unused:true */ | ||
* @param {Module} mod | ||
* @extends ModuleBindingList | ||
*/ | ||
@@ -31,3 +32,3 @@ function ImportDeclarationList(mod) { | ||
* @private | ||
* @param {ast-types.Node} node | ||
* @param {AST.Node} node | ||
* @return {boolean} | ||
@@ -43,4 +44,4 @@ */ | ||
* @private | ||
* @param {ast-types.ImportDeclaration} node | ||
* @return {Import} | ||
* @param {AST.ImportDeclaration} node | ||
* @return {ImportDeclaration} | ||
*/ | ||
@@ -70,3 +71,4 @@ ImportDeclarationList.prototype.declarationForNode = function(node) { | ||
* @param {Module} mod | ||
* @param {ast-types.ImportDeclaration} node | ||
* @param {AST.ImportDeclaration} node | ||
* @extends ModuleBindingDeclaration | ||
*/ | ||
@@ -79,11 +81,3 @@ function ImportDeclaration(mod, node) { | ||
Object.defineProperties(this, { | ||
node: { | ||
value: node | ||
}, | ||
module: { | ||
value: mod | ||
} | ||
}); | ||
ModuleBindingDeclaration.call(this, mod, node); | ||
} | ||
@@ -95,3 +89,5 @@ extend(ImportDeclaration, ModuleBindingDeclaration); | ||
* | ||
* ```js | ||
* import List from 'list'; | ||
* ``` | ||
* | ||
@@ -101,3 +97,3 @@ * @constructor | ||
* @param {Module} mod | ||
* @param {ast-types.ImportDeclaration} node | ||
* @param {AST.ImportDeclaration} node | ||
*/ | ||
@@ -117,26 +113,8 @@ function DefaultImportDeclaration(mod, node) { | ||
/** | ||
* Gets a reference to the exported value from this import's module that | ||
* corresponds to the local binding created by this import with the name given | ||
* by `identfier`. | ||
* | ||
* @param {ast-types.Identifier|string} identifier | ||
* @return {ast-types.Expression} | ||
*/ | ||
DefaultImportDeclaration.prototype.getExportReference = function(identifier) { | ||
var name = n.Identifier.check(identifier) ? identifier.name : identifier; | ||
assert.equal( | ||
name, | ||
this.node.specifiers[0].id.name, | ||
'no export specifier found for `' + name + '`' | ||
); | ||
return this.module.getExportReference('default'); | ||
}; | ||
/** | ||
* Contains a list of specifier name information for this import. | ||
* | ||
* @type {Array.<ImportSpecifier>} | ||
* @property specifiers | ||
* @type {ImportSpecifier[]} | ||
* @name DefaultImportDeclaration#specifiers | ||
*/ | ||
memo(DefaultImportDeclaration.prototype, 'specifiers', function() { | ||
memo(DefaultImportDeclaration.prototype, 'specifiers', /** @this DefaultImportDeclaration */function() { | ||
var specifier = new ImportSpecifier(this, this.node.specifiers[0]); | ||
@@ -151,3 +129,5 @@ specifier.from = 'default'; | ||
* | ||
* ```js | ||
* import { sin, cos } from 'math'; | ||
* ``` | ||
* | ||
@@ -157,3 +137,3 @@ * @constructor | ||
* @param {Module} mod | ||
* @param {ast-types.ImportDeclaration} node | ||
* @param {AST.ImportDeclaration} node | ||
*/ | ||
@@ -169,6 +149,6 @@ function NamedImportDeclaration(mod, node) { | ||
* | ||
* @type {Array.<ImportSpecifier>} | ||
* @property specifiers | ||
* @type {ImportSpecifier[]} | ||
* @name NamedImportDeclaration#specifiers | ||
*/ | ||
memo(NamedImportDeclaration.prototype, 'specifiers', function() { | ||
memo(NamedImportDeclaration.prototype, 'specifiers', /** @this NamedImportDeclaration */function() { | ||
var self = this; | ||
@@ -188,3 +168,3 @@ return this.node.specifiers.map(function(specifier) { | ||
* @param {Module} mod | ||
* @param {ast-types.ImportDeclaration} node | ||
* @param {AST.ImportDeclaration} node | ||
*/ | ||
@@ -205,6 +185,6 @@ function BareImportDeclaration(mod, node) { | ||
* | ||
* @type {Array.<ImportSpecifier>} | ||
* @property specifiers | ||
* @type {ImportSpecifier[]} | ||
* @name BareImportDeclaration#specifiers | ||
*/ | ||
memo(BareImportDeclaration.prototype, 'specifiers', function() { | ||
memo(BareImportDeclaration.prototype, 'specifiers', /** @this BareImportDeclaration */function() { | ||
return []; | ||
@@ -214,2 +194,11 @@ }); | ||
/** | ||
* Represents an import specifier. The "a" and "b as c" are both import | ||
* specifiers in the following import statement. | ||
* | ||
* import { a, b as c } from "a"; | ||
* | ||
* @constructor | ||
* @extends ModuleBindingSpecifier | ||
* @param {ImportDeclaration} declaration | ||
* @param {AST.ImportSpecifier} node | ||
*/ | ||
@@ -225,3 +214,7 @@ function ImportSpecifier(declaration, node) { | ||
memo(ImportSpecifier.prototype, 'exportSpecifier', function() { | ||
/** | ||
* @type {ExportSpecifier} | ||
* @name ImportSpecifier#exportSpecifier | ||
*/ | ||
memo(ImportSpecifier.prototype, 'exportSpecifier', /** @this ImportSpecifier */function() { | ||
var source = this.declaration.source; | ||
@@ -228,0 +221,0 @@ assert.ok(source, 'import specifiers must have a valid source'); |
@@ -6,2 +6,3 @@ /* jshint node:true, undef:true, unused:true */ | ||
var formatters = require('./formatters'); | ||
var Module = require('./module'); | ||
@@ -11,1 +12,2 @@ exports.FileResolver = FileResolver; | ||
exports.formatters = formatters; | ||
exports.Module = Module; |
@@ -20,3 +20,3 @@ /* jshint node:true, undef:true, unused:true */ | ||
* @param {Module} mod | ||
* @param {ast-types.ImportDeclaration|ast-types.ExportDeclaration} node | ||
* @param {AST.ImportDeclaration|AST.ExportDeclaration} node | ||
*/ | ||
@@ -30,2 +30,6 @@ function ModuleBindingDeclaration(mod, node) { | ||
Object.defineProperties(this, { | ||
/** | ||
* @name ModuleBindingDeclaration#node | ||
* @type {AST.ImportDeclaration|AST.ExportDeclaration} | ||
*/ | ||
node: { | ||
@@ -35,2 +39,6 @@ value: node | ||
/** | ||
* @name ModuleBindingDeclaration#module | ||
* @type {Module} | ||
*/ | ||
module: { | ||
@@ -46,3 +54,2 @@ value: mod | ||
* | ||
* @private | ||
* @param {string} name | ||
@@ -65,4 +72,3 @@ * @return {?ModuleBindingSpecifier} | ||
/** | ||
* @private | ||
* @param {ast-types.Identifier} identifier | ||
* @param {AST.Identifier} identifier | ||
* @return {?ModuleBindingSpecifier} | ||
@@ -81,4 +87,17 @@ */ | ||
memo(ModuleBindingDeclaration.prototype, 'sourcePath', function() { | ||
return this.node.source && this.node.source.value; | ||
/** | ||
* Gets the raw path of the `from` part of the declaration, if present. For | ||
* example: | ||
* | ||
* ```js | ||
* import { map } from "array"; | ||
* ``` | ||
* | ||
* The source path for the above declaration is "array". | ||
* | ||
* @type {?string} | ||
* @name ModuleBindingDeclaration#sourcePath | ||
*/ | ||
memo(ModuleBindingDeclaration.prototype, 'sourcePath', /** @this ModuleBindingDeclaration */function() { | ||
return this.node.source ? this.node.source.value : null; | ||
}); | ||
@@ -90,5 +109,5 @@ | ||
* @type {Module} | ||
* @property source | ||
* @name ModuleBindingDeclaration#source | ||
*/ | ||
memo(ModuleBindingDeclaration.prototype, 'source', function() { | ||
memo(ModuleBindingDeclaration.prototype, 'source', /** @this ModuleBindingDeclaration */function() { | ||
return this.sourcePath ? this.module.getModule(this.sourcePath) : null; | ||
@@ -98,8 +117,8 @@ }); | ||
/** | ||
* Gets the module scope. | ||
* Gets the containing module's scope. | ||
* | ||
* @type {ast-types.Scope} | ||
* @property scope | ||
* @type {Scope} | ||
* @name ModuleBindingDeclaration#moduleScope | ||
*/ | ||
memo(ModuleBindingDeclaration.prototype, 'moduleScope', function() { | ||
memo(ModuleBindingDeclaration.prototype, 'moduleScope', /** @this ModuleBindingDeclaration */function() { | ||
return this.module.scope; | ||
@@ -118,3 +137,3 @@ }); | ||
/** | ||
* @alias {#inspect} | ||
* @see ModuleBindingDeclaration#inspect | ||
*/ | ||
@@ -121,0 +140,0 @@ ModuleBindingDeclaration.prototype.toString = ModuleBindingDeclaration.prototype.inspect; |
@@ -19,2 +19,7 @@ /* jshint node:true, undef:true, unused:true */ | ||
Object.defineProperties(this, { | ||
/** | ||
* @name ModuleBindingList#_nodes | ||
* @type {AST.ImportDeclaration[]|AST.ExportDeclaration[]} | ||
* @private | ||
*/ | ||
_nodes: { | ||
@@ -24,2 +29,6 @@ value: [] | ||
/** | ||
* @name ModuleBindingList#module | ||
* @type {Module} | ||
*/ | ||
module: { | ||
@@ -35,3 +44,3 @@ value: mod | ||
* | ||
* @param {ast-types.Program} program | ||
* @param {AST.Program} program | ||
*/ | ||
@@ -51,3 +60,3 @@ ModuleBindingList.prototype.readProgram = function(program) { | ||
* @private | ||
* @param {ast-types.ImportDeclaration|ast-types.ExportDeclaration} node | ||
* @param {AST.ImportDeclaration|AST.ExportDeclaration} node | ||
*/ | ||
@@ -69,8 +78,8 @@ ModuleBindingList.prototype.addDeclaration = function(node) { | ||
/** | ||
* Gets the module scope. | ||
* Gets the associated module's scope. | ||
* | ||
* @type {ast-types.Scope} | ||
* @property scope | ||
* @type {Scope} | ||
* @name ModuleBindingList#moduleScope | ||
*/ | ||
memo(ModuleBindingList.prototype, 'moduleScope', function() { | ||
memo(ModuleBindingList.prototype, 'moduleScope', /** @this ModuleBindingList */function() { | ||
return this.module.scope; | ||
@@ -82,6 +91,6 @@ }); | ||
* | ||
* @type {Array.<Module>} | ||
* @property modules | ||
* @type {Module[]} | ||
* @name ModuleBindingList#modules | ||
*/ | ||
memo(ModuleBindingList.prototype, 'modules', function() { | ||
memo(ModuleBindingList.prototype, 'modules', /** @this ModuleBindingList */function() { | ||
var modules = []; | ||
@@ -116,4 +125,7 @@ | ||
/** | ||
* Finds the specifier whose identifier is the given identifier, if one exists. | ||
* Otherwise `null` is returned. | ||
* | ||
* @private | ||
* @param {ast-types.Identifier} identifier | ||
* @param {AST.Identifier} identifier | ||
* @return {?ModuleBindingSpecifier} | ||
@@ -133,3 +145,3 @@ */ | ||
/** | ||
* @param {ast-types.NodePath} referencePath | ||
* @param {NodePath} referencePath | ||
* @return {?ModuleBindingSpecifier} | ||
@@ -207,3 +219,3 @@ */ | ||
/** | ||
* @alias {#inspect} | ||
* @see ModuleBindingList#inspect | ||
*/ | ||
@@ -215,6 +227,6 @@ ModuleBindingList.prototype.toString = ModuleBindingList.prototype.inspect; | ||
* | ||
* @type {Array.<(Import|Export)>} | ||
* @property declarations | ||
* @type {(ImportDeclaration[]|ExportDeclaration[])} | ||
* @name ModuleBindingList#declarations | ||
*/ | ||
memo(ModuleBindingList.prototype, 'declarations', function() { | ||
memo(ModuleBindingList.prototype, 'declarations', /** @this ModuleBindingList */function() { | ||
var self = this; | ||
@@ -231,6 +243,6 @@ | ||
* | ||
* @type {Array.<string>} | ||
* @property names | ||
* @type {string[]} | ||
* @name ModuleBindingList#names | ||
*/ | ||
memo(ModuleBindingList.prototype, 'names', function() { | ||
memo(ModuleBindingList.prototype, 'names', /** @this ModuleBindingList */function() { | ||
return this.declarations.reduce(function(names, decl) { | ||
@@ -237,0 +249,0 @@ return names.concat(decl.specifiers.map(function(specifier) { |
@@ -14,5 +14,15 @@ /* jshint node:true, undef:true, unused:true */ | ||
/** | ||
* A module binding specifier provides the shared functionality of | ||
* ImportSpecifiers and ExportSpecifiers in the ES6 spec. | ||
* | ||
* @constructor | ||
* @param {ModuleBindingDeclaration} declaration | ||
* @param {AST.NamedSpecifier} node | ||
*/ | ||
function ModuleBindingSpecifier(declaration, node) { | ||
Object.defineProperties(this, { | ||
/** | ||
* @name ModuleBindingSpecifier#declaration | ||
* @type {ModuleBindingDeclaration} | ||
*/ | ||
declaration: { | ||
@@ -22,2 +32,6 @@ value: declaration | ||
/** | ||
* @name ModuleBindingSpecifier#node | ||
* @type {AST.NamedSpecifier} | ||
*/ | ||
node: { | ||
@@ -30,4 +44,8 @@ value: node | ||
/** | ||
* Gets the module this specifier is declared in. | ||
* | ||
* @type Module | ||
* @name ModuleBindingSpecifier#module | ||
*/ | ||
memo(ModuleBindingSpecifier.prototype, 'module', function() { | ||
memo(ModuleBindingSpecifier.prototype, 'module', /** @this ModuleBindingSpecifier */function() { | ||
return this.declaration.module; | ||
@@ -37,4 +55,8 @@ }); | ||
/** | ||
* Gets the scope at the top level of the module. | ||
* | ||
* @type {Scope} | ||
* @name ModuleBindingSpecifier#moduleScope | ||
*/ | ||
memo(ModuleBindingSpecifier.prototype, 'moduleScope', function() { | ||
memo(ModuleBindingSpecifier.prototype, 'moduleScope', /** @this ModuleBindingSpecifier */function() { | ||
return this.declaration.moduleScope; | ||
@@ -44,4 +66,19 @@ }); | ||
/** | ||
* Gets the name of this specifier. For import specifiers this is the name of | ||
* the binding this specifier will create locally, i.e. "foo" in both of these | ||
* import statements: | ||
* | ||
* import { foo } from "util"; | ||
* import { bar as foo } from "util"; | ||
* | ||
* In export specifiers it is the name of the exported declaration or the alias | ||
* given to an internal name, i.e. "foo" in both of these export statements: | ||
* | ||
* export { bar as foo }; | ||
* export var foo = 1; | ||
* | ||
* @type {string} | ||
* @name ModuleBindingSpecifier#name | ||
*/ | ||
memo(ModuleBindingSpecifier.prototype, 'name', function() { | ||
memo(ModuleBindingSpecifier.prototype, 'name', /** @this ModuleBindingSpecifier */function() { | ||
return this.identifier.name; | ||
@@ -51,4 +88,18 @@ }); | ||
/** | ||
* Gets the name of the identifier this specifier comes from as distinct from | ||
* `name`. This value will only be set if the local name and the | ||
* imported/exported name differ, i.e. it will be "foo" in these statements: | ||
* | ||
* import { foo as bar } from "util"; | ||
* export { foo as bar }; | ||
* | ||
* And it will be undefined in these statements: | ||
* | ||
* import { foo } from "util"; | ||
* export { foo }; | ||
* | ||
* @type {string} | ||
* @name ModuleBindingSpecifier#from | ||
*/ | ||
memo(ModuleBindingSpecifier.prototype, 'from', function() { | ||
memo(ModuleBindingSpecifier.prototype, 'from', /** @this ModuleBindingSpecifier */function() { | ||
return this.node.id.name; | ||
@@ -58,4 +109,14 @@ }); | ||
/** | ||
* Gets the node that gives this specifier its name as it would be imported, | ||
* i.e. "foo" in these statements: | ||
* | ||
* import { foo } from "utils"; | ||
* import { bar as foo } from "utils"; | ||
* export { foo }; | ||
* export { bar as foo }; | ||
* | ||
* @type {AST.Identifier} | ||
* @name ModuleBindingSpecifier#identifier | ||
*/ | ||
memo(ModuleBindingSpecifier.prototype, 'identifier', function() { | ||
memo(ModuleBindingSpecifier.prototype, 'identifier', /** @this ModuleBindingSpecifier */function() { | ||
return this.node.name || this.node.id; | ||
@@ -65,4 +126,15 @@ }); | ||
/** | ||
* Gets the export specifier corresponding to this specifier. This can be from | ||
* either an import or export declaration, since both can have a "from" part: | ||
* | ||
* import { map } from "array"; | ||
* export { map } from "array"; | ||
* | ||
* In both of the above examples, the export specifier of `map` would be part | ||
* of the export statement in the "array" module that exports it. | ||
* | ||
* @type {?ExportSpecifier} | ||
* @name ModuleBindingSpecifier#exportSpecifier | ||
*/ | ||
memo(ModuleBindingSpecifier.prototype, 'exportSpecifier', function() { | ||
memo(ModuleBindingSpecifier.prototype, 'exportSpecifier', /** @this ModuleBindingSpecifier */function() { | ||
var source = this.declaration.source; | ||
@@ -77,3 +149,17 @@ if (source) { | ||
memo(ModuleBindingSpecifier.prototype, 'importSpecifier', function() { | ||
/** | ||
* Gets the import specifier corresponding to this specifier. This should only | ||
* happen when exporting a binding that is imported in the same module, like so: | ||
* | ||
* import { map } from "array"; | ||
* export { map }; | ||
* | ||
* The `map` export specifier has the `map` import specifier as its | ||
* `importSpecifier` property value. The `map` import specifier has no | ||
* `importSpecifier` property value. | ||
* | ||
* @type {?ImportSpecifier} | ||
* @name ModuleBindingSpecifier#importSpecifier | ||
*/ | ||
memo(ModuleBindingSpecifier.prototype, 'importSpecifier', /** @this ModuleBindingSpecifier */function() { | ||
// This may be an export from this module, so find the declaration. | ||
@@ -91,3 +177,26 @@ var localExportDeclarationInfo = this.moduleDeclaration; | ||
memo(ModuleBindingSpecifier.prototype, 'terminalExportSpecifier', function() { | ||
/** | ||
* Gets the original export value by following chains of export/import | ||
* statements. For example: | ||
* | ||
* // a.js | ||
* export var a = 1; | ||
* | ||
* // b.js | ||
* export { a } from "./a"; | ||
* | ||
* // c.js | ||
* import { a } from "./b"; | ||
* export { a }; | ||
* | ||
* // d.js | ||
* import { a } from "./c"; | ||
* | ||
* The terminal export specifier for all of these specifiers is the export in | ||
* a.js, since all of them can be traced back to that one. | ||
* | ||
* @type {ExportSpecifier} | ||
* @name ModuleBindingSpecifier#terminalExportSpecifier | ||
*/ | ||
memo(ModuleBindingSpecifier.prototype, 'terminalExportSpecifier', /** @this ModuleBindingSpecifier */function() { | ||
if (this.exportSpecifier) { | ||
@@ -117,3 +226,12 @@ // This is true for both imports and exports with a source, e.g. | ||
/** | ||
* @type {?DeclarationInfo} | ||
*/ | ||
ModuleBindingSpecifier.prototype.moduleDeclaration = null; | ||
/** | ||
* Gets a string representation of this module binding specifier suitable for | ||
* debugging. | ||
* | ||
* @return {string} | ||
*/ | ||
ModuleBindingSpecifier.prototype.inspect = function() { | ||
@@ -127,4 +245,7 @@ return '#<' + this.constructor.name + | ||
/** | ||
* @see ModuleBindingSpecifier#inspect | ||
*/ | ||
ModuleBindingSpecifier.prototype.toString = ModuleBindingSpecifier.prototype.inspect; | ||
module.exports = ModuleBindingSpecifier; |
@@ -20,4 +20,16 @@ /* jshint node:true, undef:true, unused:true */ | ||
/** | ||
* Represents a JavaScript module at a particular location on disk. | ||
* | ||
* @param {string} path | ||
* @param {string} relativePath | ||
* @param {Container} container | ||
* @constructor | ||
*/ | ||
function Module(path, relativePath, container) { | ||
Object.defineProperties(this, { | ||
/** | ||
* @type {string} | ||
* @name Module#path | ||
*/ | ||
path: { | ||
@@ -29,2 +41,6 @@ value: path, | ||
/** | ||
* @type {string} | ||
* @name Module#relativePath | ||
*/ | ||
relativePath: { | ||
@@ -36,2 +52,6 @@ value: relativePath, | ||
/** | ||
* @type {Container} | ||
* @name Module#container | ||
*/ | ||
container: { | ||
@@ -60,5 +80,5 @@ value: container, | ||
* @type {ImportDeclarationList} | ||
* @property imports | ||
* @name Module#imports | ||
*/ | ||
memo(Module.prototype, 'imports', function() { | ||
memo(Module.prototype, 'imports', /** @this Module */function() { | ||
var result = new ImportDeclarationList(this); | ||
@@ -73,5 +93,5 @@ result.readProgram(this.ast.program); | ||
* @type {ExportDeclarationList} | ||
* @property exports | ||
* @name Module#exports | ||
*/ | ||
memo(Module.prototype, 'exports', function() { | ||
memo(Module.prototype, 'exports', /** @this Module */function() { | ||
var result = new ExportDeclarationList(this); | ||
@@ -85,6 +105,6 @@ result.readProgram(this.ast.program); | ||
* | ||
* @type {ast-types.Scope} | ||
* @property scope | ||
* @type {Scope} | ||
* @name Module#scope | ||
*/ | ||
memo(Module.prototype, 'scope', function() { | ||
memo(Module.prototype, 'scope', /** @this Module */function() { | ||
return new NodePath(this.ast).get('program').get('body').scope; | ||
@@ -96,6 +116,6 @@ }); | ||
* | ||
* @type {ast-types.File} | ||
* @property ast | ||
* @type {File} | ||
* @name Module#ast | ||
*/ | ||
memo(Module.prototype, 'ast', function() { | ||
memo(Module.prototype, 'ast', /** @this Module */function() { | ||
return recast.parse( | ||
@@ -113,5 +133,5 @@ this.src, { | ||
* @type {String} | ||
* @property src | ||
* @name Module#src | ||
*/ | ||
memo(Module.prototype, 'src', function() { | ||
memo(Module.prototype, 'src', /** @this Module */function() { | ||
return fs.readFileSync(this.path).toString(); | ||
@@ -124,5 +144,5 @@ }); | ||
* @type {object} | ||
* @property options | ||
* @name Module#options | ||
*/ | ||
memo(Module.prototype, 'options', function() { | ||
memo(Module.prototype, 'options', /** @this Module */function() { | ||
return this.container.options; | ||
@@ -136,5 +156,5 @@ }); | ||
* @type {string} | ||
* @property name | ||
* @name Module#name | ||
*/ | ||
memo(Module.prototype, 'name', function() { | ||
memo(Module.prototype, 'name', /** @this Module */function() { | ||
var relativePath = this.relativePath; | ||
@@ -152,5 +172,5 @@ if (endsWith(relativePath, '.js')) { | ||
* @type {string} | ||
* @property id | ||
* @name Module#id | ||
*/ | ||
memo(Module.prototype, 'id', function() { | ||
memo(Module.prototype, 'id', /** @this Module */function() { | ||
return this.name.replace(/[^\w$_]/g, '$') + '$$'; | ||
@@ -170,38 +190,2 @@ }); | ||
/** | ||
*/ | ||
Module.prototype.getExportReference = function(identifier) { | ||
var name; | ||
if (n.Identifier.check(identifier)) { | ||
name = identifier.name; | ||
} else { | ||
name = identifier; | ||
identifier = null; | ||
} | ||
assert.equal(typeof name, 'string'); | ||
// TODO: Use constant for 'compression'. | ||
if (this.options.optimize === 'compression') { | ||
return b.identifier(this.id + name); | ||
} else { | ||
return b.memberExpression( | ||
b.identifier(this.id), | ||
identifier || b.identifier(name), | ||
false | ||
); | ||
} | ||
}; | ||
/** | ||
* Gets a reference to the original exported value corresponding to the local | ||
* binding created by this import with the name given by `identfier`. | ||
* | ||
* @param {ast-types.NodePath} referencePath | ||
* @return {ast-types.Expression} | ||
*/ | ||
Module.prototype.getBindingReference = function(referencePath) { | ||
var imp = this.imports.findImportForReference(referencePath); | ||
return imp; | ||
}; | ||
/** | ||
* Generate a descriptive string suitable for debugging. | ||
@@ -216,3 +200,3 @@ * | ||
/** | ||
* @alias {#inspect} | ||
* @see Module#inspect | ||
*/ | ||
@@ -219,0 +203,0 @@ Module.prototype.toString = Module.prototype.inspect; |
/* jshint node:true, undef:true, unused:true */ | ||
var recast = require('recast'); | ||
/** @typedef [NodePath, AST.Node[]] */ | ||
var ReplacementPair; | ||
/** | ||
@@ -8,6 +12,10 @@ * Represents a replacement of a node path with zero or more nodes. | ||
* @constructor | ||
* @param {ast-types.NodePath} nodePath | ||
* @param {Array.<ast-types.Node>} nodes | ||
* @param {NodePath=} nodePath | ||
* @param {AST.Node[]=} nodes | ||
*/ | ||
function Replacement(nodePath, nodes) { | ||
/** | ||
* @private | ||
* @type {ReplacementPair[]} | ||
*/ | ||
this.queue = []; | ||
@@ -42,4 +50,4 @@ if (nodePath && nodes) { | ||
* | ||
* @param {ast-types.NodePath} nodePath | ||
* @returns {Replacement} | ||
* @param {NodePath} nodePath | ||
* @return {Replacement} | ||
*/ | ||
@@ -54,5 +62,5 @@ Replacement.removes = function(nodePath) { | ||
* | ||
* @param {ast-types.NodePath} nodePath | ||
* @param {Array.<ast-types.Node>} nodes | ||
* @returns {Replacement} | ||
* @param {NodePath} nodePath | ||
* @param {AST.Node[]} nodes | ||
* @return {Replacement} | ||
*/ | ||
@@ -67,7 +75,7 @@ Replacement.adds = function(nodePath, nodes) { | ||
* | ||
* @param {ast-types.NodePath} nodePath | ||
* @param {ast-types.Node|Array.<ast-types.Node>} nodes | ||
* @param {NodePath} nodePath | ||
* @param {AST.Node|AST.Node[]} nodes | ||
*/ | ||
Replacement.swaps = function(nodePath, nodes) { | ||
if ({}.toString.call(nodes) !== '[object Array]') { | ||
if (!Array.isArray(nodes)) { | ||
nodes = [nodes]; | ||
@@ -74,0 +82,0 @@ } |
@@ -36,2 +36,3 @@ /* jshint node:true, undef:true, unused:true */ | ||
* | ||
* ```js | ||
* import { sin, cos } from './math'; | ||
@@ -43,2 +44,3 @@ * import fib from './math/fib'; | ||
* assert.equal(fib(1), 1); | ||
* ``` | ||
* | ||
@@ -48,7 +50,9 @@ * has its references to the imported bindings `sin`, `cos`, and `fib` | ||
* | ||
* ```js | ||
* assert.equal(math$$.sin(0), 0); | ||
* assert.equal(math$$.cos(0), 1); | ||
* assert.equal(math$fib$$.fib(1), 1); | ||
* ``` | ||
* | ||
* @param {Array.<Module>} modules | ||
* @param {Module[]} modules | ||
*/ | ||
@@ -93,4 +97,4 @@ Rewriter.prototype.rewrite = function(modules) { | ||
* | ||
* @param {ast-types.NodePath} nodePath | ||
* @returns {boolean} | ||
* @param {NodePath} nodePath | ||
* @return {boolean} | ||
* @private | ||
@@ -112,3 +116,3 @@ */ | ||
* | ||
* @param {ast-types.NodePath} nodePath | ||
* @param {NodePath} nodePath | ||
* @private | ||
@@ -133,3 +137,3 @@ */ | ||
* | ||
* @param {ast-types.NodePath} nodePath | ||
* @param {NodePath} nodePath | ||
* @private | ||
@@ -152,3 +156,3 @@ */ | ||
* | ||
* @param {ast-types.NodePath} nodePath | ||
* @param {NodePath} nodePath | ||
* @private | ||
@@ -170,3 +174,3 @@ */ | ||
* | ||
* @param {ast-types.NodePath} nodePath | ||
* @param {NodePath} nodePath | ||
* @private | ||
@@ -216,3 +220,3 @@ */ | ||
* | ||
* @param {ast-types.NodePath} nodePath | ||
* @param {NodePath} nodePath | ||
* @private | ||
@@ -234,3 +238,3 @@ */ | ||
* | ||
* @param {ast-types.NodePath} nodePath | ||
* @param {NodePath} nodePath | ||
* @private | ||
@@ -258,3 +262,3 @@ */ | ||
* @param {Module} mod | ||
* @param {ast-types.Identifier} identifierPath | ||
* @param {Identifier} identifierPath | ||
*/ | ||
@@ -291,3 +295,3 @@ Rewriter.prototype.assertImportIsNotReassigned = function(mod, identifierPath) { | ||
* @param {Module} mod | ||
* @param {ast-types.NodePath} nodePath | ||
* @param {NodePath} nodePath | ||
* @private | ||
@@ -294,0 +298,0 @@ */ |
@@ -5,4 +5,4 @@ /** | ||
* | ||
* @param {Array.<Module>} modules | ||
* @return {Array.<Module>} | ||
* @param {Module[]} modules | ||
* @return {Module[]} | ||
*/ | ||
@@ -28,3 +28,3 @@ function sort(modules) { | ||
* @param {Module} mod | ||
* @param {Array.<Module>} result | ||
* @param {Module[]} result | ||
* @param {Object.<string,string>} state | ||
@@ -31,0 +31,0 @@ */ |
{ | ||
"name": "es6-module-transpiler", | ||
"version": "0.6.2", | ||
"version": "0.7.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", |
@@ -1,2 +0,2 @@ | ||
# ES6 Module Transpiler [![Build Status](https://travis-ci.org/square/es6-module-transpiler.png)](https://travis-ci.org/square/es6-module-transpiler) | ||
# ES6 Module Transpiler [![Build Status](https://travis-ci.org/esnext/es6-module-transpiler.png)](https://travis-ci.org/esnext/es6-module-transpiler) | ||
@@ -131,17 +131,2 @@ ES6 Module Transpiler is an experimental compiler that allows you to write your | ||
#### `module` | ||
Whereas the `import` keyword imports specific identifiers from a module, | ||
the `module` keyword creates an object that contains all of a module's | ||
exports: | ||
```js | ||
module foobar from "foobar"; | ||
console.log(foobar.foo); // "foo" | ||
``` | ||
In ES6, this created object is *read-only*, so don't treat it like a mutable | ||
namespace! **NOTE:** This syntax especially may be removed or modified before | ||
ES6 is finalized! | ||
#### `import "foo";` | ||
@@ -148,0 +133,0 @@ |
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
99232
24
3017
203