First pass at per-package transpilation

This commit is contained in:
Michelle Tilley 2016-10-29 14:30:24 -07:00
parent 0860f354ca
commit ad448184b4
No known key found for this signature in database
GPG Key ID: CD86C13E51F378DA
3 changed files with 152 additions and 3 deletions

View File

@ -7,12 +7,24 @@
var path = require('path')
var fs = require('fs-plus')
var PackageTranspilationRegistry = require('./package-transpilation-registry')
var CSON = null
var packageTranspilationRegistry = new PackageTranspilationRegistry()
var COMPILERS = {
'.js': require('./babel'),
'.ts': require('./typescript'),
'.coffee': require('./coffee-script')
'.js': packageTranspilationRegistry.wrapTranspiler(require('./babel')),
'.ts': packageTranspilationRegistry.wrapTranspiler(require('./typescript')),
'.coffee': packageTranspilationRegistry.wrapTranspiler(require('./coffee-script'))
}
exports.addTranspilerConfigForPath = function (packagePath, config) {
packageTranspilationRegistry.addTranspilerConfigForPath(packagePath, config)
}
exports.removeTranspilerConfigForPath = function (packagePath) {
packageTranspilationRegistry.removeTranspilerConfigForPath(packagePath)
}
var cacheStats = {}

View File

@ -0,0 +1,122 @@
var crypto = require('crypto')
var fs = require('fs')
var path = require('path')
var Resolve = null
function PackageTranspilationRegistry () {
this.configByPackagePath = {}
this.configByFilePath = {}
this.transpilerPaths = {}
this.transpilerHashes = {}
}
PackageTranspilationRegistry.prototype.addTranspilerConfigForPath = function (packagePath, config) {
packagePath = fs.realpathSync(packagePath)
this.configByPackagePath[packagePath] = Object.assign({}, config, {
path: packagePath
})
}
PackageTranspilationRegistry.prototype.removeTranspilerConfigForPath = function (packagePath) {
packagePath = fs.realpathSync(packagePath)
delete this.configByPackagePath[path]
}
// Wraps the transpiler in an object with the same interface
// that falls back to the original transpiler implementation if and
// only if a package hasn't registered its desire to transpile its own source.
PackageTranspilationRegistry.prototype.wrapTranspiler = function (transpiler) {
var self = this
return {
getCachePath: function (sourceCode, filePath) {
var config = self.getPackageTranspilerConfigForFilePath(filePath)
if (config) {
return self.getCachePath(sourceCode, filePath, config)
}
return transpiler.getCachePath(sourceCode, filePath)
},
compile: function (sourceCode, filePath) {
var config = self.getPackageTranspilerConfigForFilePath(filePath)
if (config) {
return self.transpileWithPackageTranspiler(sourceCode, filePath, config)
}
return transpiler.compile(sourceCode, filePath)
},
shouldCompile: function (sourceCode, filePath) {
if (self.transpilerPaths[filePath]) {
return false
}
var config = self.getPackageTranspilerConfigForFilePath(filePath)
if (config) {
return true
}
return transpiler.shouldCompile(sourceCode, filePath)
}
}
}
PackageTranspilationRegistry.prototype.getPackageTranspilerConfigForFilePath = function (filePath) {
if (this.configByFilePath[filePath] !== undefined) return this.configByFilePath[filePath]
var config = null
var thisPath = filePath
var lastPath = null
// Iterate parents from the file path to the root, checking at each level
// to see if a package manages transpilation for that directory.
// This means searching for a config for `/path/to/file/here.js` only
// only iterates four times, even if there are hundreds of configs registered.
while (thisPath !== lastPath) { // until we reach the root
if (config = this.configByPackagePath[thisPath]) {
this.configByFilePath[filePath] = config
return config
}
lastPath = thisPath
thisPath = path.resolve(thisPath, '..')
}
this.configByFilePath[filePath] = null
return null
}
PackageTranspilationRegistry.prototype.getCachePath = function (sourceCode, filePath, config) {
var transpilerPath = path.join(config.path, config.transpiler)
var transpilerSource = config._transpilerSource || fs.readFileSync(transpilerPath, 'utf8')
config._transpilerSource = transpilerSource
return path.join(
"package-transpile",
crypto
.createHash('sha1')
.update(transpilerSource, 'utf8')
.update(sourceCode, 'utf8')
.digest('hex')
)
}
PackageTranspilationRegistry.prototype.transpileWithPackageTranspiler = function (sourceCode, filePath) {
var config = this.configByFilePath[filePath]
Resolve = Resolve || require('resolve')
var transpilerPath = Resolve.sync(config.transpiler, {basedir: config.path, extensions: Object.keys(require.extensions)})
if (transpilerPath) {
this.transpilerPaths[transpilerPath] = true
var transpiler = require(transpilerPath)
var result = transpiler.compile(sourceCode, filePath)
if (result === undefined) {
return sourceCode
} else {
return result
}
} else {
var err = new Error("Could not find transpiler '" + config.transpiler + "' from '" + config.path + "'")
console.error(err)
}
}
module.exports = PackageTranspilationRegistry

View File

@ -6,6 +6,7 @@ CSON = require 'season'
fs = require 'fs-plus'
{Emitter, CompositeDisposable} = require 'event-kit'
CompileCache = require './compile-cache'
ModuleCache = require './module-cache'
ScopedProperties = require './scoped-properties'
BufferedProcess = require './buffered-process'
@ -86,6 +87,7 @@ class Package
@loadStylesheets()
@registerDeserializerMethods()
@activateCoreStartupServices()
@registerTranspilerConfig()
@configSchemaRegisteredOnLoad = @registerConfigSchemaFromMetadata()
@settingsPromise = @loadSettings()
if @shouldRequireMainModuleOnLoad() and not @mainModule?
@ -94,6 +96,9 @@ class Package
@handleError("Failed to load the #{@name} package", error)
this
unload: ->
@unregisterTranspilerConfig()
shouldRequireMainModuleOnLoad: ->
not (
@metadata.deserializers? or
@ -247,6 +252,16 @@ class Package
@activationDisposables.add @packageManager.serviceHub.consume(name, version, @mainModule[methodName].bind(@mainModule))
return
registerTranspilerConfig: ->
if @metadata.atomTranspilers
for transpiler in @metadata.atomTranspilers
CompileCache.addTranspilerConfigForPath(@path, transpiler)
unregisterTranspilerConfig: ->
if @metadata.atomTranspilers
for transpiler in @metadata.atomTranspilers
CompileCache.removeTranspilerConfigForPath(@path)
loadKeymaps: ->
if @bundledPackage and @packageManager.packagesCache[@name]?
@keymaps = (["#{@packageManager.resourcePath}#{path.sep}#{keymapPath}", keymapObject] for keymapPath, keymapObject of @packageManager.packagesCache[@name].keymaps)