rails + sprockets + uglifierでCopyrightを残したままcompressする

環境

  • Rails 4.0.0
  • ruby 2.0.0p0
  • uglifier 2.1.2
  • sprockets 2.10.0
  • sprockets-rails 2.0.0

デフォルトではcompress時にCopyrightは削除される

このようなenvironments/production.rbがあるとします。
(developmentでもtestでも同じだと思う)

MyApp::Application.configure do
  config.assets.compress = true
  config.assets.js_compressor = :uglifier
end

その上で rake assets:precompile RAILS_ENV=production を実行します。
出来上がったjsは結合され、縮小化され、難読化されていてイイコトづくしなのですがCopyrightも消えてしまいます。

MITなどライセンスの表記が必要なjsを使っている場合、これでは困ります。

uglifierはデフォルトではCopyrightを残す

Copyrightを残すかどうかはuglifierのオプションで指定できるのですが、
デフォルトでは残すことになっています。

詳しくは公式に載っています。

sprocketsのデフォルト設定が、Copyrightを無効にしてしまう

デフォルト設定とは、config.assets.js_compressor = :uglifierのことです。
sprocketsはjs_compressorに指定された値がシンボルだった場合、
そのシンボルをキーにしてデフォルトのCompressorクラスを設定します。

    # sprockets-2.10.0/lib/sprockets/compressing.rb

    def js_compressor=(compressor)
      unregister_bundle_processor 'application/javascript', js_compressor if js_compressor
      @js_compressor = nil
      return unless compressor

      if compressor.is_a?(Symbol)
        compressor = compressors['application/javascript'][compressor] || raise(Error, "unknown compressor: #{compressor}")
      end

      if compressor.respond_to?(:compress)
        klass = Class.new(Processor) do
          @name = "js_compressor"
          @processor = proc { |context, data| compressor.compress(data) }
        end
        @js_compressor = :js_compressor
      else
        @js_compressor = klass = compressor
      end

      register_bundle_processor 'application/javascript', klass
    end

ここで設定されるデフォルトのUglifierCompressorというクラスが、実行時にCopyrightオプションを無効にしています。

    # sprockets-2.10.0/lib/sprockets/uglifier_compressor.rb

    def evaluate(context, locals, &block)
      # Feature detect Uglifier 2.0 option support
      if Uglifier::DEFAULTS[:copyright]
        # Uglifier < 2.x
        Uglifier.new(:copyright => false).compile(data)
      else
        # Uglifier >= 2.x
        Uglifier.new(:comments => :none).compile(data)
      end
    end

公式のオプションでは :output => {:comments => :copyright} ですが
:comments => :copyright でも動くようにコード内で考慮されています。

シンボルじゃなくてUglifierのインスタンスを設定すればOK

configureの設定方法を次のように変更します。

MyApp::Application.configure do
  config.assets.compress = true
  config.assets.js_compressor = Uglifier.new(:comments => :copyright)
end

なお、uglifierのバージョンが古い場合はUglifier.new(:copyright => :true)と指定します。
(ちなみに新しいバージョンでも古い書き方で動くように考慮されているようです)

これで解決です。無事Copyrightが含まれるようになりました。

ただ、sprocketsがデフォルトでCopyrightを消すのが少し不思議です。
消されると困る人の方が多い気がするのですが、何か勘違いしているかな。

おまけ:configureの変更が反映されたりされなかったり?

MyApp::Application.configure の中身を書き換えて rake assets:precompile RAILS_ENV=productionをしても、
なぜか変更が反映されないことが頻繁にありました。

特定の設定が影響しているわけでもないようで、よく分からず。

以下の手順で必ず反映されるようになりましたが、何が影響しているのだろう。

  • MyApp::Application.configureの中身を空にする
  • rake assets:precompile RAILS_ENV=production (compressされない状態で生成される)
  • public/assetsを削除
  • MyApp::Application.configureの中身を期待するものに更新
  • rake assets:precompile RAILS_ENV=production

追記: コメント欄にて補足頂きましたのでそちらもご覧ください