Comparing version 0.1.1 to 0.3.0
11
index.js
@@ -1,3 +0,2 @@ | ||
var recast = require( 'recast' ), | ||
parse = require( './lib/parse' ), | ||
var transform = require( './lib/transform' ), | ||
generators = require( './lib/generators' ); | ||
@@ -9,4 +8,4 @@ | ||
var parsed = parse( source, options ); | ||
return generators.amd( parsed, options ); | ||
var transformed = transform( source, options, true ); | ||
return generators.amd( transformed, options ); | ||
}, | ||
@@ -17,5 +16,5 @@ | ||
var parsed = parse( source, options ); | ||
return generators.cjs( parsed, options ); | ||
var transformed = transform( source, options ); | ||
return generators.cjs( transformed, options ); | ||
} | ||
}; |
@@ -1,29 +0,53 @@ | ||
var recast = require( 'recast' ); | ||
var __export; | ||
module.exports = function ( parsed, options ) { | ||
__export = function amd ( parsed, options ) { | ||
var generated = '', imports = parsed.imports, exports = parsed.exports; | ||
var guessIndent = require( '../utils/guessIndent' ), | ||
applyIndent = require( '../utils/applyIndent' ), | ||
intro, | ||
outro, | ||
code = '', | ||
imports = parsed.imports, | ||
hasExports = parsed.hasExports, | ||
importPaths = '', | ||
importNames = '', | ||
indent; | ||
generated += 'define([' + | ||
( options.defaultOnly ? imports : imports.concat( 'exports' ) ).map( quote ).join( ',' ) + | ||
'], function (' + | ||
( options.defaultOnly ? imports.map( getImportName ) : imports.map( getImportName ).concat( 'exports' ) ).join( ',' ) + | ||
') {\n\n'; | ||
if ( imports.length ) { | ||
importPaths = '[' + | ||
( options.defaultOnly ? | ||
imports.map( getPath ) : | ||
imports.map( getPath ).concat( 'exports' ) | ||
).map( quote ).join( ',' ) + | ||
'],'; | ||
if ( options.defaultOnly ) { | ||
generated += 'var __export;\n\n'; | ||
importNames = ( | ||
options.defaultOnly ? | ||
imports.map( getImportName ) : | ||
imports.map( getImportName ).concat( 'exports' ) | ||
).join( ', ' ); | ||
} | ||
generated += recast.print( parsed.ast ).code; | ||
intro = 'define(' + importPaths + 'function (' + importNames + ') {'; | ||
if ( options.defaultOnly ) { | ||
generated += '\nreturn __export;'; | ||
if ( options.addUseStrict !== false ) { | ||
code = "'use strict';\n\n"; | ||
} | ||
generated += '\n\n});'; | ||
if ( options.defaultOnly && !parsed.alreadyReturned && hasExports ) { | ||
code += 'var __export;\n\n'; | ||
} | ||
return generated.trim(); | ||
code += parsed.body; | ||
if ( options.defaultOnly && !parsed.alreadyReturned && hasExports ) { | ||
code += '\nreturn __export;'; | ||
} | ||
outro = '});'; | ||
indent = options.indent || guessIndent( parsed.body ); | ||
return [ intro, applyIndent( code.trim(), indent ), outro ].join( '\n\n' ); | ||
}; | ||
function quote ( str ) { | ||
@@ -33,4 +57,9 @@ return "'" + str + "'"; | ||
function getImportName ( x, i ) { | ||
return '__imports_' + i; | ||
function getPath ( x ) { | ||
return x.path; | ||
} | ||
function getImportName ( x ) { | ||
return x.name; | ||
} | ||
module.exports = __export; |
@@ -1,22 +0,25 @@ | ||
var recast = require( 'recast' ); | ||
module.exports = function ( parsed, options ) { | ||
var generated = '', imports = parsed.imports, exports = parsed.exports; | ||
var result = [], | ||
code = parsed.body.trim(), | ||
imports = parsed.imports, | ||
hasExports = parsed.hasExports; | ||
generated += imports.map( function ( source, i ) { | ||
return 'var __imports_' + i + ' = require(\'' + source + '\');' | ||
}).join( '\n' ) + '\n'; | ||
if ( imports.length ) { | ||
result[0] = imports.map( function ( x ) { | ||
return 'var ' + x.name + ' = require(\'' + x.path + '\');'; | ||
}).join( '\n' ); | ||
} | ||
if ( options.defaultOnly ) { | ||
generated += 'var __export;\n\n'; | ||
if ( options.defaultOnly && !parsed.alreadyReturned && hasExports ) { | ||
code = 'var __export;\n\n' + code; | ||
} | ||
generated += recast.print( parsed.ast ).code; | ||
result.push( code ); | ||
if ( options.defaultOnly ) { | ||
generated += '\nmodule.exports = __export;'; | ||
if ( options.defaultOnly && !parsed.alreadyReturned && hasExports ) { | ||
result.push( 'module.exports = __export;' ); | ||
} | ||
return generated.trim(); | ||
}; | ||
return result.join( '\n' ); | ||
}; |
{ | ||
"name": "esperanto", | ||
"description": "An easier way to convert ES6 modules to AMD and CommonJS", | ||
"version": "0.1.1", | ||
"version": "0.3.0", | ||
"author": "Rich Harris", | ||
"dependencies": { | ||
"recast": "~0.7.0" | ||
"acorn": "~0.7.0" | ||
}, | ||
"main": "lib/esperanto.js", | ||
"devDependencies": { | ||
@@ -13,3 +14,5 @@ "promo": "~0.1.1", | ||
"rimraf": "~2.2.8", | ||
"mkdirp": "~0.5.0" | ||
"mkdirp": "~0.5.0", | ||
"gobble": "~0.1.2", | ||
"gobble-requirejs": "~0.1.0" | ||
}, | ||
@@ -16,0 +19,0 @@ "files": [ |
229
README.md
# esperanto | ||
coming soon... | ||
A better way to transpile ES6 modules to AMD and CommonJS: | ||
* Easier - no laborious configuration | ||
* Simpler - doesn't make dangerous assumptions about your project setup | ||
* Smarter - non-destructive source code transformation, no runtime Traceur dependency, and no ES5-only features | ||
* Faster - roughly 10x quicker than the alternatives | ||
## Installation | ||
```bash | ||
npm install esperanto | ||
``` | ||
## Usage | ||
```js | ||
var fs = require( 'fs' ); | ||
var esperanto = require( 'esperanto' ); | ||
fs.readFile( 'path/to/es6/modules/foo.js', function ( err, result ) { | ||
if ( err ) throw err; | ||
fs.writeFile( 'path/to/amd/output/foo.js', esperanto.toAmd( result.toString() ) ); | ||
fs.writeFile( 'path/to/cjs/output/foo.js', esperanto.toCjs( result.toString() ) ); | ||
}); | ||
``` | ||
Esperanto exposes two methods - `esperanto.toAmd()` and `esperanto.toCjs()`. Both methods take a `source` argument, which is the source code of an ES6 module, and an optional second argument, `options`. | ||
The `options` argument can have a `defaultOnly` property, which defaults to `false`. See the next section for an explanation. | ||
If you're using `esperanto.toAmd()`, you have two additional options: | ||
* `indent` (string) specifies how to indent the contents of the module. By default, esperanto will guess the correct indentation from the code, and default to a single tab character if it's not sure. | ||
* `addUseStrict` (boolean) determines whether the 'use strict' pragma should be added to the top of the module. Defaults to `true`. | ||
## When to use `defaultOnly` | ||
ES6 modules support both *default* and *named* imports and exports: | ||
```js | ||
// default | ||
import foo from 'foo'; | ||
var bar = foo.toUpperCase(); | ||
export default bar; | ||
// named | ||
import { foo, bar } from 'baz'; | ||
var bar = foo.toUpperCase(); | ||
export { qux }; | ||
``` | ||
See [jsmodules.io](http://jsmodules.io/) for an explanation of the difference. | ||
This is a good design, but [it poses problems](https://gist.github.com/domenic/4748675) for developers who want to use ES6 modules with existing codebases. An AMD representation of the first example above might look like this: | ||
```js | ||
define(['foo','exports'], function (__import_1,exports) { | ||
var foo = __import_1.default; | ||
var bar = foo.toUpperCase(); | ||
exports.default = bar; | ||
}); | ||
``` | ||
As long as `foo` is also a transpiled ES6 module with a `default` property (or an AMD module that had a `default` property added by design), that's fine - as far as *this* module is concerned. But if `foo` is an external library, that almost certainly won't be the case. | ||
On the other side of the fence, if someone were to require this ES6 module from within an AMD module, they'd have the same problem in reverse. | ||
Esperanto's `defaultOnly` option solves this problem. As long as you're not using named imports or exports, it will cause modules to behave as you (as a seasoned user of AMD or CommonJS modules) would naturally expect: | ||
```js | ||
define(['foo'],function (foo) { | ||
'use strict'; | ||
var bar = foo.toUpperCase(); | ||
return bar; | ||
}); | ||
``` | ||
## Why not use existing module transpilers? | ||
There are already a couple of ES6 module transpilers. Let's consider our example from above... | ||
```js | ||
import foo from 'foo'; | ||
var bar = foo.toUpperCase(); | ||
export default bar; | ||
``` | ||
...and see how those transpilers fare. | ||
### [bitovi/transpile](https://github.com/bitovi/transpile) | ||
```js | ||
var transpile = require( 'transpile' ); | ||
var transpiled = transpile.to({ | ||
name: 'test', | ||
source: test, // the contents of the module | ||
metadata: { format: 'es6' } | ||
}, 'amd' ); | ||
``` | ||
Pretty easy - we just tell it what input and output formats to use, and off it goes. It's a bit of a shame that you have to specify a `name` property, since AMD best practice is to use anonymous modules, but never mind. What does the result look like? | ||
```js | ||
define('sample', ['foo'], function ($__0) { | ||
'use strict'; | ||
if (!$__0 || !$__0.__esModule) | ||
$__0 = { 'default': $__0 }; | ||
var foo = $traceurRuntime.assertObject($__0).default; | ||
var bar = foo.toUpperCase(); | ||
var $__default = bar; | ||
return { | ||
get default() { | ||
return $__default; | ||
}, | ||
__esModule: true | ||
}; | ||
}); | ||
``` | ||
Wait, I'm supposed to use Traceur in production? No thanks! Oh, and I still need to support IE8, so that `get default()` is a no-go. | ||
### [esnext/es6-module-transpiler](https://github.com/esnext/es6-module-transpiler) | ||
```js | ||
var transpiler = require( 'es6-module-transpiler' ); | ||
var Container = transpiler.Container; | ||
var FileResolver = transpiler.FileResolver; | ||
var BundleFormatter = transpiler.formatters.bundle; | ||
var AmdFormatter = require( 'es6-module-transpiler-amd-formatter' ); | ||
var container = new Container({ | ||
resolvers: [new FileResolver(['./'])], | ||
formatter: new AmdFormatter() | ||
}); | ||
container.getModule('test'); | ||
container.write('output/test.js'); | ||
``` | ||
This time, rather than passing in a string, we have to point the transpiler to the files on disk. That's because it actually follows the dependency graph of your modules, so that it can (if you're not using the [AMD formatter](https://github.com/caridy/es6-module-transpiler-amd-formatter)) bundle up your source code into a single file. If it can't find a module, it errors out - so as far as I can tell, it's impossible to use es6-module-transpiler if you have external dependencies (e.g. in a `bower_components` folder) that aren't packaged as ES6 modules. | ||
Leaving aside that minor problem, what does the result look like? | ||
```js | ||
define("test", ["foo", "exports"], function(foo$$, __exports__) { | ||
"use strict"; | ||
function __es6_export__(name, value) { | ||
__exports__[name] = value; | ||
} | ||
var foo; | ||
foo = foo$$["default"]; | ||
var bar = foo.toUpperCase(); | ||
__es6_export__("default", bar); | ||
}); | ||
//# sourceMappingURL=es6-module-transpiler.js.map | ||
``` | ||
Better, certainly, and you get source maps. Though as with [transpile](https://github.com/bitovi/transpile), you're stuck with named (as opposed to anonymous) AMD modules. Frankly, though, the external dependencies thing is a dealbreaker for me. | ||
### esperanto | ||
Here's the code to generate AMD output: | ||
```js | ||
var transpiled = esperanto.toAmd( test ); | ||
``` | ||
And here's the output: | ||
```js | ||
define(['foo','exports'], function (__imports_0,exports) { | ||
var foo = __imports_0.default; | ||
var bar = foo.toUpperCase(); | ||
exports.default = bar; | ||
}); | ||
``` | ||
If we run it in `defaultOnly` mode... | ||
```js | ||
var transpiled = esperanto.toAmd( test, { defaultOnly: true }); | ||
``` | ||
```js | ||
define(['foo'],function (foo) { | ||
'use strict'; | ||
var bar = foo.toUpperCase(); | ||
return bar; | ||
}); | ||
``` | ||
No muss, no fuss. Oh, and did I mention that it's an order of magnitude faster than the alternatives? | ||
## Still to-do | ||
* A proper test suite (if you want to test esperanto, clone this module, `cd` into this folder, `npm install` dependencies, and run `node test.js`. The generated code will be written to the `output` folder) | ||
* Renaming imports (e.g. `import { unlink as rm } from 'fs'`) | ||
* Source maps? | ||
* Allow named modules, if you're into that | ||
## Credits | ||
Many thanks to [Marijn Haverbeke](http://marijnhaverbeke.nl/) for [Acorn](https://github.com/marijnh/acorn), which does all the heavy lifting. | ||
## License | ||
Copyright 2014 Rich Harris. MIT Licensed. |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
15567
9
268
231
6
1
+ Addedacorn@~0.7.0
+ Addedacorn@0.7.0(transitive)
- Removedrecast@~0.7.0
- Removedamdefine@1.0.1(transitive)
- Removedast-types@0.4.13(transitive)
- Removedcls@0.1.5(transitive)
- Removeddepd@1.0.1(transitive)
- Removedesprima-fb@6001.1001.0-dev-harmony-fb(transitive)
- Removedprivate@0.1.8(transitive)
- Removedrecast@0.7.5(transitive)
- Removedsource-map@0.1.32(transitive)