diff --git a/Gemfile b/Gemfile
index 178f64053..d96c77701 100644
--- a/Gemfile
+++ b/Gemfile
@@ -106,7 +106,8 @@ gem 'sidekiq-statistic'
# for sidekiq web
gem 'sinatra', require: false
-gem 'therubyracer'
+gem 'execjs', github: 'rails/execjs', require: false
+gem 'mini_racer'
gem 'thin', require: false
gem 'highline', require: false
gem 'rack-protection' # security
diff --git a/Gemfile.lock b/Gemfile.lock
index 5ed367706..3491b735a 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,3 +1,9 @@
+GIT
+ remote: git://github.com/rails/execjs.git
+ revision: 22476229323cbec3befa8b690cc6c7c3957a0044
+ specs:
+ execjs (2.6.0)
+
GEM
remote: https://rubygems.org/
specs:
@@ -94,7 +100,6 @@ GEM
erubis (2.7.0)
eventmachine (1.2.0.1)
excon (0.45.4)
- execjs (2.6.0)
exifr (1.2.4)
fabrication (2.9.8)
fakeweb (1.3.0)
@@ -144,7 +149,7 @@ GEM
librarian (0.1.2)
highline
thor (~> 0.15)
- libv8 (3.16.14.13)
+ libv8 (5.0.71.48.3)
listen (0.7.3)
logster (1.2.3)
loofah (2.0.3)
@@ -159,6 +164,8 @@ GEM
method_source (0.8.2)
mime-types (2.99.1)
mini_portile2 (2.1.0)
+ mini_racer (0.1.3)
+ libv8 (~> 5.0)
minitest (5.8.4)
mocha (1.1.0)
metaclass (~> 0.0.1)
@@ -284,7 +291,6 @@ GEM
redis (3.3.0)
redis-namespace (1.5.2)
redis (~> 3.0, >= 3.0.4)
- ref (2.0.0)
rest-client (1.8.0)
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 3.0)
@@ -373,9 +379,6 @@ GEM
activesupport (>= 4.0)
sprockets (>= 3.0.0)
stackprof (0.2.9)
- therubyracer (0.12.2)
- libv8 (~> 3.16.14.0)
- ref
thin (1.6.4)
daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4)
@@ -417,6 +420,7 @@ DEPENDENCIES
ember-rails (= 0.18.5)
ember-source (= 1.12.2)
excon
+ execjs!
fabrication (= 2.9.8)
fakeweb (~> 1.3.0)
fast_blank
@@ -439,6 +443,7 @@ DEPENDENCIES
memory_profiler
message_bus (= 2.0.0.beta.11)
mime-types
+ mini_racer
minitest
mocha
mock_redis
@@ -493,7 +498,6 @@ DEPENDENCIES
sinatra
spork-rails
stackprof
- therubyracer
thin
timecop
uglifier
diff --git a/app/models/site_customization.rb b/app/models/site_customization.rb
index a74e188a2..562c544f4 100644
--- a/app/models/site_customization.rb
+++ b/app/models/site_customization.rb
@@ -59,7 +59,7 @@ SCRIPT
begin
code = transpile(node.inner_html, node['version'])
node.replace("")
- rescue Tilt::ES6ModuleTranspilerTemplate::JavaScriptError => ex
+ rescue MiniRacer::RuntimeError => ex
node.replace("")
end
end
diff --git a/lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb b/lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb
index 58eb0e678..68b2ca990 100644
--- a/lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb
+++ b/lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb
@@ -1,22 +1,9 @@
require 'execjs'
require 'babel/transpiler'
+require 'mini_racer'
module Tilt
- class Console
- def initialize(prefix=nil)
- @prefix = prefix || ''
- end
-
- def log(msg)
- Rails.logger.info("#{@prefix}#{msg}")
- end
-
- def error(msg)
- Rails.logger.error("#{@prefix}#{msg}")
- end
- end
-
class ES6ModuleTranspilerTemplate < Tilt::Template
self.default_mime_type = 'application/javascript'
@@ -30,10 +17,19 @@ module Tilt
def self.create_new_context
# timeout any eval that takes longer than 15 seconds
- ctx = V8::Context.new(timeout: 15000)
+ ctx = MiniRacer::Context.new(timeout: 15000)
ctx.eval("var self = this; #{File.read(Babel::Transpiler.script_path)}")
ctx.eval("module = {}; exports = {};");
ctx.load("#{Rails.root}/lib/es6_module_transpiler/support/es6-module-transpiler.js")
+ ctx.attach("rails.logger.info", proc{|err| Rails.logger.info(err.to_s)})
+ ctx.attach("rails.logger.error", proc{|err| Rails.logger.error(err.to_s)})
+ ctx.eval < e
- raise JavaScriptError.new(e.message, e.backtrace)
- end
+ yield
end
- rval
end
def whitelisted?(path)
@@ -98,7 +84,7 @@ module Tilt
def babel_transpile(source)
klass = self.class
klass.protect do
- klass.v8['console'] = Console.new("BABEL: babel-eval: ")
+ klass.v8.eval("console.prefix = 'BABEL: babel-eval: ';")
@output = klass.v8.eval(babel_source(source))
end
end
@@ -108,7 +94,7 @@ module Tilt
klass = self.class
klass.protect do
- klass.v8['console'] = Console.new("BABEL: #{scope.logical_path}: ")
+ klass.v8.eval("console.prefix = 'BABEL: #{scope.logical_path}: ';")
@output = klass.v8.eval(generate_source(scope))
end
diff --git a/lib/js_locale_helper.rb b/lib/js_locale_helper.rb
index db4821a37..eb2f1a19d 100644
--- a/lib/js_locale_helper.rb
+++ b/lib/js_locale_helper.rb
@@ -154,15 +154,25 @@ module JsLocaleHelper
result
end
- def self.compile_message_format(locale, format)
- ctx = V8::Context.new
- ctx.load(Rails.root + 'lib/javascripts/messageformat.js')
- path = Rails.root + "lib/javascripts/locale/#{locale}.js"
- ctx.load(path) if File.exists?(path)
- ctx.eval("mf = new MessageFormat('#{locale}');")
- ctx.eval("mf.precompile(mf.parse(#{format.inspect}))")
+ @mutex = Mutex.new
+ def self.with_context
+ @mutex.synchronize do
+ yield @ctx ||= begin
+ ctx = MiniRacer::Context.new
+ ctx.load(Rails.root + 'lib/javascripts/messageformat.js')
+ ctx
+ end
+ end
+ end
- rescue V8::Error => e
+ def self.compile_message_format(locale, format)
+ with_context do |ctx|
+ path = Rails.root + "lib/javascripts/locale/#{locale}.js"
+ ctx.load(path) if File.exists?(path)
+ ctx.eval("mf = new MessageFormat('#{locale}');")
+ ctx.eval("mf.precompile(mf.parse(#{format.inspect}))")
+ end
+ rescue MiniRacer::EvalError => e
message = "Invalid Format: " << e.message
"function(){ return #{message.inspect};}"
end
diff --git a/lib/pretty_text.rb b/lib/pretty_text.rb
index e3e5cc756..b328f230f 100644
--- a/lib/pretty_text.rb
+++ b/lib/pretty_text.rb
@@ -1,4 +1,4 @@
-require 'v8'
+require 'mini_racer'
require 'nokogiri'
require_dependency 'url_helper'
require_dependency 'excerpt_parser'
@@ -7,7 +7,9 @@ require_dependency 'discourse_tagging'
module PrettyText
- class Helpers
+ module Helpers
+ extend self
+
def t(key, opts)
key = "js." + key
unless opts
@@ -81,6 +83,7 @@ module PrettyText
nil
end
end
+
end
@mutex = Mutex.new
@@ -92,9 +95,11 @@ module PrettyText
def self.create_new_context
# timeout any eval that takes longer than 15 seconds
- ctx = V8::Context.new(timeout: 15000)
+ ctx = MiniRacer::Context.new(timeout: 15000)
- ctx["helpers"] = Helpers.new
+ Helpers.instance_methods.each do |method|
+ ctx.attach("helpers.#{method}", Helpers.method(method))
+ end
ctx_load(ctx,
"vendor/assets/javascripts/md5.js",
@@ -198,6 +203,7 @@ module PrettyText
# we use the exact same markdown converter as the client
# TODO: use the same extensions on both client and server (in particular the template for mentions)
baked = nil
+ text = text || ""
protect do
context = v8
@@ -206,8 +212,9 @@ module PrettyText
context_opts = opts || {}
context_opts[:sanitize] = true unless context_opts[:sanitize] == false
- context['opts'] = context_opts
- context['raw'] = text
+
+ context.eval("opts = #{context_opts.to_json};")
+ context.eval("raw = #{text.inspect};")
if Post.white_listed_image_classes.present?
Post.white_listed_image_classes.each do |klass|
@@ -258,8 +265,10 @@ module PrettyText
# leaving this here, cause it invokes v8, don't want to implement twice
def self.avatar_img(avatar_template, size)
protect do
- v8['avatarTemplate'] = avatar_template
- v8['size'] = size
+ v8.eval < e
- raise JavaScriptError.new(e.message, e.backtrace)
- end
+ rval = yield
end
rval
end
diff --git a/spec/components/js_locale_helper_spec.rb b/spec/components/js_locale_helper_spec.rb
index 1b26efe88..125474f93 100644
--- a/spec/components/js_locale_helper_spec.rb
+++ b/spec/components/js_locale_helper_spec.rb
@@ -1,5 +1,6 @@
require 'rails_helper'
require_dependency 'js_locale_helper'
+require 'mini_racer'
describe JsLocaleHelper do
@@ -25,7 +26,7 @@ describe JsLocaleHelper do
end
def setup_message_format(format)
- @ctx = V8::Context.new
+ @ctx = MiniRacer::Context.new
@ctx.eval('MessageFormat = {locale: {}};')
@ctx.load(Rails.root + 'lib/javascripts/locale/en.js')
compiled = JsLocaleHelper.compile_message_format('en', format)
@@ -72,7 +73,7 @@ describe JsLocaleHelper do
end
it 'handles message format special keys' do
- ctx = V8::Context.new
+ ctx = MiniRacer::Context.new
ctx.eval("I18n = {};")
JsLocaleHelper.set_translations 'en', {
@@ -149,7 +150,7 @@ describe JsLocaleHelper do
SiteSetting.default_locale = 'ru'
I18n.locale = :uk
- ctx = V8::Context.new
+ ctx = MiniRacer::Context.new
ctx.eval('var window = this;')
ctx.load(Rails.root + 'app/assets/javascripts/locales/i18n.js')
ctx.eval(JsLocaleHelper.output_locale(I18n.locale))
@@ -167,7 +168,7 @@ describe JsLocaleHelper do
LocaleSiteSetting.values.each do |locale|
it "generates valid date helpers for #{locale[:value]} locale" do
js = JsLocaleHelper.output_locale(locale[:value])
- ctx = V8::Context.new
+ ctx = MiniRacer::Context.new
ctx.eval('var window = this;')
ctx.load(Rails.root + 'app/assets/javascripts/locales/i18n.js')
ctx.eval(js)