Transpile all .js files with 6to5.

In the spirit of supporting JavaScript development for Atom packages,
this adds default support for es.next transpilation support in the way
that Atom already has default support for CoffeeScript transpilation.
There are many new features in ES6+ that make JavaScript development
easier and more enjoyable, particularly in terms of support for async code.

For reference, this was a much faster way to iterate on this than running `./script/build`
each time:

```
cp /Users/mbolin/src/atom/static/index.js /Applications/Atom.app/Contents/Resources/app/static/index.js
coffee --output /Applications/Atom.app/Contents/Resources/app/src --compile /Users/mbolin/src/atom/src/esnext.coffee
```

Run the following in the console to see how warm the cache was after startup:

```
global.require('../src/esnext/').getCacheHits()
global.require('../src/esnext/').getCacheMisses()
```
Esse commit está contido em:
Michael Bolin
2015-01-28 15:08:49 -08:00
commit 242fce3d79
4 arquivos alterados com 195 adições e 2 exclusões
+1
Ver Arquivo
@@ -19,6 +19,7 @@
],
"atomShellVersion": "0.21.0",
"dependencies": {
"6to5-core": "3.0.14",
"async": "0.2.6",
"atom-keymap": "^3.1.0",
"bootstrap": "git+https://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
+27
Ver Arquivo
@@ -0,0 +1,27 @@
{create6to5VersionAndOptionsDigest} = require '../src/esnext'
crypto = require 'crypto'
describe "esnext", ->
it "::create6to5VersionAndOptionsDigest", ->
defaultOptions =
'blacklist': [
'useStrict'
]
'experimental': true,
'optional': [
'asyncToGenerator'
]
'reactCompat': true
'sourceMap': 'inline'
version = '3.0.14'
shasum = crypto.createHash('sha1')
shasum.update('6to5-core', 'utf8')
shasum.update('\0', 'utf8')
shasum.update(version, 'utf8')
shasum.update('\0', 'utf8')
shasum.update('{"blacklist": ["useStrict",],"experimental": true,"optional": ["asyncToGenerator",],"reactCompat": true,"sourceMap": "inline",}')
expectedDigest = shasum.digest('hex')
observedDigest = create6to5VersionAndOptionsDigest(version, defaultOptions)
expect(observedDigest).toEqual(expectedDigest)
+157
Ver Arquivo
@@ -0,0 +1,157 @@
###
Cache for source code transpiled by 6to5.
Inspired by https://github.com/atom/atom/blob/6b963a562f8d495fbebe6abdbafbc7caf705f2c3/src/coffee-cache.coffee.
###
crypto = require 'crypto'
fs = require 'fs-plus'
path = require 'path';
to5 = require '6to5-core'
stats =
hits: 0
misses: 0
defaultOptions =
# The Chrome dev tools will show the original version of the file
# when the source map is inlined.
'sourceMap': 'inline'
# Because Atom is currently packaged with a fork of React v0.11,
# it makes sense to use the --react-compat option so the React
# JSX transformer produces pre-v0.12 code.
'reactCompat': true
# Blacklisted features do not get transpiled. Features that are
# natively supported in the target environment should be listed
# here. Because Atom uses a bleeding edge version of Node/io.js,
# I think this can include es6.arrowFunctions, es6.classes, and
# possibly others, but I want to be conservative.
'blacklist': [
'useStrict'
]
# Includes support for es7 features listed at:
# http://6to5.org/docs/usage/transformers/#es7-experimental-.
'experimental': true,
'optional': [
# Target a version of the regenerator runtime that
# supports yield so the transpiled code is cleaner/smaller.
'asyncToGenerator'
]
###
shasum - Hash with an update() method.
value - Must be a value that could be returned by JSON.parse().
###
updateDigestForJsonValue = (shasum, value) ->
# Implmentation is similar to that of pretty-printing a JSON object, except:
# * Strings are not escaped.
# * No effort is made to avoid trailing commas.
# These shortcuts should not affect the correctness of this function.
if typeof value is 'string'
shasum.update('"', 'utf8')
shasum.update(value, 'utf8')
shasum.update('"', 'utf8')
else if typeof value is 'boolean' or typeof value is 'number'
shasum.update(value.toString(), 'utf8')
else if typeof value is null
shasum.update('null', 'utf8')
else if Array.isArray value
shasum.update('[', 'utf8')
for item in value
updateDigestForJsonValue(shasum, item)
shasum.update(',', 'utf8')
shasum.update(']', 'utf8')
else
# Must be an object: be sure to sort the keys.
keys = []
for key of value
keys.push(key)
keys.sort()
shasum.update('{', 'utf8')
for key in keys
updateDigestForJsonValue(shasum, key)
shasum.update(': ', 'utf8')
updateDigestForJsonValue(shasum, value[key])
shasum.update(',', 'utf8')
shasum.update('}', 'utf8')
create6to5VersionAndOptionsDigest = (version, options) ->
shasum = crypto.createHash('sha1')
# Include the version of 6to5 in the hash.
shasum.update('6to5-core', 'utf8')
shasum.update('\0', 'utf8')
shasum.update(version, 'utf8')
shasum.update('\0', 'utf8')
updateDigestForJsonValue(shasum, options)
return shasum.digest('hex')
cacheDir = path.join(fs.absolute('~/.atom'), 'compile-cache')
jsCacheDir = path.join(
cacheDir,
create6to5VersionAndOptionsDigest(to5.version, defaultOptions),
'js')
getCachePath = (sourceCode) ->
digest = crypto.createHash('sha1').update(sourceCode, 'utf8').digest('hex')
return path.join(jsCacheDir, "#{digest}.js")
getCachedJavaScript = (cachePath) ->
if fs.isFileSync(cachePath)
try
cachedJavaScript = fs.readFileSync(cachePath, 'utf8')
stats.hits++
return cachedJavaScript
return null
# Returns the 6to5 options that should be used to transpile filePath.
createOptions = (filePath) ->
options =
'filename': filePath
for key, value of defaultOptions
options[key] = value
return options
# Function that obeys the contract of an entry in the require.extensions map.
# Returns the transpiled version of the JavaScript code at filePath, which is
# either generated on the fly or pulled from cache.
loadFile = (module, filePath) ->
sourceCode = fs.readFileSync(filePath, 'utf8')
if not (sourceCode.startsWith('"use 6to5"') or sourceCode.startsWith("'use 6to5'"))
module._compile(sourceCode, filePath)
return
cachePath = getCachePath(sourceCode)
js = getCachedJavaScript(cachePath)
unless js
options = createOptions filePath
try
js = to5.transform(sourceCode, options).code
stats.misses++
catch error
console.error('Error compiling %s: %o', filePath, error)
throw error
try
fs.writeFileSync(cachePath, js)
catch error
console.error('Error writing to cache at %s: %o', cachePath, error)
throw error
module._compile(js, filePath)
register = (appVersion) ->
require.extensions['.js'] = loadFile
module.exports =
register: register
getCacheMisses: -> stats.misses
getCacheHits: -> stats.hits
# Visible for testing.
create6to5VersionAndOptionsDigest: create6to5VersionAndOptionsDigest
+10 -2
Ver Arquivo
@@ -1,3 +1,11 @@
function registerRuntimeTranspilers() {
// This sets require.extensions['.coffee'].
require('coffee-script').register();
// This redefines require.extensions['.js'].
require('../src/esnext').register();
}
window.onload = function() {
try {
var startTime = Date.now();
@@ -22,7 +30,7 @@ window.onload = function() {
// Require before the module cache in dev mode
if (devMode) {
require('coffee-script').register();
registerRuntimeTranspilers();
}
ModuleCache = require('../src/module-cache');
@@ -41,7 +49,7 @@ window.onload = function() {
require('vm-compatibility-layer');
if (!devMode) {
require('coffee-script').register();
registerRuntimeTranspilers();
}
require('../src/coffee-cache').register();