broccoli-es6-module-transpiler
Advanced tools
Comparing version 0.1.1 to 0.2.0
373
index.js
@@ -1,69 +0,342 @@ | ||
var Filter = require('broccoli-filter'); | ||
var Compiler = require('es6-module-transpiler').Compiler; | ||
'use strict'; | ||
module.exports = TranspilerFilter; | ||
var fs = require('fs'), | ||
path = require('path'), | ||
util = require('util'), | ||
mkdirp = require('mkdirp'), | ||
quickTemp = require('quick-temp'), | ||
walkSync = require('walk-sync'), | ||
helpers = require('broccoli-kitchen-sink-helpers'), | ||
Writer = require('broccoli-writer'); | ||
TranspilerFilter.prototype = Object.create(Filter.prototype);; | ||
TranspilerFilter.prototype.constructor = TranspilerFilter; | ||
var transpiler = require('es6-module-transpiler'), | ||
Container = transpiler.Container, | ||
FileResolver = transpiler.FileResolver, | ||
Module = require('es6-module-transpiler/lib/module'); | ||
function TranspilerFilter(inputTree, options) { | ||
if (!(this instanceof TranspilerFilter)) { | ||
return new TranspilerFilter(inputTree, options); | ||
} | ||
module.exports = CompileModules; | ||
options = options || {}; | ||
this.inputTree = inputTree; | ||
this.type = 'amd'; | ||
// -- CompileModules ----------------------------------------------------------- | ||
for (var key in options) { | ||
if (options.hasOwnProperty(key)) { | ||
this[key] = options[key] | ||
function CompileModules(inputTree, options) { | ||
if (!(this instanceof CompileModules)) { | ||
return new CompileModules(inputTree, options); | ||
} | ||
} | ||
if (this.moduleName === true) { | ||
this.moduleName = function(filePath) { | ||
return filePath.slice(0, -3); | ||
}; | ||
} | ||
options || (options = {}); | ||
var formatter = options.formatter; | ||
if (!formatter) { | ||
formatter = transpiler.formatters.DEFAULT; | ||
} | ||
if (typeof formatter === 'string') { | ||
formatter = transpiler.formatters[formatter]; | ||
} | ||
this.inputTree = inputTree; | ||
this.formatter = formatter; | ||
this.output = options.output || '.'; | ||
this.description = options.description; | ||
this._cache = {}; | ||
this._cacheIndex = 0; | ||
} | ||
TranspilerFilter.prototype.extensions = ['js']; | ||
TranspilerFilter.prototype.targetExtension = 'js'; | ||
util.inherits(CompileModules, Writer); | ||
TranspilerFilter.prototype.getModuleName = function(filePath) { | ||
if (typeof this.moduleName === 'function') { | ||
return this.moduleName(filePath); | ||
} else { | ||
return this.moduleName; | ||
} | ||
CompileModules.prototype.cleanup = function () { | ||
quickTemp.remove(this, 'tmpCacheDir'); | ||
Writer.prototype.cleanup.apply(this, arguments); | ||
}; | ||
TranspilerFilter.prototype.getTranspilerOptions = function(filePath) { | ||
if (typeof this.transpilerOptions === 'function') { | ||
return this.transpilerOptions(filePath); | ||
} else { | ||
return this.transpilerOptions; | ||
} | ||
CompileModules.prototype.getCacheDir = function () { | ||
return quickTemp.makeOrReuse(this, 'tmpCacheDir'); | ||
}; | ||
TranspilerFilter.prototype.processString = function (fileContents, filePath) { | ||
var name = this.getModuleName(filePath); | ||
var options = this.getTranspilerOptions(filePath); | ||
CompileModules.prototype.write = function (readTree, destDir) { | ||
return readTree(this.inputTree).then(function (srcDir) { | ||
var outputPath = path.join(destDir, this.output), | ||
modules = []; | ||
var compiler = new Compiler(fileContents, name, options); | ||
function hash(filePaths) { | ||
Array.isArray(filePaths) || (filePaths = [filePaths]); | ||
try { | ||
return compiler[transpilerMethods[this.type]](); | ||
} catch(e) { | ||
e.file = filePath; | ||
throw e; | ||
} | ||
return filePaths.map(function (filePath) { | ||
return hashFile(path.join(srcDir, filePath)); | ||
}).join(','); | ||
} | ||
walkSync(srcDir).forEach(function (relPath) { | ||
// Keep track of all the JavaScript modules. | ||
if (path.extname(relPath) === '.js') { | ||
modules.push(relPath); | ||
return; | ||
} | ||
// Skip doing anything with dir entries. When outputting a bundle | ||
// format some dirs may go away. For non-JavaScript files, their | ||
// containing dir will be created before they are copied over. | ||
if (relPath.charAt(relPath.length - 1) === '/') { | ||
return; | ||
} | ||
var srcPath = path.join(srcDir, relPath), | ||
destPath = path.join(destDir, relPath); | ||
// Copy over non-JavaScript files to the `destDir`. | ||
// | ||
// TODO: switch to `symlinkOrCopySync()` after: | ||
// https://github.com/broccolijs/broccoli/issues/179 | ||
mkdirp.sync(path.dirname(destPath)); | ||
helpers.copyPreserveSync(srcPath, destPath); | ||
}); | ||
var modulesToCompile = [], | ||
cache = this._cache, | ||
cacheEntry; | ||
// The specificed `output` can either be a file (for bundle formatters), | ||
// or a dir. | ||
// | ||
// When outputting to a single file, all the input files must must be | ||
// unchanged in order to use the cached compiled file. | ||
// | ||
// When outputting to a dir, we symlink/copy over the cached compiled | ||
// file for any modules that are unchanged. Any modified modules are | ||
// added to the `modulesToCompile` collection. | ||
if (path.extname(outputPath) === '.js') { | ||
cacheEntry = cache[path.basename(outputPath)]; | ||
// Must hash _all_ modules when outputting to a single file. | ||
if (cacheEntry && cacheEntry.hash === hash(modules)) { | ||
this.copyFromCache(cacheEntry, path.dirname(outputPath)); | ||
} else { | ||
// With a cache-miss, we need to re-generate the bundle output | ||
// file, so we have to visit all the modules. The CacheResolver | ||
// will make sure we don't have to read-and-parse the modules | ||
// that are unchanged. | ||
modulesToCompile = modules; | ||
} | ||
} else { | ||
// Iterate over all the modules and copy compiled files from the | ||
// cache for the ones that are unchanged. | ||
modules.forEach(function (module) { | ||
cacheEntry = cache[module]; | ||
if (cacheEntry && cacheEntry.hash === hash(module)) { | ||
this.copyFromCache(cacheEntry, outputPath); | ||
} else { | ||
modulesToCompile.push(module); | ||
} | ||
}, this); | ||
} | ||
this.compileAndCacheModules(modulesToCompile, srcDir, outputPath); | ||
}.bind(this)); | ||
}; | ||
var transpilerMethods = { | ||
'amd': 'toAMD', | ||
'yui': 'toYUI', | ||
'cjs': 'toCJS', | ||
'globals': 'toGlobals' | ||
CompileModules.prototype.compileAndCacheModules = function (modulePaths, srcDir, outputPath) { | ||
// Noop when no modules to compile. | ||
if (modulePaths.length < 1) { return; } | ||
var cache = this._cache, | ||
outputIsFile = path.extname(outputPath) === '.js'; | ||
// The container will first use the CacheResolver so that any unchanged | ||
// modules that need to be visited by the transpiler don't have to be | ||
// re-read from disk or re-parsed. | ||
// | ||
// If "foo" imports "bar", and "bar" is unchanged, the transpiler still will | ||
// need to vist it when re-processing "foo". | ||
var container = new Container({ | ||
formatter: this.formatter, | ||
resolvers: [ | ||
new CacheResolver(cache, srcDir), | ||
new FileResolver([srcDir]) | ||
] | ||
}); | ||
// Returns transpiler `Module` instances. | ||
var modules = modulePaths.map(function (modulePath) { | ||
return container.getModule(modulePath); | ||
}); | ||
// Create a new cache sub-dir for this compile run. | ||
var cacheDir = path.join(this.getCacheDir(), String(this._cacheIndex++)); | ||
// Determine target path to compile modules to. | ||
var target = outputIsFile ? | ||
path.join(cacheDir, path.basename(outputPath)) : cacheDir; | ||
// Outputs the compiled modules to the cache. | ||
mkdirp(target); | ||
container.write(target); | ||
var outputHash = [], | ||
cacheEntry, outputFile; | ||
modules.forEach(function (module) { | ||
var hash = hashFile(module.path), | ||
relPath = path.relative(srcDir, module.path), | ||
modImports = module.imports, | ||
modExports = module.exports; | ||
// If the `module` was built with cached metadata, we want to un-wrap | ||
// its `imports` and `exports` before storing them in the `cacheEntry`. | ||
if (module instanceof CachedModule) { | ||
modImports = Object.getPrototypeOf(modImports); | ||
modExports = Object.getPrototypeOf(modExports); | ||
} | ||
// Adds an entry to the cache for the later use by the CacheResolver. | ||
// This holds the parsed and walked AST, so re-builds of unchanged | ||
// modules don't need to be re-read and re-parsed. | ||
var cacheEntry = cache[relPath] = { | ||
hash: hash, | ||
ast : module.ast, | ||
scope : module.scope, | ||
imports: modImports, | ||
exports: modExports | ||
}; | ||
// Accumulate hashes if the final output is a single bundle file, and | ||
// return early. | ||
if (outputIsFile) { | ||
outputHash.push(hash); | ||
return; | ||
} | ||
// When outputting to a dir, add the compiled files to the cache entry | ||
// and copy the files from the cache dir to the `outputPath`. | ||
cacheEntry.dir = cacheDir; | ||
cacheEntry.outputFiles = [ | ||
relPath, | ||
relPath + '.map' | ||
]; | ||
this.copyFromCache(cacheEntry, outputPath); | ||
}, this); | ||
if (outputIsFile) { | ||
outputFile = path.basename(outputPath); | ||
// Create a cache entry for the entire bundle output file and copy it | ||
// from the cache to the `outputPath`. | ||
cacheEntry = cache[outputFile] = { | ||
hash: outputHash.join(','), | ||
dir : cacheDir, | ||
outputFiles: [ | ||
outputFile, | ||
outputFile + '.map' | ||
] | ||
}; | ||
this.copyFromCache(cacheEntry, path.dirname(outputPath)); | ||
} | ||
}; | ||
CompileModules.prototype.copyFromCache = function (cacheEntry, destDir) { | ||
var cacheDir = cacheEntry.dir; | ||
cacheEntry.outputFiles.forEach(function (outputFile) { | ||
var cachePath = path.join(cacheDir, outputFile), | ||
destPath = path.join(destDir, outputFile); | ||
// TODO: switch to `symlinkOrCopySync()` after: | ||
// https://github.com/broccolijs/broccoli/issues/179 | ||
mkdirp.sync(path.dirname(destPath)); | ||
helpers.copyPreserveSync(cachePath, destPath); | ||
}); | ||
}; | ||
// -- CacheResolver ------------------------------------------------------------ | ||
// Used to speed up transpiling process on re-builds. The `cache` contains the | ||
// `ast`s and other info from perviously read-and-parsed modules. This data can | ||
// be reused for unchanged modules that the transpiler still needs to visit when | ||
// it's compiling during a re-build. | ||
function CacheResolver(cache, srcDir) { | ||
this.cache = cache; | ||
this.srcDir = srcDir; | ||
} | ||
CacheResolver.prototype.resolveModule = function (importedPath, fromModule, container) { | ||
var resolvedPath = this.resolvePath(importedPath, fromModule), | ||
cachedModule = container.getCachedModule(resolvedPath); | ||
if (cachedModule) { | ||
return cachedModule; | ||
} | ||
var cacheEntry = this.cache[path.relative(this.srcDir, resolvedPath)]; | ||
// Gets a file-stats hash of the module file, then checks if there's a cache | ||
// entry with that hash. | ||
if (cacheEntry && cacheEntry.hash === hashFile(resolvedPath)) { | ||
return new CachedModule(resolvedPath, importedPath, container, cacheEntry); | ||
} | ||
return null; | ||
}; | ||
CacheResolver.prototype.resolvePath = function (importedPath, fromModule) { | ||
var srcDir = this.srcDir, | ||
resolved; | ||
if (importedPath.charAt(0) === '.' && fromModule) { | ||
srcDir = path.dirname(fromModule.path); | ||
} | ||
resolved = path.resolve(srcDir, importedPath); | ||
if (path.extname(resolved) !== '.js') { | ||
resolved += '.js'; | ||
} | ||
return resolved; | ||
}; | ||
// -- CachedModule ------------------------------------------------------------- | ||
function CachedModule(resolvedPath, importedPath, container, cachedMeta) { | ||
Module.call(this, resolvedPath, importedPath, container); | ||
this.ast = cachedMeta.ast; | ||
this.scope = cachedMeta.scope; | ||
// Shadow cached `module` with `this`. | ||
this.imports = Object.create(cachedMeta.imports, { | ||
module: {value: this} | ||
}); | ||
// Shadow cached `module` with `this`. | ||
this.exports = Object.create(cachedMeta.exports, { | ||
module: {value: this} | ||
}); | ||
} | ||
util.inherits(CachedModule, Module); | ||
// -- Utilities ---------------------------------------------------------------- | ||
var hashFile = helpers.hashTree; | ||
// TODO: Determine if this impl is needed after: | ||
// https://github.com/broccolijs/broccoli/issues/179 | ||
// | ||
// Wrapper around `hashTree()` to dereference symbolic links within the Broccoli | ||
// build chain, so when new symlinks are created they won't be considered as | ||
// changed files, unless the real file they are pointing to hashes differently. | ||
// function hashFile(path) { | ||
// if (fs.lstatSync(path).isSymbolicLink()) { | ||
// path = fs.realpathSync(path); | ||
// } | ||
// | ||
// return helpers.hashTree(path); | ||
// } |
{ | ||
"name": "broccoli-es6-module-transpiler", | ||
"version": "0.1.1", | ||
"version": "0.2.0", | ||
"description": "Broccoli plugin for Square's ES6 Module Transpiler", | ||
@@ -8,2 +8,5 @@ "main": "index.js", | ||
"license": "MIT", | ||
"contributors": [ | ||
"Eric Ferraiuolo <eferraiuolo@gmail.com>" | ||
], | ||
"repository": { | ||
@@ -13,2 +16,5 @@ "type": "git", | ||
}, | ||
"scripts": { | ||
"test": "mocha tests/" | ||
}, | ||
"keywords": [ | ||
@@ -18,3 +24,5 @@ "broccoli-plugin", | ||
"es6", | ||
"modules", | ||
"module", | ||
"compile", | ||
"transpile", | ||
@@ -25,5 +33,14 @@ "amd", | ||
"dependencies": { | ||
"broccoli-filter": "0.1.6", | ||
"es6-module-transpiler": "~0.4.0" | ||
"broccoli-kitchen-sink-helpers": "^0.2.4", | ||
"broccoli-writer": "^0.1.1", | ||
"es6-module-transpiler": "^0.6.0", | ||
"mkdirp": "^0.5.0", | ||
"quick-temp": "^0.1.2", | ||
"walk-sync": "^0.1.2" | ||
}, | ||
"devDependencies": { | ||
"broccoli": "^0.12.3", | ||
"expect.js": "^0.3.1", | ||
"mocha": "^1.20.1" | ||
} | ||
} |
# Broccoli's ES6 Module Transpiler | ||
[![Build Status](https://travis-ci.org/mmun/broccoli-es6-module-transpiler.svg?branch=tests)](https://travis-ci.org/mmun/broccoli-es6-module-transpiler) | ||
A Broccoli plugin that transpiles ES6 modules to other module types using | ||
**Square's [es6-module-transpiler][transpiler]**. | ||
**Note:** The `es6-module-transpiler` package underwent a major refactor _after_ | ||
`v0.4.0`, the previous version of this package that works with the older | ||
transpiler is available on the [`transpiler-0.4` branch][prev-version]. | ||
## Usage | ||
Transpiling to AMD: | ||
### Transpiling to CommonJS | ||
```javascript | ||
var transpileES6 = require('broccoli-es6-module-transpiler'); | ||
var compileModules = require('broccoli-es6-module-transpiler'); | ||
var transpiledLib = transpileES6(lib, { | ||
moduleName: function(filePath) { | ||
return filePath.replace(/.js$/, ''); | ||
} | ||
var transpiledLib = compileModules(lib, { | ||
formatter: 'commonjs' | ||
}); | ||
``` | ||
Transpiling to CommonJS: | ||
### Transpiling to Bundle Format | ||
The bundle format is perfect for packaging your app's modules into one file that | ||
can be loaded in the browser _without_ needing a module loader. | ||
```javascript | ||
var transpileES6 = require('broccoli-es6-module-transpiler'); | ||
var compileModules = require('broccoli-es6-module-transpiler'); | ||
var transpiledLib = transpileES6(lib, { | ||
type: 'cjs' | ||
var transpiledLib = compileModules(lib, { | ||
formatter: 'bundle', | ||
output : 'app.js' | ||
}); | ||
``` | ||
## Documentation | ||
**Note:** The `output` option has a specified value to tell the transpiler where | ||
to output the new JavaScript file that contains the bundled transpiled modules. | ||
An `output` value is required when using the Bundle Format. | ||
### `transpileES6(inputTree, options)` | ||
### Transpiling to AMD | ||
--- | ||
The latest version of Square's [transpiler][] is flexible and pluggable, and | ||
while it doesn't ship with AMD support built-in you can use the AMD formatter: | ||
[es6-module-transpiler-amd-formatter][amd-formatter]. | ||
`options.type` *{String}* | ||
```javascript | ||
var compileModules = require('broccoli-es6-module-transpiler'); | ||
var AMDFormatter = require('es6-module-transpiler-amd-formatter'); | ||
The type of module to transpile to. | ||
var transpiledLib = compileModules(lib, { | ||
formatter: new AMDFormatter() | ||
}); | ||
``` | ||
Possible values: `amd`, `cjs`, `yui` or `globals`. | ||
Default: `amd`. | ||
## Documentation | ||
### `compileModules(inputTree, [options])` | ||
--- | ||
`options.moduleName` *{Function, String, `true`, null}* | ||
`options.formatter` *{String | Object}* | ||
The module name that is passed to the transpiler for each file. | ||
The formatter instance or built-in name to use to transpile the modules. | ||
Built-in formatters: `bundle`, `commonjs`. | ||
- If `moduleName` is `null` an anonymous module will be generated. | ||
- If `moduleName` is a string it will be passed directly the transpiler for all files. | ||
- If `moduleName` is a function it will be called with the path of the current file as its sole argument. The return value of the function will be passed to the transpiler. | ||
- If `moduleName` is `true` the filename will be used (after stripping the last three characters). | ||
Default: `bundle`. | ||
Default: `null`. | ||
--- | ||
`options.transpilerOptions` *{Function, Object, null}* | ||
`options.output` *{String}* | ||
The options that are passed to the transpiler for each file. | ||
The path where the transpiler should output the transpiled modules to. For | ||
formatters that output one file per module, this should be a directory, while | ||
formatters like the Bundle Format require a value for this option and it must be | ||
a file path. | ||
- If `transpilerOptions` is `null` no options will be passed. | ||
- If `transpilerOptions` is an object it will be passed directly the transpiler for all files. | ||
- If `transpilerOptions` is a function it will be called with the path of the current file as its sole argument. The return value of the function will be passed to the transpiler. | ||
Default: `"."`. | ||
Default: `null`. | ||
[transpiler]: https://github.com/esnext/es6-module-transpiler | ||
[prev-version]: https://github.com/mmun/broccoli-es6-module-transpiler/tree/transpiler-0.4 | ||
[amd-formatter]: https://github.com/caridy/es6-module-transpiler-amd-formatter |
Sorry, the diff of this file is not supported yet
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
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
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
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
18124
11
330
85
6
3
2
1
+ Addedbroccoli-writer@^0.1.1
+ Addedmkdirp@^0.5.0
+ Addedquick-temp@^0.1.2
+ Addedwalk-sync@^0.1.2
+ Addedamdefine@1.0.1(transitive)
+ Addedast-types@0.3.380.4.13(transitive)
+ Addedast-util@0.1.2(transitive)
+ Addedcls@0.1.5(transitive)
+ Addeddepd@1.0.1(transitive)
+ Addedes6-module-transpiler@0.6.2(transitive)
+ Addedposix-getopt@1.2.1(transitive)
+ Addedprivate@0.1.8(transitive)
+ Addedrecast@0.6.10(transitive)
+ Addedsource-map@0.1.32(transitive)
- Removedbroccoli-filter@0.1.6
- Removedbroccoli-filter@0.1.6(transitive)
- Removedes6-module-transpiler@0.4.0(transitive)
- Removedmkdirp@0.3.5(transitive)
- Removedoptimist@0.3.7(transitive)
- Removedpromise-map-series@0.2.3(transitive)
- Removedthrough@2.3.8(transitive)
- Removedwordwrap@0.0.3(transitive)
Updatedes6-module-transpiler@^0.6.0