mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-02-24 08:04:11 +08:00
162 lines
4.9 KiB
CoffeeScript
162 lines
4.9 KiB
CoffeeScript
|
###
|
||
|
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 = null # Defer until used
|
||
|
|
||
|
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.
|
||
|
type = typeof value
|
||
|
if type is 'string'
|
||
|
shasum.update('"', 'utf8')
|
||
|
shasum.update(value, 'utf8')
|
||
|
shasum.update('"', 'utf8')
|
||
|
else if type in ['boolean', 'number']
|
||
|
shasum.update(value.toString(), 'utf8')
|
||
|
else if 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
|
||
|
# value must be an object: be sure to sort the keys.
|
||
|
keys = Object.keys value
|
||
|
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)
|
||
|
shasum.digest('hex')
|
||
|
|
||
|
jsCacheDir = null
|
||
|
|
||
|
getCachePath = (sourceCode) ->
|
||
|
digest = crypto.createHash('sha1').update(sourceCode, 'utf8').digest('hex')
|
||
|
|
||
|
unless jsCacheDir?
|
||
|
to5Version = require('6to5-core/package.json').version
|
||
|
cacheDir = path.join(fs.absolute('~/.atom'), 'compile-cache')
|
||
|
jsCacheDir = path.join(cacheDir, 'js', '6to5', create6to5VersionAndOptionsDigest(to5Version, defaultOptions))
|
||
|
|
||
|
path.join(jsCacheDir, "#{digest}.js")
|
||
|
|
||
|
getCachedJavaScript = (cachePath) ->
|
||
|
if fs.isFileSync(cachePath)
|
||
|
try
|
||
|
cachedJavaScript = fs.readFileSync(cachePath, 'utf8')
|
||
|
stats.hits++
|
||
|
return cachedJavaScript
|
||
|
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
|
||
|
options
|
||
|
|
||
|
transpile = (sourceCode, filePath, cachePath) ->
|
||
|
options = createOptions(filePath)
|
||
|
to5 ?= require '6to5-core'
|
||
|
js = to5.transform(sourceCode, options).code
|
||
|
stats.misses++
|
||
|
|
||
|
try
|
||
|
fs.writeFileSync(cachePath, js)
|
||
|
|
||
|
js
|
||
|
|
||
|
# 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')
|
||
|
unless sourceCode.startsWith('"use 6to5"') or sourceCode.startsWith("'use 6to5'")
|
||
|
return module._compile(sourceCode, filePath)
|
||
|
|
||
|
cachePath = getCachePath(sourceCode)
|
||
|
js = getCachedJavaScript(cachePath) ? transpile(sourceCode, filePath, cachePath)
|
||
|
module._compile(js, filePath)
|
||
|
|
||
|
register = ->
|
||
|
Object.defineProperty(require.extensions, '.js', {
|
||
|
writable: false
|
||
|
value: loadFile
|
||
|
})
|
||
|
|
||
|
module.exports =
|
||
|
register: register
|
||
|
getCacheMisses: -> stats.misses
|
||
|
getCacheHits: -> stats.hits
|
||
|
|
||
|
# Visible for testing.
|
||
|
create6to5VersionAndOptionsDigest: create6to5VersionAndOptionsDigest
|
||
|
|
||
|
addPathToCache: (filePath) ->
|
||
|
return if path.extname(filePath) isnt '.js'
|
||
|
|
||
|
sourceCode = fs.readFileSync(filePath, 'utf8')
|
||
|
cachePath = getCachePath(sourceCode)
|
||
|
transpile(sourceCode, filePath, cachePath)
|