class BrowserifyRails::BrowserifyProcessor

Attributes

config[RW]
data[RW]
file[RW]

Public Class Methods

call(input) click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 15
def self.call(input)
  instance.call(input)
end
instance() click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 11
def self.instance
  @instance ||= new
end
new() click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 19
def initialize
  self.config = Rails.application.config.browserify_rails
end

Public Instance Methods

call(input) click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 23
def call(input)
  self.file = input[:filename]
  return input[:data] unless in_path?
  self.data = input[:data]

  # Clear the cached dependencies because the source file changes
  @dependencies = nil

  ensure_tmp_dir_exists!
  ensure_commands_exist!

  # If there's nothing to do, we just return the data we received
  return data unless should_browserify?

  dependencies = Set.new(input[:metadata][:dependencies])

  # Signal dependencies to sprockets to ensure we track changes
  evaluate_dependencies(input[:environment].paths).each do |path|
    resolved = input[:environment].resolve(path)

    if resolved && resolved.is_a?(Array)
      dependencies += resolved[1]
    elsif resolved
      dependencies << "file-digest://#{Addressable::URI.escape resolved}"
    elsif config.evaluate_node_modules
      dependencies << "file-digest://#{Addressable::URI.escape path}"
    end
  end

  new_data = run_browserify(input[:name])
  { data: new_data, dependencies: dependencies }
end

Private Instance Methods

asset_paths() click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 124
def asset_paths
  @asset_paths ||= Rails.application.config.assets.paths.collect { |p| p.to_s }.join(":") || ""
end
browserified?() click to toggle source

Is this file already packaged for the browser?

# File lib/browserify-rails/browserify_processor.rb, line 112
def browserified?
  data.to_s.include?("define.amd") || data.to_s.include?("_dereq_")
end
browserify_cmd() click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 62
def browserify_cmd
  @browserify_cmd ||= File.join(config.node_bin, "browserify").freeze
end
browserify_command(force=nil) click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 272
def browserify_command(force=nil)
  rails_path(uses_browserifyinc(force) ? browserifyinc_cmd : browserify_cmd)
end
browserifyinc_cmd() click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 66
def browserifyinc_cmd
  @browserifyinc_cmd ||= File.join(config.node_bin, "browserifyinc").freeze
end
commonjs_module?() click to toggle source

Is this a commonjs module?

Be here as strict as possible, so that non-commonjs files are not preprocessed.

# File lib/browserify-rails/browserify_processor.rb, line 120
def commonjs_module?
  data.to_s.include?("module.exports") || data.present? && data.to_s.match(/(require\(.*\)|import)/) && dependencies.length > 0
end
dependencies() click to toggle source

@return [<String>] Paths of files, that this file depends on

# File lib/browserify-rails/browserify_processor.rb, line 140
def dependencies
  @dependencies ||= begin
    # We forcefully run browserify (avoiding browserifyinc) with the --list
    # option to get a list of files.
    list = run_browserify(nil, "--list")

    cleaned_paths = list.lines.map do |path|
      # clean and normalize paths for all operating systems
      Pathname.new(path.strip).cleanpath.to_s
    end

    cleaned_paths.select do |path|
      # Filter the temp file, where browserify caches the input stream
      File.exist?(path)
    end
  end
end
ensure_commands_exist!() click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 78
def ensure_commands_exist!
  error = ->(cmd) { "Unable to run #{cmd}. Ensure you have installed it with npm." }

  # Browserify has to be installed in any case
  if !File.exist?(rails_path(browserify_cmd))
    raise BrowserifyRails::BrowserifyError.new(error.call(browserify_cmd))
  end

  # If the user wants to use browserifyinc, we need to ensure it's there too
  if config.use_browserifyinc && !File.exist?(rails_path(browserifyinc_cmd))
    raise BrowserifyRails::BrowserifyError.new(error.call(browserifyinc_cmd))
  end
end
ensure_tmp_dir_exists!() click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 74
def ensure_tmp_dir_exists!
  FileUtils.mkdir_p(rails_path(tmp_path))
end
env() click to toggle source

Environtment to run browserify in:

NODE_PATH nodejs.org/api/all.html#all_loading_from_the_global_folders but basically allows one to have multiple locations for non-relative requires to be resolved to.

NODE_ENV is set to the Rails.env. This is used by some modules to determine how to build. Example: facebook.github.io/react/downloads.html#npm

# File lib/browserify-rails/browserify_processor.rb, line 166
def env
  env_hash = {}
  env_hash["NODE_PATH"] = asset_paths unless uses_exorcist
  env_hash["NODE_ENV"] = config.node_env || Rails.env
  env_hash
end
evaluate_dependencies(asset_paths) click to toggle source

This primarily filters out required files from node modules

@return [<String>] Paths of dependencies to evaluate

# File lib/browserify-rails/browserify_processor.rb, line 131
def evaluate_dependencies(asset_paths)
  return dependencies if config.evaluate_node_modules

  dependencies.select do |path|
    path.start_with?(*asset_paths)
  end
end
exorcist_cmd() click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 70
def exorcist_cmd
  @exorcist_cmd ||= rails_path(File.join(config.node_bin, "exorcist").freeze)
end
exorcist_options() click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 294
def exorcist_options
  exorcist_options = []
  root_path = config.root || File.expand_path('../..', __FILE__)
  exorcist_base_path = config.exorcist_base_path || root_path
  exorcist_options.push("-b #{exorcist_base_path}")
  exorcist_options.join(" ")
end
force_browserify?() click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 96
def force_browserify?
  if config.force.is_a? Proc
    config.force.call file
  else
    config.force
  end
