2014-05-05 17:04:09 -05:00
require 'execjs'
module Tilt
class ES6ModuleTranspilerTemplate < Tilt :: Template
self . default_mime_type = 'application/javascript'
@mutex = Mutex . new
@ctx_init = Mutex . new
def prepare
# intentionally left empty
# Tilt requires this method to be defined
end
def self . create_new_context
ctx = V8 :: Context . new ( timeout : 5000 )
ctx . eval ( " module = {}; exports = {}; " ) ;
ctx . load ( " #{ Rails . root } /lib/es6_module_transpiler/support/es6-module-transpiler.js " )
ctx
end
def self . v8
return @ctx if @ctx
# ensure we only init one of these
@ctx_init . synchronize do
return @ctx if @ctx
@ctx = create_new_context
end
@ctx
end
class JavaScriptError < StandardError
attr_accessor :message , :backtrace
def initialize ( message , backtrace )
@message = message
@backtrace = backtrace
end
end
def self . protect
rval = nil
@mutex . synchronize do
begin
rval = yield
# This may seem a bit odd, but we don't want to leak out
# objects that require locks on the v8 vm, to get a backtrace
# you need a lock, if this happens in the wrong spot you can
# deadlock a process
rescue V8 :: Error = > e
raise JavaScriptError . new ( e . message , e . backtrace )
end
end
rval
end
def evaluate ( scope , locals , & block )
return @output if @output
klass = self . class
klass . protect do
@output = klass . v8 . eval ( generate_source ( scope ) )
end
2014-05-12 14:53:22 -05:00
# For backwards compatibility with plugins, for now export the Global format too.
# We should eventually have an upgrade system for plugins to use ES6 or some other
# resolve based API.
2014-05-16 15:39:41 -05:00
if ENV [ 'DISCOURSE_NO_CONSTANTS' ] . nil? && scope . logical_path =~ / discourse \/ (controllers|components|views) \/ (.*) /
type = Regexp . last_match [ 1 ]
class_name = Regexp . last_match [ 2 ] . gsub ( / [ \ - \/ ] / , '_' ) . classify
@output << " \n \n Discourse. #{ class_name } #{ type . classify } = require(' #{ scope . logical_path } ').default "
2014-05-12 14:53:22 -05:00
end
2014-05-05 17:04:09 -05:00
@output
end
private
def generate_source ( scope )
" new module.exports.Compiler( #{ :: JSON . generate ( data , quirks_mode : true ) } , ' #{ module_name ( scope . root_path , scope . logical_path ) } ', #{ compiler_options } ). #{ compiler_method } () "
end
def module_name ( root_path , logical_path )
2014-05-15 15:31:45 -05:00
path = nil
# If the resource is a plugin, use the plugin name as a prefix
if root_path =~ / (.* \/ discourse \/ plugins \/ [^ \/ ]+) \/ /
plugin_path = " #{ Regexp . last_match [ 1 ] } /plugin.rb "
plugin = Discourse . plugins . find { | p | p . path == plugin_path }
path = " discourse/plugins/ #{ plugin . name } / #{ logical_path . sub ( / javascripts \/ / , '' ) } " if plugin
2014-05-05 17:04:09 -05:00
end
2014-05-15 15:31:45 -05:00
path || = logical_path
2014-05-05 17:04:09 -05:00
if ES6ModuleTranspiler . transform
path = ES6ModuleTranspiler . transform . call ( path )
end
path
end
def compiler_method
type = {
amd : 'AMD' ,
cjs : 'CJS' ,
globals : 'Globals'
} [ ES6ModuleTranspiler . compile_to . to_sym ]
" to #{ type } "
end
def compiler_options
:: JSON . generate ( ES6ModuleTranspiler . compiler_options , quirks_mode : true )
end
end
end