mirror of
https://github.com/atom/atom.git
synced 2026-04-28 03:01:47 -04:00
Use source-map-support to handle stack trace conversion
Previously, our Error.convertStackTrace function was provided by coffeestack, which only works for coffee-script. This adds a dependency on 'source-map-support', which works for any source file with inline source maps. This also refactors the code for registering our compilers (coffee-script, typescript, and babel) so that caching logic is shared.
This commit is contained in:
201
src/babel.coffee
201
src/babel.coffee
@@ -1,201 +0,0 @@
|
||||
###
|
||||
Cache for source code transpiled by Babel.
|
||||
|
||||
Inspired by https://github.com/atom/atom/blob/6b963a562f8d495fbebe6abdbafbc7caf705f2c3/src/coffee-cache.coffee.
|
||||
###
|
||||
|
||||
crypto = require 'crypto'
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
babel = null # Defer until used
|
||||
Grim = null # Defer until used
|
||||
|
||||
stats =
|
||||
hits: 0
|
||||
misses: 0
|
||||
|
||||
defaultOptions =
|
||||
# Currently, the cache key is a function of:
|
||||
# * The version of Babel used to transpile the .js file.
|
||||
# * The contents of this defaultOptions object.
|
||||
# * The contents of the .js file.
|
||||
# That means that we cannot allow information from an unknown source
|
||||
# to affect the cache key for the output of transpilation, which means
|
||||
# we cannot allow users to override these default options via a .babelrc
|
||||
# file, because the contents of that .babelrc file will not make it into
|
||||
# the cache key. It would be great to support .babelrc files once we
|
||||
# have a way to do so that is safe with respect to caching.
|
||||
breakConfig: true
|
||||
|
||||
# The Chrome dev tools will show the original version of the file
|
||||
# when the source map is inlined.
|
||||
sourceMap: 'inline'
|
||||
|
||||
# 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: [
|
||||
'es6.forOf'
|
||||
'useStrict'
|
||||
]
|
||||
|
||||
optional: [
|
||||
# Target a version of the regenerator runtime that
|
||||
# supports yield so the transpiled code is cleaner/smaller.
|
||||
'asyncToGenerator'
|
||||
]
|
||||
|
||||
# Includes support for es7 features listed at:
|
||||
# http://babeljs.io/docs/usage/experimental/.
|
||||
stage: 0
|
||||
|
||||
|
||||
###
|
||||
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')
|
||||
|
||||
createBabelVersionAndOptionsDigest = (version, options) ->
|
||||
shasum = crypto.createHash('sha1')
|
||||
# Include the version of babel in the hash.
|
||||
shasum.update('babel-core', 'utf8')
|
||||
shasum.update('\0', 'utf8')
|
||||
shasum.update(version, 'utf8')
|
||||
shasum.update('\0', 'utf8')
|
||||
updateDigestForJsonValue(shasum, options)
|
||||
shasum.digest('hex')
|
||||
|
||||
cacheDir = null
|
||||
jsCacheDir = null
|
||||
|
||||
getCachePath = (sourceCode) ->
|
||||
digest = crypto.createHash('sha1').update(sourceCode, 'utf8').digest('hex')
|
||||
|
||||
unless jsCacheDir?
|
||||
to5Version = require('babel-core/package.json').version
|
||||
jsCacheDir = path.join(cacheDir, createBabelVersionAndOptionsDigest(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 babel 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)
|
||||
babel ?= require 'babel-core'
|
||||
js = babel.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')
|
||||
if sourceCode.startsWith('"use babel"') or sourceCode.startsWith("'use babel'") or sourceCode.startsWith('/** use babel */')
|
||||
# Continue.
|
||||
else if sourceCode.startsWith('"use 6to5"') or sourceCode.startsWith("'use 6to5'")
|
||||
# Create a manual deprecation since the stack is too deep to use Grim
|
||||
# which limits the depth to 3
|
||||
Grim ?= require 'grim'
|
||||
stack = [
|
||||
{
|
||||
fileName: __filename
|
||||
functionName: 'loadFile'
|
||||
location: "#{__filename}:161:5"
|
||||
}
|
||||
{
|
||||
fileName: filePath
|
||||
functionName: '<unknown>'
|
||||
location: "#{filePath}:1:1"
|
||||
}
|
||||
]
|
||||
deprecation =
|
||||
message: "Use the 'use babel' pragma instead of 'use 6to5'"
|
||||
stacks: [stack]
|
||||
Grim.addSerializedDeprecation(deprecation)
|
||||
else
|
||||
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', {
|
||||
enumerable: true
|
||||
writable: false
|
||||
value: loadFile
|
||||
})
|
||||
|
||||
setCacheDirectory = (newCacheDir) ->
|
||||
if cacheDir isnt newCacheDir
|
||||
cacheDir = newCacheDir
|
||||
jsCacheDir = null
|
||||
|
||||
module.exports =
|
||||
register: register
|
||||
setCacheDirectory: setCacheDirectory
|
||||
getCacheMisses: -> stats.misses
|
||||
getCacheHits: -> stats.hits
|
||||
defaultOptions: defaultOptions
|
||||
|
||||
# Visible for testing.
|
||||
createBabelVersionAndOptionsDigest: createBabelVersionAndOptionsDigest
|
||||
|
||||
addPathToCache: (filePath) ->
|
||||
return if path.extname(filePath) isnt '.js'
|
||||
|
||||
sourceCode = fs.readFileSync(filePath, 'utf8')
|
||||
cachePath = getCachePath(sourceCode)
|
||||
transpile(sourceCode, filePath, cachePath)
|
||||
70
src/babel.js
Normal file
70
src/babel.js
Normal file
@@ -0,0 +1,70 @@
|
||||
'use strict'
|
||||
|
||||
const _ = require('underscore-plus')
|
||||
const crypto = require('crypto')
|
||||
const path = require('path')
|
||||
|
||||
let babel = null
|
||||
let babelVersionDirectory = null
|
||||
|
||||
// This field is used by the Gruntfile for compiling babel in bundled packages.
|
||||
exports.defaultOptions = {
|
||||
|
||||
// Currently, the cache key is a function of:
|
||||
// * The version of Babel used to transpile the .js file.
|
||||
// * The contents of this defaultOptions object.
|
||||
// * The contents of the .js file.
|
||||
// That means that we cannot allow information from an unknown source
|
||||
// to affect the cache key for the output of transpilation, which means
|
||||
// we cannot allow users to override these default options via a .babelrc
|
||||
// file, because the contents of that .babelrc file will not make it into
|
||||
// the cache key. It would be great to support .babelrc files once we
|
||||
// have a way to do so that is safe with respect to caching.
|
||||
breakConfig: true,
|
||||
|
||||
sourceMap: 'inline',
|
||||
blacklist: ['es6.forOf', 'useStrict'],
|
||||
optional: ['asyncToGenerator'],
|
||||
stage: 0
|
||||
}
|
||||
|
||||
exports.shouldCompile = function(sourceCode) {
|
||||
return sourceCode.startsWith('/** use babel */') ||
|
||||
sourceCode.startsWith('"use babel"') ||
|
||||
sourceCode.startsWith("'use babel'")
|
||||
}
|
||||
|
||||
exports.getCachePath = function(sourceCode) {
|
||||
if (babelVersionDirectory == null) {
|
||||
let babelVersion = require('babel-core/package.json').version
|
||||
babelVersionDirectory = path.join('js', 'babel', createVersionAndOptionsDigest(babelVersion, defaultOptions))
|
||||
}
|
||||
|
||||
return path.join(
|
||||
babelVersionDirectory,
|
||||
crypto
|
||||
.createHash('sha1')
|
||||
.update(sourceCode, 'utf8')
|
||||
.digest('hex') + ".js"
|
||||
)
|
||||
}
|
||||
|
||||
exports.compile = function(sourceCode, filePath) {
|
||||
if (!babel) {
|
||||
babel = require('babel-core')
|
||||
}
|
||||
|
||||
let options = _.defaults({filename: filePath}, defaultOptions)
|
||||
return babel.transform(sourceCode, options).code
|
||||
}
|
||||
|
||||
function createVersionAndOptionsDigest (version, options) {
|
||||
return crypto
|
||||
.createHash('sha1')
|
||||
.update('babel-core', 'utf8')
|
||||
.update('\0', 'utf8')
|
||||
.update(version, 'utf8')
|
||||
.update('\0', 'utf8')
|
||||
.update(JSON.stringify(options), 'utf8')
|
||||
.digest('hex')
|
||||
}
|
||||
38
src/coffee-script.js
Normal file
38
src/coffee-script.js
Normal file
@@ -0,0 +1,38 @@
|
||||
'use strict'
|
||||
|
||||
const crypto = require('crypto')
|
||||
const path = require('path')
|
||||
|
||||
// The coffee-script compiler is required eagerly because:
|
||||
// 1. It is always used.
|
||||
// 2. It reassigns Error.prepareStackTrace, so we need to make sure that
|
||||
// the 'source-map-support' module is installed *after* it is loaded.
|
||||
const CoffeeScript = require('coffee-script')
|
||||
|
||||
exports.shouldCompile = function() {
|
||||
return true
|
||||
}
|
||||
|
||||
exports.getCachePath = function(sourceCode) {
|
||||
return path.join(
|
||||
"coffee",
|
||||
crypto
|
||||
.createHash('sha1')
|
||||
.update(sourceCode, 'utf8')
|
||||
.digest('hex') + ".js"
|
||||
)
|
||||
}
|
||||
|
||||
exports.compile = function(sourceCode, filePath) {
|
||||
let output = CoffeeScript.compile(sourceCode, {
|
||||
filename: filePath,
|
||||
sourceMap: true
|
||||
})
|
||||
|
||||
let js = output.js
|
||||
js += '\n'
|
||||
js += '//# sourceMappingURL=data:application/json;base64,'
|
||||
js += new Buffer(output.v3SourceMap).toString('base64')
|
||||
js += '\n'
|
||||
return js
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
path = require 'path'
|
||||
CSON = require 'season'
|
||||
CoffeeCache = require 'coffee-cash'
|
||||
babel = require './babel'
|
||||
typescript = require './typescript'
|
||||
|
||||
# This file is required directly by apm so that files can be cached during
|
||||
# package install so that the first package load in Atom doesn't have to
|
||||
# compile anything.
|
||||
exports.addPathToCache = (filePath, atomHome) ->
|
||||
atomHome ?= process.env.ATOM_HOME
|
||||
cacheDir = path.join(atomHome, 'compile-cache')
|
||||
# Use separate compile cache when sudo'ing as root to avoid permission issues
|
||||
if process.env.USER is 'root' and process.env.SUDO_USER and process.env.SUDO_USER isnt process.env.USER
|
||||
cacheDir = path.join(cacheDir, 'root')
|
||||
|
||||
CoffeeCache.setCacheDirectory(path.join(cacheDir, 'coffee'))
|
||||
CSON.setCacheDir(path.join(cacheDir, 'cson'))
|
||||
babel.setCacheDirectory(path.join(cacheDir, 'js', 'babel'))
|
||||
typescript.setCacheDirectory(path.join(cacheDir, 'ts'))
|
||||
|
||||
switch path.extname(filePath)
|
||||
when '.coffee'
|
||||
CoffeeCache.addPathToCache(filePath)
|
||||
when '.cson'
|
||||
CSON.readFileSync(filePath)
|
||||
when '.js'
|
||||
babel.addPathToCache(filePath)
|
||||
when '.ts'
|
||||
typescript.addPathToCache(filePath)
|
||||
123
src/compile-cache.js
Normal file
123
src/compile-cache.js
Normal file
@@ -0,0 +1,123 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const CSON = require('season')
|
||||
const fs = require('fs-plus')
|
||||
|
||||
const COMPILERS = {
|
||||
'.js': require('./babel'),
|
||||
'.ts': require('./typescript'),
|
||||
'.coffee': require('./coffee-script')
|
||||
}
|
||||
|
||||
for (let extension in COMPILERS) {
|
||||
let compiler = COMPILERS[extension]
|
||||
Object.defineProperty(require.extensions, extension, {
|
||||
enumerable: true,
|
||||
writable: false,
|
||||
value: function (module, filePath) {
|
||||
let code = compileFileAtPath(compiler, filePath)
|
||||
return module._compile(code, filePath)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let cacheDirectory = null
|
||||
|
||||
exports.setAtomHomeDirectory = function (atomHome) {
|
||||
let cacheDir = path.join(atomHome, 'compile-cache')
|
||||
if (process.env.USER === 'root' && process.env.SUDO_USER && process.env.SUDO_USER !== process.env.USER) {
|
||||
cacheDir = path.join(cacheDirectory, 'root')
|
||||
}
|
||||
this.setCacheDirectory(cacheDir)
|
||||
}
|
||||
|
||||
exports.setCacheDirectory = function (directory) {
|
||||
cacheDirectory = directory
|
||||
CSON.setCacheDir(path.join(cacheDirectory, 'cson'));
|
||||
}
|
||||
|
||||
exports.getCacheDirectory = function () {
|
||||
return cacheDirectory
|
||||
}
|
||||
|
||||
exports.addPathToCache = function (filePath, atomHome) {
|
||||
this.setAtomHomeDirectory(atomHome)
|
||||
extension = path.extname(filePath)
|
||||
if (extension === '.cson') {
|
||||
return CSON.readFileSync(filePath)
|
||||
}
|
||||
|
||||
if (compiler = COMPILERS[extension]) {
|
||||
return compileFileAtPath(compiler, filePath)
|
||||
}
|
||||
}
|
||||
|
||||
function compileFileAtPath (compiler, filePath) {
|
||||
let sourceCode = fs.readFileSync(filePath, 'utf8')
|
||||
if (compiler.shouldCompile(sourceCode, filePath)) {
|
||||
let cachePath = compiler.getCachePath(sourceCode, filePath)
|
||||
let compiledCode = readCachedJavascript(cachePath)
|
||||
if (compiledCode == null) {
|
||||
compiledCode = compiler.compile(sourceCode, filePath)
|
||||
writeCachedJavascript(cachePath, compiledCode)
|
||||
}
|
||||
return compiledCode
|
||||
}
|
||||
return sourceCode
|
||||
}
|
||||
|
||||
function readCachedJavascript (relativeCachePath) {
|
||||
let cachePath = path.join(cacheDirectory, relativeCachePath)
|
||||
if (fs.isFileSync(cachePath)) {
|
||||
try {
|
||||
return fs.readFileSync(cachePath, 'utf8')
|
||||
} catch (error) {}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function writeCachedJavascript (relativeCachePath, code) {
|
||||
let cachePath = path.join(cacheDirectory, relativeCachePath)
|
||||
fs.writeFileSync(cachePath, code, 'utf8')
|
||||
}
|
||||
|
||||
const InlineSourceMapRegExp = /\/\/[#@]\s*sourceMappingURL=([^'"]+)\s*$/g
|
||||
|
||||
require('source-map-support').install({
|
||||
handleUncaughtExceptions: false,
|
||||
|
||||
// Most of this logic is the same as the default implementation in the
|
||||
// source-map-support module, but we've overridden it to read the javascript
|
||||
// code from our cache directory.
|
||||
retrieveSourceMap: function (filePath) {
|
||||
if (!fs.isFileSync(filePath)){
|
||||
return null
|
||||
}
|
||||
|
||||
let sourceCode = fs.readFileSync(filePath, 'utf8')
|
||||
let compiler = COMPILERS[path.extname(filePath)]
|
||||
let fileData = readCachedJavascript(compiler.getCachePath(sourceCode, filePath))
|
||||
if (fileData == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
let match, lastMatch
|
||||
InlineSourceMapRegExp.lastIndex = 0
|
||||
while ((match = InlineSourceMapRegExp.exec(fileData))) {
|
||||
lastMatch = match
|
||||
}
|
||||
if (lastMatch == null){
|
||||
return null
|
||||
}
|
||||
|
||||
let sourceMappingURL = lastMatch[1]
|
||||
let rawData = sourceMappingURL.slice(sourceMappingURL.indexOf(',') + 1)
|
||||
let sourceMapData = new Buffer(rawData, 'base64').toString()
|
||||
|
||||
return {
|
||||
map: JSON.parse(sourceMapData),
|
||||
url: null
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -66,15 +66,11 @@ class Task
|
||||
# * `taskPath` The {String} path to the CoffeeScript/JavaScript file that
|
||||
# exports a single {Function} to execute.
|
||||
constructor: (taskPath) ->
|
||||
coffeeCacheRequire = "require('#{require.resolve('coffee-cash')}')"
|
||||
coffeeCachePath = require('coffee-cash').getCacheDirectory()
|
||||
coffeeStackRequire = "require('#{require.resolve('coffeestack')}')"
|
||||
stackCachePath = require('coffeestack').getCacheDirectory()
|
||||
compileCacheRequire = "require('#{require.resolve('./compile-cache')}')"
|
||||
compileCachePath = require('./compile-cache').getCacheDirectory()
|
||||
taskBootstrapRequire = "require('#{require.resolve('./task-bootstrap')}');"
|
||||
bootstrap = """
|
||||
#{coffeeCacheRequire}.setCacheDirectory('#{coffeeCachePath}');
|
||||
#{coffeeCacheRequire}.register();
|
||||
#{coffeeStackRequire}.setCacheDirectory('#{stackCachePath}');
|
||||
#{compileCacheRequire}.setCacheDirectory('#{compileCachePath}');
|
||||
#{taskBootstrapRequire}
|
||||
"""
|
||||
bootstrap = bootstrap.replace(/\\/g, "\\\\")
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
###
|
||||
Cache for source code transpiled by TypeScript.
|
||||
|
||||
Inspired by https://github.com/atom/atom/blob/7a719d585db96ff7d2977db9067e1d9d4d0adf1a/src/babel.coffee
|
||||
###
|
||||
|
||||
crypto = require 'crypto'
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
tss = null # Defer until used
|
||||
|
||||
stats =
|
||||
hits: 0
|
||||
misses: 0
|
||||
|
||||
defaultOptions =
|
||||
target: 1 # ES5
|
||||
module: 'commonjs'
|
||||
sourceMap: true
|
||||
|
||||
createTypeScriptVersionAndOptionsDigest = (version, options) ->
|
||||
shasum = crypto.createHash('sha1')
|
||||
# Include the version of typescript in the hash.
|
||||
shasum.update('typescript', 'utf8')
|
||||
shasum.update('\0', 'utf8')
|
||||
shasum.update(version, 'utf8')
|
||||
shasum.update('\0', 'utf8')
|
||||
shasum.update(JSON.stringify(options))
|
||||
shasum.digest('hex')
|
||||
|
||||
cacheDir = null
|
||||
jsCacheDir = null
|
||||
|
||||
getCachePath = (sourceCode) ->
|
||||
digest = crypto.createHash('sha1').update(sourceCode, 'utf8').digest('hex')
|
||||
|
||||
unless jsCacheDir?
|
||||
tssVersion = require('typescript-simple/package.json').version
|
||||
jsCacheDir = path.join(cacheDir, createTypeScriptVersionAndOptionsDigest(tssVersion, 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 TypeScript 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)
|
||||
unless tss?
|
||||
{TypeScriptSimple} = require 'typescript-simple'
|
||||
tss = new TypeScriptSimple(options, false)
|
||||
js = tss.compile(sourceCode, filePath)
|
||||
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')
|
||||
cachePath = getCachePath(sourceCode)
|
||||
js = getCachedJavaScript(cachePath) ? transpile(sourceCode, filePath, cachePath)
|
||||
module._compile(js, filePath)
|
||||
|
||||
register = ->
|
||||
Object.defineProperty(require.extensions, '.ts', {
|
||||
enumerable: true
|
||||
writable: false
|
||||
value: loadFile
|
||||
})
|
||||
|
||||
setCacheDirectory = (newCacheDir) ->
|
||||
if cacheDir isnt newCacheDir
|
||||
cacheDir = newCacheDir
|
||||
jsCacheDir = null
|
||||
|
||||
module.exports =
|
||||
register: register
|
||||
setCacheDirectory: setCacheDirectory
|
||||
getCacheMisses: -> stats.misses
|
||||
getCacheHits: -> stats.hits
|
||||
|
||||
# Visible for testing.
|
||||
createTypeScriptVersionAndOptionsDigest: createTypeScriptVersionAndOptionsDigest
|
||||
|
||||
addPathToCache: (filePath) ->
|
||||
return if path.extname(filePath) isnt '.ts'
|
||||
|
||||
sourceCode = fs.readFileSync(filePath, 'utf8')
|
||||
cachePath = getCachePath(sourceCode)
|
||||
transpile(sourceCode, filePath, cachePath)
|
||||
53
src/typescript.js
Normal file
53
src/typescript.js
Normal file
@@ -0,0 +1,53 @@
|
||||
'use strict'
|
||||
|
||||
const _ = require('underscore-plus')
|
||||
const crypto = require('crypto')
|
||||
const path = require('path')
|
||||
|
||||
let TypeScriptSimple = null
|
||||
let typescriptVersionDir = null
|
||||
|
||||
const defaultOptions = {
|
||||
target: 1,
|
||||
module: 'commonjs',
|
||||
sourceMap: true
|
||||
}
|
||||
|
||||
exports.shouldCompile = function() {
|
||||
return true
|
||||
}
|
||||
|
||||
exports.getCachePath = function(sourceCode) {
|
||||
if (typescriptVersionDir == null) {
|
||||
let version = require('typescript-simple/package.json').version
|
||||
typescriptVersionDir = path.join('ts', createVersionAndOptionsDigest(version, defaultOptions))
|
||||
}
|
||||
|
||||
return path.join(
|
||||
typescriptVersionDir,
|
||||
crypto
|
||||
.createHash('sha1')
|
||||
.update(sourceCode, 'utf8')
|
||||
.digest('hex') + ".js"
|
||||
)
|
||||
}
|
||||
|
||||
exports.compile = function(sourceCode, filePath) {
|
||||
if (!TypeScriptSimple) {
|
||||
TypeScriptSimple = require('typescript-simple').TypeScriptSimple
|
||||
}
|
||||
|
||||
let options = _.defaults({filename: filePath}, defaultOptions)
|
||||
return new TypeScriptSimple(options, false).compile(sourceCode, filePath)
|
||||
}
|
||||
|
||||
function createVersionAndOptionsDigest (version, options) {
|
||||
return crypto
|
||||
.createHash('sha1')
|
||||
.update('typescript', 'utf8')
|
||||
.update('\0', 'utf8')
|
||||
.update(version, 'utf8')
|
||||
.update('\0', 'utf8')
|
||||
.update(JSON.stringify(options), 'utf8')
|
||||
.digest('hex')
|
||||
}
|
||||
Reference in New Issue
Block a user