File: engines.md

package info (click to toggle)
ruby-webpacker 5.4.3-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,472 kB
  • sloc: ruby: 1,626; javascript: 1,480; makefile: 4
file content (213 lines) | stat: -rw-r--r-- 5,915 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# 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 official [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.

- File `lib/my_engine.rb`

```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.

- File `lib/my_engine/engine.rb`

```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, # "Webpacker::DevServerProxy" if Rails version < 5
          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.

- File `app/helpers/my_engine/application_helper.rb`

```ruby
require "webpacker/helper"

module MyEngine
  module ApplicationHelper
    include ::Webpacker::Helper

    def current_webpacker_instance
      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`)

- File `my_engine_rootlib/tasks/my_engine_tasks.rake`

```ruby
def ensure_log_goes_to_stdout
  old_logger = Webpacker.logger
  Webpacker.logger = ActiveSupport::Logger.new(STDOUT)
  yield
ensure
  Webpacker.logger = old_logger
end


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
        ensure_log_goes_to_stdout do
          if MyEngine.webpacker.commands.compile
            # Successful compilation!
          else
            # Failed compilation
            exit!
          end
        end
      end
    end
  end
end

def yarn_install_available?
  rails_major = Rails::VERSION::MAJOR
  rails_minor = Rails::VERSION::MINOR

  rails_major > 5 || (rails_major == 5 && rails_minor >= 1)
end

def enhance_assets_precompile
  # yarn:install was added in Rails 5.1
  deps = yarn_install_available? ? [] : ["my_engine:webpacker:yarn_install"]
  Rake::Task["assets:precompile"].enhance(deps) do
    Rake::Task["my_engine:webpacker:compile"].invoke
  end
end

# Compile packs after we've compiled all other assets during precompilation
skip_webpacker_precompile = %w(no false n f).include?(ENV["WEBPACKER_PRECOMPILE"])

unless skip_webpacker_precompile
  if Rake::Task.task_defined?("assets:precompile")
    enhance_assets_precompile
  else
    Rake::Task.define_task("assets:precompile" => "my_engine:webpacker:compile")
  end
end
```

## Step 7: serving compiled packs.

There are two approaches on serving compiled assets.

### Put engine's assets to the root app's public/ folder

You can serve engine's assets using the main app's static files server which serves files from `public/` folder.

For that you must configure your engine's webpacker to put compiled assets to the app's `public/` folder:

```yml
# my_engine/config/webpacker.yml
default: &default
  # ...
  # public_root_path could be used to override the path to `public/` folder
  # (relative to the engine root)
  public_root_path: ../public
  # use a different sub-folder name
  public_output_path: my-engine-packs
```

### Use a separate middleware

To serve static assets from the engine's `public/` folder you must 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"
)
```
or if you prefer to keep your engine-related configuration within the engine itself

```ruby
# my-engine-root/lib/my-engine/engine.rb
module MyEngine
  class Engine < ::Rails:Engine
    config.app_middleware.use(
      Rack::Static,
      urls: ["/my-engine-packs"], root: "my_engine/public"
    )
  end
end
```

**NOTE:** in the example above we assume that your `public_output_path` is set to `my-engine-packs` in your engine's `webpacker.yml`.