lib/webpacker/compiler.rb



require "open3"
require "digest/sha1"

class Webpacker::Compiler
  # Additional paths that test compiler needs to watch
  # Webpacker::Compiler.watched_paths << 'bower_components'
  cattr_accessor(:watched_paths) { [] }

  # Additional environment variables that the compiler is being run with
  # Webpacker::Compiler.env['FRONTEND_API_KEY'] = 'your_secret_key'
  cattr_accessor(:env) { {} }

  delegate :config, :logger, to: :@webpacker

  def initialize(webpacker)
    @webpacker = webpacker
  end

  def compile
    if stale?
      record_compilation_digest
      run_webpack
    else
      true
    end
  end

  # Returns true if all the compiled packs are up to date with the underlying asset files.
  def fresh?
    watched_files_digest == last_compilation_digest
  end

  # Returns true if the compiled packs are out of date with the underlying asset files.
  def stale?
    !fresh?
  end

  private
    def last_compilation_digest
      compilation_digest_path.read if compilation_digest_path.exist? && config.public_manifest_path.exist?
    end

    def watched_files_digest
      files = Dir[*default_watched_paths, *watched_paths].reject { |f| File.directory?(f) }
      Digest::SHA1.hexdigest(files.map { |f| "#{File.basename(f)}/#{File.mtime(f).utc.to_i}" }.join("/"))
    end

    def record_compilation_digest
      config.cache_path.mkpath
      compilation_digest_path.write(watched_files_digest)
    end

    def run_webpack
      logger.info "Compiling…"

      sterr, stdout, status = Open3.capture3(webpack_env, "#{RbConfig.ruby} ./bin/webpack")

      if status.success?
        logger.info "Compiled all packs in #{config.public_output_path}"
      else
        logger.error "Compilation failed:\n#{sterr}\n#{stdout}"
      end

      status.success?
    end

    def default_watched_paths
      [
        *config.resolved_paths_globbed,
        "#{config.source_path.relative_path_from(Rails.root)}/**/*",
        "yarn.lock", "package.json",
        "config/webpack/**/*"
      ].freeze
    end

    def compilation_digest_path
      config.cache_path.join(".last-compilation-digest-#{Webpacker.env}")
    end

    def webpack_env
      env.merge("WEBPACKER_ASSET_HOST"        => ActionController::Base.helpers.compute_asset_host,
                "WEBPACKER_RELATIVE_URL_ROOT" => ActionController::Base.relative_url_root)
    end
end