class ReactOnRails::ServerRenderingPool::RubyEmbeddedJavaScript
rubocop:disable Metrics/ClassLength
Public Class Methods
console_polyfill()
click to toggle source
Reimplement console methods for replaying on the client Save a handle to the original console if needed.
# File lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb, line 203 def console_polyfill <<~JS var debugConsole = console; var console = { history: [] }; ['error', 'log', 'info', 'warn'].forEach(function (level) { console[level] = function () { var argArray = Array.prototype.slice.call(arguments); if (argArray.length > 0) { argArray[0] = '[SERVER] ' + argArray[0]; } console.history.push({level: level, arguments: argArray}); }; }); JS end
create_js_context()
click to toggle source
# File lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb, line 133 def create_js_context return if ReactOnRails.configuration.server_bundle_js_file.blank? bundle_js_code = read_bundle_js_code base_js_code = <<~JS #{console_polyfill} #{execjs_timer_polyfills} #{bundle_js_code}; JS file_name = "tmp/base_js_code.js" begin if ReactOnRails.configuration.trace Rails.logger.info do "[react_on_rails] Created JavaScript context with file " \ "#{ReactOnRails::Utils.server_bundle_js_file_path}" end end ExecJS.compile(base_js_code) rescue StandardError => e msg = "ERROR when compiling base_js_code! " \ "See file #{file_name} to " \ "correlate line numbers of error. Error is\n\n#{e.message}" \ "\n\n#{e.backtrace.join("\n")}" Rails.logger.error(msg) trace_js_code_used("Error when compiling JavaScript code for the context.", base_js_code, file_name, force: true) raise e end end
eval_js(js_code, _render_options)
click to toggle source
# File lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb, line 113 def eval_js(js_code, _render_options) @js_context_pool.with do |js_context| js_context.eval(js_code) end end
exec_server_render_js(js_code, render_options, js_evaluator = nil)
click to toggle source
js_code: JavaScript expression that returns a string. render_options: lib/react_on_rails/react_component/render_options.rb Using these options:
trace: saves the executed JS to a file, used in development logging_on_server: put on server logs, not just in browser console
Returns a Hash:
html: string of HTML for direct insertion on the page by evaluating js_code consoleReplayScript: script for replaying console hasErrors: true if server rendering errors
Note, js_code does not have to be based on React. js_code MUST RETURN json stringify Object
Calling code will probably call ‘html_safe’ on return value before rendering to the view. rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
# File lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb, line 50 def exec_server_render_js(js_code, render_options, js_evaluator = nil) js_evaluator ||= self if render_options.trace @file_index ||= 1 trace_js_code_used("Evaluating code to server render.", js_code, "tmp/server-generated-#{@file_index % 10}.js") @file_index += 1 end begin json_string = js_evaluator.eval_js(js_code, render_options) rescue StandardError => err msg = <<~MSG Error evaluating server bundle. Check your webpack configuration. =============================================================== Caught error: #{err.message} =============================================================== MSG if err.message.include?("ReferenceError: self is not defined") msg << "\nError indicates that you may have code-splitting incorrectly enabled.\n" end raise ReactOnRails::Error, msg, err.backtrace end result = nil begin result = JSON.parse(json_string) rescue JSON::ParserError => e raise ReactOnRails::JsonParseError.new(parse_error: e, json: json_string) end if render_options.logging_on_server console_script = result["consoleReplayScript"] console_script_lines = console_script.split("\n") console_script_lines = console_script_lines[2..-2] re = /console\.(?:log|error)\.apply\(console, \["\[SERVER\] (?<msg>.*)"\]\);/ console_script_lines&.each do |line| match = re.match(line) Rails.logger.info { "[react_on_rails] #{match[:msg]}" } if match end end result end
execjs_timer_polyfills()
click to toggle source
# File lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb, line 164 def execjs_timer_polyfills <<~JS function getStackTrace () { var stack; try { throw new Error(''); } catch (error) { stack = error.stack || ''; } stack = stack.split('\\n').map(function (line) { return line.trim(); }); return stack.splice(stack[0] == 'Error' ? 2 : 1); } function setInterval() { #{undefined_for_exec_js_logging('setInterval')} } function setTimeout() { #{undefined_for_exec_js_logging('setTimeout')} } function clearTimeout() { #{undefined_for_exec_js_logging('clearTimeout')} } JS end
read_bundle_js_code()
click to toggle source
# File lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb, line 119 def read_bundle_js_code server_js_file = ReactOnRails::Utils.server_bundle_js_file_path if ReactOnRails::Utils.server_bundle_path_is_http? file_url_to_string(server_js_file) else File.read(server_js_file) end rescue StandardError => e msg = "You specified server rendering JS file: #{server_js_file}, but it cannot be " \ "read. You may set the server_bundle_js_file in your configuration to be \"\" to " \ "avoid this warning.\nError is: #{e}" raise ReactOnRails::Error, msg end
reset_pool()
click to toggle source
# File lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb, line 11 def reset_pool options = { size: ReactOnRails.configuration.server_renderer_pool_size, timeout: ReactOnRails.configuration.server_renderer_timeout } @js_context_pool = ConnectionPool.new(options) { create_js_context } end
reset_pool_if_server_bundle_was_modified()
click to toggle source
# File lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb, line 19 def reset_pool_if_server_bundle_was_modified return unless ReactOnRails.configuration.development_mode if ReactOnRails::Utils.server_bundle_path_is_http? return if @server_bundle_url == ReactOnRails::Utils.server_bundle_js_file_path @server_bundle_url = ReactOnRails::Utils.server_bundle_js_file_path else file_mtime = File.mtime(ReactOnRails::Utils.server_bundle_js_file_path) @server_bundle_timestamp ||= file_mtime return if @server_bundle_timestamp == file_mtime @server_bundle_timestamp = file_mtime end ReactOnRails::ServerRenderingPool.reset_pool end
trace_js_code_used(msg, js_code, file_name = "tmp/server-generated.js", force: false)
click to toggle source
rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
# File lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb, line 95 def trace_js_code_used(msg, js_code, file_name = "tmp/server-generated.js", force: false) return unless ReactOnRails.configuration.trace || force # Set to anything to print generated code. File.write(file_name, js_code) msg = <<-MSG.strip_heredoc #{'Z' * 80} [react_on_rails] #{msg} JavaScript code used: #{file_name} #{'Z' * 80} MSG if force Rails.logger.error(msg) else Rails.logger.info(msg) end end
undefined_for_exec_js_logging(function_name)
click to toggle source
# File lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb, line 192 def undefined_for_exec_js_logging(function_name) if ReactOnRails.configuration.trace "console.error('[React on Rails Rendering] #{function_name} is not defined for server rendering.');\n " \ "console.error(getStackTrace().join('\\n'));" else "" end end
Private Class Methods
file_url_to_string(url)
click to toggle source
# File lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb, line 226 def file_url_to_string(url) response = Net::HTTP.get_response(URI.parse(url)) content_type_header = response["content-type"] match = content_type_header.match(/\A.*; charset=(?<encoding>.*)\z/) encoding_type = match[:encoding] response.body.force_encoding(encoding_type) rescue StandardError => e msg = "file_url_to_string #{url} failed\nError is: #{e}" raise ReactOnRails::Error, msg end