end
get_granular_config(logical_path) click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 302
def get_granular_config(logical_path)
  granular_config = config.granular["javascript"]

  granular_config && granular_config[logical_path]
end
granular_options(logical_path) click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 308
def granular_options(logical_path)
  granular_config = get_granular_config(logical_path)

  return nil if granular_config.blank?

  # We set separate options for each of the items in granular_config
  options = granular_config.keys.collect do |key|
    granular_config[key].collect { |value| "--#{key} #{value}" }
  end

  options.flatten.join(" ") if options
end
in_path?() click to toggle source

Is this file in any of the configured paths?

# File lib/browserify-rails/browserify_processor.rb, line 105
def in_path?
  config.paths.any? do |path_spec|
    path_spec === file
  end
end
options() click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 284
def options
  options = []

  options.push("-d") if config.source_map_environments.include?(Rails.env)

  options += options_to_array(config.commandline_options) if config.commandline_options.present?

  options.uniq.join(" ")
end
options_to_array(options) click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 276
def options_to_array(options)
  if options.respond_to? :call
    options.call(file)
  else
    Array(options)
  end
end
rails_path(*paths) click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 321
def rails_path(*paths)
  Rails.root.join(*paths).to_s
end
run_browserify(logical_path=nil, extra_options=nil, force_browserifyinc=nil) click to toggle source

Run the requested version of browserify (browserify or browserifyinc) based on configuration or the use_browserifyinc parameter if present.

We are passing the data via stdin, so that earlier preprocessing steps are respected. If you had, say, an “application.js.coffee.erb”, passing the filename would fail, because browserify would read the original file with ERB tags and fail. By passing the data via stdin, we get the expected behavior of success, because everything has been compiled to plain javascript at the time this processor is called.

@raise [BrowserifyRails::BrowserifyError] if browserify does not succeed @param logical_path [String] Sprockets's logical path for the file @param extra_options [String] Options to be included in the command @param force_browserifyinc [Boolean] Causes browserifyinc to be used if true @return [String] Output of the command

# File lib/browserify-rails/browserify_processor.rb, line 188
def run_browserify(logical_path=nil, extra_options=nil, force_browserifyinc=nil)
  command_options = "#{options} #{extra_options} #{granular_options(logical_path)}".strip

  # Browserifyinc uses a special cache file. We set up the path for it if
  # we're going to use browserifyinc.
  if uses_browserifyinc(force_browserifyinc)
    cache_file_path = rails_path(tmp_path, "browserifyinc-cache.json")
    command_options << " --cachefile=#{Shellwords.escape(cache_file_path)}"
  end

  # Create a temporary file for the output. Such file is necessary when
  # using browserifyinc, but we use it in all instances for consistency
  output_file = Tempfile.new("output", rails_path(tmp_path))
  command_options << " -o #{output_file.path.inspect}"

  # Compose the full command (using browserify or browserifyinc as necessary)
  command = "#{Shellwords.escape(browserify_command(force_browserifyinc))} #{command_options} -"

  # The directory the command will be executed from
  base_directory = File.dirname(file)

  Logger::log "Browserify: #{command}"

  # If we are on JRuby 1.x, capture3 does not support chdir option
  stdout, stderr, status = if RUBY_PLATFORM == "java" && JRUBY_VERSION =~ /^1/
    Dir.chdir(base_directory) {
      Open3.capture3(env, command, stdin_data: data)
    }
  else
    Open3.capture3(env, command, stdin_data: data, chdir: base_directory)
  end

  if !status.success?
    raise BrowserifyRails::BrowserifyError.new("Error while running `#{command}`:\n\n#{stderr}")
  end

  # If using exorcist, pipe output from browserify command into exorcist
  if uses_exorcist && logical_path
    if stdout.present?
      bfy_output = stdout
    else
      bfy_output = output_file.read
    end
    sourcemap_output_file = "#{File.dirname(file)}/#{logical_path.split('/')[-1]}.map"
    exorcist_command = "#{Shellwords.shellescape(exorcist_cmd)} #{Shellwords.shellescape(sourcemap_output_file)} #{exorcist_options}"
    Logger::log "Exorcist: #{exorcist_command}"
    exorcist_stdout, exorcist_stderr, exorcist_status = Open3.capture3(env,
                                                                       exorcist_command,
                                                                       stdin_data: bfy_output,
                                                                       chdir: base_directory)

    if !exorcist_status.success?
      raise BrowserifyRails::BrowserifyError.new("Error while running `#{exorcist_command}`:\n\n#{exorcist_stderr}")
    end
  end

  # Read the output that was stored in the temp file
  output_file.open
  output = output_file.read

  # Destroy the temp file (good practice)
  output_file.close
  output_file.unlink

  # Some command flags (such as --list) make the output go to stdout,
  # ignoring -o. If this happens, we give out stdout instead.
  # If we're using exorcist, then we directly use its output
  if uses_exorcist && exorcist_stdout.present?
    exorcist_stdout
  elsif stdout.present?
    stdout
  else
    output
  end
end
should_browserify?() click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 92
def should_browserify?
  force_browserify? || (in_path? && !browserified? && commonjs_module?)
end
tmp_path() click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 58
def tmp_path
  @tmp_path ||= Rails.root.join("tmp", "cache", "browserify-rails").freeze
end
uses_browserifyinc(force=nil) click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 264
def uses_browserifyinc(force=nil)
  !force.nil? ? force : config.use_browserifyinc
end
uses_exorcist() click to toggle source
# File lib/browserify-rails/browserify_processor.rb, line 268
def uses_exorcist
  config.use_exorcist
end