From 2a36ac52a6a33aea3fdbc007c35295797768718a Mon Sep 17 00:00:00 2001 From: Alexey Karasev Date: Wed, 4 May 2016 20:31:32 +0300 Subject: [PATCH] added non-digested assets symlinking --- CHANGELOG.md | 3 + docs/additional-reading/rails-assets.md | 19 +++++ .../config/initializers/react_on_rails.rb.tt | 6 ++ lib/react_on_rails/configuration.rb | 10 ++- lib/tasks/assets.rake | 75 +++++++++++++++++++ 5 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 docs/additional-reading/rails-assets.md create mode 100644 lib/tasks/assets.rake diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ce124d4b..1043f21ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ All notable changes to this project will be documented in this file. Items under Contributors: please follow the recommendations outlined at [keepachangelog.com](http://keepachangelog.com/). Please use the existing headings and styling as a guide, and add a link for the version diff at the bottom of the file. Also, please update the `Unreleased` link to compare to the latest release version. ## [Unreleased] +##### Added +- Non-digested version of assets in public folder [#413](https://github.com/shakacode/react_on_rails/pull/413) by [alleycat-at-git] + ##### Changed - Replace URI with Addressable gem. See [#405](https://github.com/shakacode/react_on_rails/pull/405) by [lucke84] diff --git a/docs/additional-reading/rails-assets.md b/docs/additional-reading/rails-assets.md new file mode 100644 index 000000000..2cfb05985 --- /dev/null +++ b/docs/additional-reading/rails-assets.md @@ -0,0 +1,19 @@ +## Rails assets + +### Problem +When client js uses images in render methods, e.g. `` or in css, e.g. `background-image: url(...)` +these assets fail to load. This happens because rails adds digest hashes to filenames +when compiling assets, e.g. `img1.jpg` becomes `img1-dbu097452jf2v2.jpg`. + +When compiling its native css Rails transforms all urls and links to digested +versions, i.e. `background-image: image-url(img1.jpg)` becomes +`background-image: url(img1-dbu097452jf2v2.jpg)`. However this doesn't happen for js and +css files compiled by webpack on the client side, because they don't use +`image-url` and `asset-url` and therefore assets fail to load. + +### Solution + +Create symlinks of non-digested versions to digested versions when Rails assets compile. +The solution is implemented using `assets:precompile` after-hook. The assets for symlinking +are defined by `config.symlink_non_digested_assets_regex` in `config/initializers/react_on_rails.rb`. +To disable symlinks set this parameter to `nil`. diff --git a/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt b/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt index 7a45e40d0..a71f5f18f 100644 --- a/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt +++ b/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt @@ -43,4 +43,10 @@ ReactOnRails.configure do |config| # The server render method - either ExecJS or NodeJS config.server_render_method = "ExecJS" + + # Client js uses assets not digested by rails. + # For any asset matching this regex, non-digested symlink will be created + # To disable symlinks set this parameter to nil. + config.symlink_non_digested_assets_regex = /\.(png|jpg|jpeg|gif|tiff|woff|ttf|eot|svg)/ + end diff --git a/lib/react_on_rails/configuration.rb b/lib/react_on_rails/configuration.rb index 4fd9e080a..054797720 100644 --- a/lib/react_on_rails/configuration.rb +++ b/lib/react_on_rails/configuration.rb @@ -57,7 +57,8 @@ def self.configuration skip_display_none: false, webpack_generated_files: [], rendering_extension: nil, - server_render_method: "" + server_render_method: "", + symlink_non_digested_assets_regex: /\.(png|jpg|jpeg|gif|tiff|woff|ttf|eot|svg)/ ) end @@ -67,7 +68,8 @@ class Configuration :logging_on_server, :server_renderer_pool_size, :server_renderer_timeout, :raise_on_prerender_error, :skip_display_none, :generated_assets_dirs, :generated_assets_dir, - :webpack_generated_files, :rendering_extension, :server_render_method + :webpack_generated_files, :rendering_extension, + :server_render_method, :symlink_non_digested_assets_regex def initialize(server_bundle_js_file: nil, prerender: nil, replay_console: nil, trace: nil, development_mode: nil, @@ -75,7 +77,8 @@ def initialize(server_bundle_js_file: nil, prerender: nil, replay_console: nil, server_renderer_timeout: nil, raise_on_prerender_error: nil, skip_display_none: nil, generated_assets_dirs: nil, generated_assets_dir: nil, webpack_generated_files: nil, - rendering_extension: nil, server_render_method: nil) + rendering_extension: nil, server_render_method: nil, + symlink_non_digested_assets_regex: nil) self.server_bundle_js_file = server_bundle_js_file self.generated_assets_dirs = generated_assets_dirs self.generated_assets_dir = generated_assets_dir @@ -100,6 +103,7 @@ def initialize(server_bundle_js_file: nil, prerender: nil, replay_console: nil, self.rendering_extension = rendering_extension self.server_render_method = server_render_method + self.symlink_non_digested_assets_regex = symlink_non_digested_assets_regex end end end diff --git a/lib/tasks/assets.rake b/lib/tasks/assets.rake new file mode 100644 index 000000000..112a37d9c --- /dev/null +++ b/lib/tasks/assets.rake @@ -0,0 +1,75 @@ +module ReactOnRails + class << self + def assets_path + dir = File.join(Rails.configuration.paths['public'].first, + Rails.configuration.assets.prefix) + Pathname.new(dir) + end + + def symlink_file(target, symlink) + if not File.exist?(symlink) or File.lstat(symlink).symlink? + if File.exist?(target) + puts "React On Rails: Symlinking #{target} to #{symlink}" + FileUtils.ln_s target, symlink, force: true + end + else + puts "React On Rails: File #{symlink} already exists. Failed to symlink #{target}" + end + end + end +end + +if ReactOnRails.configuration.symlink_non_digested_assets_regex + Rake::Task["assets:precompile"].enhance do + Rake::Task["react_on_rails:assets:symlink_non_digested_assets"].invoke + Rake::Task["react_on_rails:assets:delete_broken_symlinks"].invoke + end +end + +namespace :react_on_rails do + namespace :assets do + + desc "Creates non-digested symlinks for the assets in the public asset dir" + task symlink_non_digested_assets: :"assets:environment" do + manifest_path = Dir.glob(ReactOnRails::assets_path.join(".sprockets-manifest-*.json")) + .first + manifest_data = JSON.load(File.new(manifest_path)) + + manifest_data["assets"].each do |logical_path, digested_path| + regex = ReactOnRails.configuration.symlink_non_digested_assets_regex + if logical_path =~ regex + full_digested_path = ReactOnRails::assets_path.join(digested_path) + full_nondigested_path = ReactOnRails::assets_path.join(logical_path) + extension = full_digested_path.extname + full_digested_gz_path = full_digested_path.sub_ext("#{extension}.gz") + full_nondigested_gz_path = full_nondigested_path.sub_ext("#{extension}.gz") + ReactOnRails::symlink_file(full_digested_path, full_nondigested_path) + ReactOnRails::symlink_file(full_digested_gz_path, full_nondigested_gz_path) + end + end + end + + desc "Cleans all broken symlinks for the assets in the public asset dir" + task delete_broken_symlinks: :"assets:environment" do + Dir.glob(ReactOnRails::assets_path.join("*")).each do |filename| + if File.lstat(filename).symlink? + begin + target = File.readlink(filename) + rescue + puts "React on Rails: Warning: your platform doesn't support File::readlink method."/ + "Skipping broken link check." + return + end + path = Pathname.new(File.dirname(filename)) + target_path = path.join(target) + unless File.exist?(target_path) + puts "React on Rails: Deleting broken link: #{filename}" + File.delete(filename) + end + end + end + end + + end +end +