From d36227370c586bff6e1205c4a05b5f048c3f8d07 Mon Sep 17 00:00:00 2001 From: Vladimir Dementyev Date: Tue, 20 Nov 2018 17:10:53 -0500 Subject: [PATCH 1/7] refactor compiler to use webpacker instance instead of singleton --- lib/webpacker/compiler.rb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/webpacker/compiler.rb b/lib/webpacker/compiler.rb index a09590de3..afb077c9b 100644 --- a/lib/webpacker/compiler.rb +++ b/lib/webpacker/compiler.rb @@ -10,7 +10,7 @@ class Webpacker::Compiler # Webpacker::Compiler.env['FRONTEND_API_KEY'] = 'your_secret_key' cattr_accessor(:env) { {} } - delegate :config, :logger, to: :@webpacker + delegate :config, :logger, to: :webpacker def initialize(webpacker) @webpacker = webpacker @@ -37,6 +37,8 @@ def stale? end private + attr_reader :webpacker + def last_compilation_digest compilation_digest_path.read if compilation_digest_path.exist? && config.public_manifest_path.exist? rescue Errno::ENOENT, Errno::ENOTDIR @@ -56,7 +58,11 @@ def record_compilation_digest def run_webpack logger.info "Compiling…" - stdout, sterr , status = Open3.capture3(webpack_env, "#{RbConfig.ruby} #{@webpacker.root_path}/bin/webpack") + stdout, sterr , status = Open3.capture3( + webpack_env, + "#{RbConfig.ruby} ./bin/webpack", + chdir: File.expand_path(config.root_path) + ) if sterr == "" && status.success? logger.info "Compiled all packs in #{config.public_output_path}" @@ -71,17 +77,19 @@ def run_webpack def default_watched_paths [ *config.resolved_paths_globbed, - "#{config.source_path.relative_path_from(Rails.root)}/**/*", + "#{config.source_path.relative_path_from(config.root_path)}/**/*", "yarn.lock", "package.json", "config/webpack/**/*" ].freeze end def compilation_digest_path - config.cache_path.join("last-compilation-digest-#{Webpacker.env}") + config.cache_path.join(".last-compilation-digest-#{webpacker.env}") end def webpack_env + return env unless defined?(ActionController::Base) + env.merge("WEBPACKER_ASSET_HOST" => ActionController::Base.helpers.compute_asset_host, "WEBPACKER_RELATIVE_URL_ROOT" => ActionController::Base.relative_url_root) end From 3bba124a1b1eb79cfe81b3af6005bdf0d6e9969b Mon Sep 17 00:00:00 2001 From: Vladimir Dementyev Date: Tue, 20 Nov 2018 17:15:45 -0500 Subject: [PATCH 2/7] refactor dev_server_proxy to pass webpacker instance via options --- lib/webpacker/dev_server_proxy.rb | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/webpacker/dev_server_proxy.rb b/lib/webpacker/dev_server_proxy.rb index ed73ebac0..03787f103 100644 --- a/lib/webpacker/dev_server_proxy.rb +++ b/lib/webpacker/dev_server_proxy.rb @@ -1,18 +1,25 @@ require "rack/proxy" class Webpacker::DevServerProxy < Rack::Proxy + delegate :config, :dev_server, to: :@webpacker + + def initialize(app = nil, opts = {}) + @webpacker = opts.delete(:webpacker) || Webpacker.instance + super + end + def rewrite_response(response) _status, headers, _body = response headers.delete "transfer-encoding" - headers.delete "content-length" if Webpacker.dev_server.running? && Webpacker.dev_server.https? + headers.delete "content-length" if dev_server.running? && dev_server.https? response end def perform_request(env) - if env["PATH_INFO"].start_with?("/#{public_output_uri_path}") && Webpacker.dev_server.running? - env["HTTP_HOST"] = env["HTTP_X_FORWARDED_HOST"] = env["HTTP_X_FORWARDED_SERVER"] = Webpacker.dev_server.host_with_port - env["HTTP_X_FORWARDED_PROTO"] = env["HTTP_X_FORWARDED_SCHEME"] = Webpacker.dev_server.protocol - unless Webpacker.dev_server.https? + if env["PATH_INFO"].start_with?("/#{public_output_uri_path}") && dev_server.running? + env["HTTP_HOST"] = env["HTTP_X_FORWARDED_HOST"] = env["HTTP_X_FORWARDED_SERVER"] = dev_server.host_with_port + env["HTTP_X_FORWARDED_PROTO"] = env["HTTP_X_FORWARDED_SCHEME"] = dev_server.protocol + unless dev_server.https? env["HTTPS"] = env["HTTP_X_FORWARDED_SSL"] = "off" end env["SCRIPT_NAME"] = "" @@ -25,6 +32,6 @@ def perform_request(env) private def public_output_uri_path - Webpacker.config.public_output_path.relative_path_from(Webpacker.config.public_path) + config.public_output_path.relative_path_from(config.public_path) end end From de916f330eb140af7e33dd7a70ade4d0d13ea70a Mon Sep 17 00:00:00 2001 From: Vladimir Dementyev Date: Tue, 20 Nov 2018 18:14:00 -0500 Subject: [PATCH 3/7] add current_webpacker to helper --- lib/webpacker/helper.rb | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/webpacker/helper.rb b/lib/webpacker/helper.rb index 53ddf9a3d..a96011a43 100644 --- a/lib/webpacker/helper.rb +++ b/lib/webpacker/helper.rb @@ -1,4 +1,11 @@ module Webpacker::Helper + # Returns current Webpacker instance. + # Could be overriden to use multiple Webpacker + # configurations within the same app (e.g. with engines) + def current_webpacker + Webpacker.instance + end + # Computes the relative path for a given Webpacker asset. # Return relative path using manifest.json and passes it to asset_path helper # This will use asset_path internally, so most of their behaviors will be the same. @@ -11,8 +18,8 @@ module Webpacker::Helper # # When extract_css is true in webpacker.yml or the file is not a css: # <%= asset_pack_path 'calendar.css' %> # => "/packs/calendar-1016838bab065ae1e122.css" def asset_pack_path(name, **options) - if Webpacker.config.extract_css? || !stylesheet?(name) - asset_path(Webpacker.manifest.lookup!(name), **options) + if current_webpacker.config.extract_css? || !stylesheet?(name) + asset_path(current_webpacker.manifest.lookup!(name), **options) end end @@ -28,8 +35,8 @@ def asset_pack_path(name, **options) # # When extract_css is true in webpacker.yml or the file is not a css: # <%= asset_pack_url 'calendar.css' %> # => "http://example.com/packs/calendar-1016838bab065ae1e122.css" def asset_pack_url(name, **options) - if Webpacker.config.extract_css? || !stylesheet?(name) - asset_url(Webpacker.manifest.lookup!(name), **options) + if current_webpacker.config.extract_css? || !stylesheet?(name) + asset_url(current_webpacker.manifest.lookup!(name), **options) end end @@ -40,7 +47,7 @@ def asset_pack_url(name, **options) # <%= image_pack_tag 'application.png', size: '16x10', alt: 'Edit Entry' %> # Edit Entry def image_pack_tag(name, **options) - image_tag(asset_path(Webpacker.manifest.lookup!(name)), **options) + image_tag(asset_path(current_webpacker.manifest.lookup!(name)), **options) end # Creates a script tag that references the named pack file, as compiled by webpack per the entries list @@ -72,7 +79,7 @@ def javascript_pack_tag(*names, **options) # <%= stylesheet_pack_tag 'calendar', 'data-turbolinks-track': 'reload' %> # => # def stylesheet_pack_tag(*names, **options) - if Webpacker.config.extract_css? + if current_webpacker.config.extract_css? stylesheet_link_tag(*sources_from_pack_manifest(names, type: :stylesheet), **options) end end @@ -83,6 +90,6 @@ def stylesheet?(name) end def sources_from_pack_manifest(names, type:) - names.map { |name| Webpacker.manifest.lookup!(name, type: type) }.flatten + names.map { |name| current_webpacker.manifest.lookup!(name, type: type) }.flatten end end From f07bd9499cef018ea0b522653cc35a92d1ede254 Mon Sep 17 00:00:00 2001 From: Vladimir Dementyev Date: Mon, 3 Dec 2018 12:28:57 -0500 Subject: [PATCH 4/7] make env prefix for dev_server configurable --- lib/webpacker/dev_server.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/webpacker/dev_server.rb b/lib/webpacker/dev_server.rb index 12b03f203..44d86cae5 100644 --- a/lib/webpacker/dev_server.rb +++ b/lib/webpacker/dev_server.rb @@ -1,4 +1,6 @@ class Webpacker::DevServer + DEFAULT_ENV_PREFIX = "WEBPACKER_DEV_SERVER".freeze + # Configure dev server connection timeout (in seconds), default: 0.01 # Webpacker.dev_server.connect_timeout = 1 cattr_accessor(:connect_timeout) { 0.01 } @@ -49,9 +51,13 @@ def pretty? fetch(:pretty) end + def env_prefix + config.dev_server.fetch(:env_prefix, DEFAULT_ENV_PREFIX) + end + private def fetch(key) - ENV["WEBPACKER_DEV_SERVER_#{key.upcase}"] || config.dev_server.fetch(key, defaults[key]) + ENV["#{env_prefix}_#{key.upcase}"] || config.dev_server.fetch(key, defaults[key]) end def defaults From 4bceeffe08892b2358f8271c9601878507f65e51 Mon Sep 17 00:00:00 2001 From: Vladimir Dementyev Date: Sat, 15 Dec 2018 15:09:01 -0500 Subject: [PATCH 5/7] add engine usage docs --- docs/engines.md | 135 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 docs/engines.md diff --git a/docs/engines.md b/docs/engines.md new file mode 100644 index 000000000..9803b59e0 --- /dev/null +++ b/docs/engines.md @@ -0,0 +1,135 @@ +# Using in Rails engines + +If the application UI consists of multiple frontend application, you'd probably like to isolate their building too (e.g. if you use different frameworks/versions). Hence we needed our webpack(-er) to be isolated too: separate `package.json`, dev server, compilation process. + +You can do this by adding another Webpacker instance to your application. + +This guide describes how to do that using [Rails engines](https://guides.rubyonrails.org/engines.html). + + +## Step 1: create Rails engine. + +First, you create a Rails engine (say, `MyEngine`). See the offical [Rails guide](https://guides.rubyonrails.org/engines.html). + +## Step 2: install Webpacker within the engine. + +There is no built-in tasks to install Webpacker within the engine, thus you have to add all the require files manually (you can copy them from the main app): +- Add `config/webpacker.yml` and `config/webpack/*.js` files +- Add `bin/webpack` and `bin/webpack-dev-server` files +- Add `package.json` with required deps. + + +## Step 3: configure Webpacker instance. + +```ruby +module MyEngine + ROOT_PATH = Pathname.new(File.join(__dir__, "..")) + + class << self + def webpacker + @webpacker ||= ::Webpacker::Instance.new( + root_path: ROOT_PATH, + config_path: ROOT_PATH.join("config/webpacker.yml") + ) + end + end +end +``` + +## Step 4: Configure dev server proxy. + +```ruby +module MyEngine + class Engine < ::Rails::Engine + initializer "webpacker.proxy" do |app| + insert_middleware = begin + MyEngine.webpacker.config.dev_server.present? + rescue + nil + end + next unless insert_middleware + + app.middleware.insert_before( + 0, "Webpacker::DevServerProxy", + ssl_verify_none: true, + webpacker: MyEngine.webpacker + ) + end + end +end +``` + +If you have multiple webpackers, you would probably want to run multiple dev servers at a time, and hence be able to configure their setting through env vars (e.g. within a `docker-compose.yml` file): + +```yml +# webpacker.yml +# ... +development: + # ... + dev_server: + env_prefix: "MY_ENGINE_WEBPACKER_DEV_SERVER" + # ... +``` + +## Step 5: configure helper. + +```ruby +require "webpacker/helper" + +module MyEngine + module ApplicationHelper + include ::Webpacker::Helper + + def current_webpacker + MyEngine.webpacker + end + end +end +``` + +Now you can use `stylesheet_pack_tag` and `javascript_pack_tag` from within your engine. + +## Step 6: rake tasks. + +Add Rake task to compile assets in production (`rake my_engine:webpacker:compile`) + +```ruby +namespace :my_engine do + namespace :webpacker do + desc "Install deps with yarn" + task :yarn_install do + Dir.chdir(File.join(__dir__, "../..")) do + system "yarn install --no-progress --production" + end + end + + desc "Compile JavaScript packs using webpack for production with digests" + task compile: [:yarn_install, :environment] do + Webpacker.with_node_env("production") do + if MyEngine.webpacker.commands.compile + # Successful compilation! + else + # Failed compilation + exit! + end + end + end + end +end +``` + +## Step 7: serving compiled packs. + +To serve static assets in production via Rails you might need to add a middleware and point it to your engine's webpacker output path: + +```ruby +# application.rb + +config.middleware.use( + "Rack::Static", + urls: ["/my-engine-packs"], root: "my_engine/public" +) +``` + +**NOTE:** in the example above we assume that your `public_output_path` is set to `my-engine-packs` in your engine's `webpacker.yml`. + \ No newline at end of file From 5b76426dbc2a3a228b6f33b2d375146cc4bf9cdc Mon Sep 17 00:00:00 2001 From: Gaurav Tiwari Date: Sat, 15 Dec 2018 22:20:38 +0000 Subject: [PATCH 6/7] Remove dot --- lib/webpacker/compiler.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/webpacker/compiler.rb b/lib/webpacker/compiler.rb index afb077c9b..f7d83c7e9 100644 --- a/lib/webpacker/compiler.rb +++ b/lib/webpacker/compiler.rb @@ -84,7 +84,7 @@ def default_watched_paths end def compilation_digest_path - config.cache_path.join(".last-compilation-digest-#{webpacker.env}") + config.cache_path.join("last-compilation-digest-#{webpacker.env}") end def webpack_env From 7ac812fe3bcf25d5bc12b946d767a33c1d8d8022 Mon Sep 17 00:00:00 2001 From: Vladimir Dementyev Date: Sat, 15 Dec 2018 18:50:19 -0500 Subject: [PATCH 7/7] current_webpacker -> current_webpacker_instance --- docs/engines.md | 2 +- lib/webpacker/helper.rb | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/engines.md b/docs/engines.md index 9803b59e0..e50b2f16c 100644 --- a/docs/engines.md +++ b/docs/engines.md @@ -80,7 +80,7 @@ module MyEngine module ApplicationHelper include ::Webpacker::Helper - def current_webpacker + def current_webpacker_instance MyEngine.webpacker end end diff --git a/lib/webpacker/helper.rb b/lib/webpacker/helper.rb index a96011a43..85cd274ad 100644 --- a/lib/webpacker/helper.rb +++ b/lib/webpacker/helper.rb @@ -2,7 +2,7 @@ module Webpacker::Helper # Returns current Webpacker instance. # Could be overriden to use multiple Webpacker # configurations within the same app (e.g. with engines) - def current_webpacker + def current_webpacker_instance Webpacker.instance end @@ -18,8 +18,8 @@ def current_webpacker # # When extract_css is true in webpacker.yml or the file is not a css: # <%= asset_pack_path 'calendar.css' %> # => "/packs/calendar-1016838bab065ae1e122.css" def asset_pack_path(name, **options) - if current_webpacker.config.extract_css? || !stylesheet?(name) - asset_path(current_webpacker.manifest.lookup!(name), **options) + if current_webpacker_instance.config.extract_css? || !stylesheet?(name) + asset_path(current_webpacker_instance.manifest.lookup!(name), **options) end end @@ -35,8 +35,8 @@ def asset_pack_path(name, **options) # # When extract_css is true in webpacker.yml or the file is not a css: # <%= asset_pack_url 'calendar.css' %> # => "http://example.com/packs/calendar-1016838bab065ae1e122.css" def asset_pack_url(name, **options) - if current_webpacker.config.extract_css? || !stylesheet?(name) - asset_url(current_webpacker.manifest.lookup!(name), **options) + if current_webpacker_instance.config.extract_css? || !stylesheet?(name) + asset_url(current_webpacker_instance.manifest.lookup!(name), **options) end end @@ -47,7 +47,7 @@ def asset_pack_url(name, **options) # <%= image_pack_tag 'application.png', size: '16x10', alt: 'Edit Entry' %> # Edit Entry def image_pack_tag(name, **options) - image_tag(asset_path(current_webpacker.manifest.lookup!(name)), **options) + image_tag(asset_path(current_webpacker_instance.manifest.lookup!(name)), **options) end # Creates a script tag that references the named pack file, as compiled by webpack per the entries list @@ -79,7 +79,7 @@ def javascript_pack_tag(*names, **options) # <%= stylesheet_pack_tag 'calendar', 'data-turbolinks-track': 'reload' %> # => # def stylesheet_pack_tag(*names, **options) - if current_webpacker.config.extract_css? + if current_webpacker_instance.config.extract_css? stylesheet_link_tag(*sources_from_pack_manifest(names, type: :stylesheet), **options) end end @@ -90,6 +90,6 @@ def stylesheet?(name) end def sources_from_pack_manifest(names, type:) - names.map { |name| current_webpacker.manifest.lookup!(name, type: type) }.flatten + names.map { |name| current_webpacker_instance.manifest.lookup!(name, type: type) }.flatten end end