shakapacker 6.2.1 → 6.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +53 -4
- data/CONTRIBUTING.md +2 -2
- data/Gemfile +2 -1
- data/Gemfile.lock +84 -74
- data/README.md +172 -65
- data/docs/react.md +262 -0
- data/docs/sprockets.md +10 -0
- data/docs/v6_upgrade.md +1 -1
- data/lib/install/config/webpacker.yml +14 -0
- data/lib/tasks/webpacker/clean.rake +1 -3
- data/lib/tasks/webpacker/clobber.rake +1 -3
- data/lib/tasks/webpacker/compile.rake +3 -14
- data/lib/tasks/webpacker.rake +0 -1
- data/lib/webpacker/base_strategy.rb +24 -0
- data/lib/webpacker/compiler.rb +4 -67
- data/lib/webpacker/compiler_strategy.rb +20 -0
- data/lib/webpacker/configuration.rb +13 -0
- data/lib/webpacker/digest_strategy.rb +59 -0
- data/lib/webpacker/helper.rb +26 -1
- data/lib/webpacker/instance.rb +4 -0
- data/lib/webpacker/manifest.rb +2 -2
- data/lib/webpacker/mtime_strategy.rb +40 -0
- data/lib/webpacker/version.rb +1 -1
- data/package/babel/preset.js +0 -1
- data/package/environments/__tests__/base.js +30 -3
- data/package/environments/base.js +8 -1
- data/package/rules/file.js +2 -2
- data/package.json +13 -12
- data/test/compiler_strategy_test.rb +27 -0
- data/test/compiler_test.rb +26 -34
- data/test/configuration_test.rb +24 -4
- data/test/digest_strategy_test.rb +33 -0
- data/test/helper_test.rb +22 -0
- data/test/manifest_test.rb +3 -3
- data/test/mtime_strategy_test.rb +42 -0
- data/test/rake_tasks_test.rb +0 -34
- data/test/test_app/app/packs/entrypoints/generated/something.js +2 -0
- data/test/test_app/config/webpacker.yml +1 -0
- data/test/test_app/config/webpacker_nested_entries.yml +83 -0
- data/test/test_app/config/webpacker_no_precompile.yml +7 -0
- data/yarn.lock +917 -884
- metadata +21 -5
- data/lib/tasks/webpacker/yarn_install.rake +0 -18
- data/lib/tasks/yarn.rake +0 -38
data/docs/react.md
ADDED
@@ -0,0 +1,262 @@
|
|
1
|
+
# React Integration
|
2
|
+
|
3
|
+
These steps describe how to create a Rails/React app, using Shakapacker as the bundler.
|
4
|
+
|
5
|
+
Before starting, ensure that you have Yarn installed, for example:
|
6
|
+
|
7
|
+
```shell
|
8
|
+
npm i -g yarn
|
9
|
+
```
|
10
|
+
|
11
|
+
## Basic Setup
|
12
|
+
|
13
|
+
Create a new Rails app as per the [installation instructions in the README](https://github.com/shakacode/shakapacker#installation).
|
14
|
+
|
15
|
+
Add React, as well as the necessary libraries to enable CSS support in your application:
|
16
|
+
|
17
|
+
```shell
|
18
|
+
yarn add react react-dom @babel/preset-react
|
19
|
+
yarn add css-loader style-loader mini-css-extract-plugin css-minimizer-webpack-plugin
|
20
|
+
```
|
21
|
+
|
22
|
+
Update the Babel configuration in the `package.json` file:
|
23
|
+
|
24
|
+
```diff
|
25
|
+
"babel": {
|
26
|
+
"presets": [
|
27
|
+
"./node_modules/shakapacker/package/babel/preset.js",
|
28
|
+
+ "@babel/preset-react"
|
29
|
+
]
|
30
|
+
},
|
31
|
+
```
|
32
|
+
|
33
|
+
And that's it. You can now create a React app using `app/javascript/application.js` as your entry point.
|
34
|
+
|
35
|
+
## Enabling Hot Module Replacement (HMR)
|
36
|
+
|
37
|
+
With HMR enabled, Shakapacker will automatically update only that part of the page that changed when it detects changes in your project files. This has the nice advantage of preserving your app’s state.
|
38
|
+
|
39
|
+
To enable HMR in a React app, proceed as follows:.
|
40
|
+
|
41
|
+
In `config/webpacker.yml` set `hmr` is set to `true`.
|
42
|
+
|
43
|
+
Install the [react-refresh](https://www.npmjs.com/package/react-refresh) package, as well as [@pmmmwh/react-refresh-webpack-plugin](https://www.npmjs.com/package/@pmmmwh/react-refresh-webpack-plugin):
|
44
|
+
|
45
|
+
```shell
|
46
|
+
yarn add --dev react-refresh @pmmmwh/react-refresh-webpack-plugin
|
47
|
+
```
|
48
|
+
|
49
|
+
Alter `config/webpack/webpack.config.js` like so:
|
50
|
+
|
51
|
+
```js
|
52
|
+
const { webpackConfig, inliningCss } = require('shakapacker');
|
53
|
+
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
|
54
|
+
const isDevelopment = process.env.NODE_ENV !== 'production';
|
55
|
+
|
56
|
+
if (isDevelopment && inliningCss) {
|
57
|
+
webpackConfig.plugins.push(
|
58
|
+
new ReactRefreshWebpackPlugin({
|
59
|
+
overlay: {
|
60
|
+
sockPort: webpackConfig.devServer.port,
|
61
|
+
},
|
62
|
+
})
|
63
|
+
);
|
64
|
+
}
|
65
|
+
|
66
|
+
module.exports = webpackConfig;
|
67
|
+
```
|
68
|
+
|
69
|
+
This applies the plugin to the webpack configuration.
|
70
|
+
|
71
|
+
Delete the Babel configuration from `package.json`:
|
72
|
+
|
73
|
+
```diff
|
74
|
+
- "babel": {
|
75
|
+
- "presets": [
|
76
|
+
- "./node_modules/shakapacker/package/babel/preset.js",
|
77
|
+
- "@babel/preset-react"
|
78
|
+
- ]
|
79
|
+
- },
|
80
|
+
```
|
81
|
+
|
82
|
+
Then create a `babel.config.js` file in the root of project and add the following:
|
83
|
+
|
84
|
+
```js
|
85
|
+
module.exports = function (api) {
|
86
|
+
const defaultConfigFunc = require('shakapacker/package/babel/preset.js')
|
87
|
+
const resultConfig = defaultConfigFunc(api)
|
88
|
+
const isDevelopmentEnv = api.env('development')
|
89
|
+
const isProductionEnv = api.env('production')
|
90
|
+
const isTestEnv = api.env('test')
|
91
|
+
|
92
|
+
const changesOnDefault = {
|
93
|
+
presets: [
|
94
|
+
[
|
95
|
+
'@babel/preset-react',
|
96
|
+
{
|
97
|
+
development: isDevelopmentEnv || isTestEnv,
|
98
|
+
useBuiltIns: true
|
99
|
+
}
|
100
|
+
]
|
101
|
+
].filter(Boolean),
|
102
|
+
plugins: [
|
103
|
+
isProductionEnv && ['babel-plugin-transform-react-remove-prop-types',
|
104
|
+
{
|
105
|
+
removeImport: true
|
106
|
+
}
|
107
|
+
],
|
108
|
+
process.env.WEBPACK_SERVE && 'react-refresh/babel'
|
109
|
+
].filter(Boolean),
|
110
|
+
}
|
111
|
+
|
112
|
+
resultConfig.presets = [...resultConfig.presets, ...changesOnDefault.presets]
|
113
|
+
resultConfig.plugins = [...resultConfig.plugins, ...changesOnDefault.plugins ]
|
114
|
+
|
115
|
+
return resultConfig
|
116
|
+
}
|
117
|
+
```
|
118
|
+
|
119
|
+
This is taken from the [sample React Babel config](https://github.com/jameshibbard/shakapacker/blob/master/docs/customizing_babel_config.md#react-configuration).
|
120
|
+
|
121
|
+
HMR for your React app is now enabled. 🚀
|
122
|
+
|
123
|
+
## A Basic Demo App
|
124
|
+
|
125
|
+
To test that all of the above is working, you can follow these instructions to create a basic React app using Shakapacker.
|
126
|
+
|
127
|
+
1. Create a new Rails app:
|
128
|
+
```shell
|
129
|
+
rails new myapp --skip-javascript
|
130
|
+
cd myapp
|
131
|
+
bundle add shakapacker --strict
|
132
|
+
./bin/bundle install
|
133
|
+
./bin/rails webpacker:install
|
134
|
+
yarn add react react-dom @babel/preset-react
|
135
|
+
yarn add css-loader style-loader mini-css-extract-plugin css-minimizer-webpack-plugin
|
136
|
+
```
|
137
|
+
|
138
|
+
2. Generate a controller
|
139
|
+
```shell
|
140
|
+
rails g controller site index
|
141
|
+
echo '<div id="root"></div>' > app/views/site/index.html.erb
|
142
|
+
```
|
143
|
+
|
144
|
+
3. Create a CSS file and a React component:
|
145
|
+
```shell
|
146
|
+
touch app/javascript/App.css app/javascript/App.js
|
147
|
+
```
|
148
|
+
|
149
|
+
4. Edit `app/javascript/application.js` like so:
|
150
|
+
```jsx
|
151
|
+
import React from 'react';
|
152
|
+
import { createRoot } from 'react-dom/client';
|
153
|
+
import HelloMessage from './App';
|
154
|
+
|
155
|
+
const container = document.getElementById('root');
|
156
|
+
const root = createRoot(container);
|
157
|
+
|
158
|
+
document.addEventListener('DOMContentLoaded', () => {
|
159
|
+
root.render(<HelloMessage name="World" />);
|
160
|
+
});
|
161
|
+
```
|
162
|
+
|
163
|
+
5. Add the following to `app/javascript/App.js`:
|
164
|
+
```jsx
|
165
|
+
import React from 'react';
|
166
|
+
import 'App.css';
|
167
|
+
const HelloMessage = ({ name }) => <h1>Hello, {name}!</h1>;
|
168
|
+
export default HelloMessage;
|
169
|
+
```
|
170
|
+
|
171
|
+
6. Add the following to `app/javascript/App.css`:
|
172
|
+
```css
|
173
|
+
h1 { color: blue; }
|
174
|
+
```
|
175
|
+
|
176
|
+
7. Enable HMR in config/webpacker.yml:
|
177
|
+
```shell
|
178
|
+
hmr: true
|
179
|
+
```
|
180
|
+
|
181
|
+
8. Install the [react-refresh](https://www.npmjs.com/package/react-refresh) package, as well as [@pmmmwh/react-refresh-webpack-plugin](https://www.npmjs.com/package/@pmmmwh/react-refresh-webpack-plugin):
|
182
|
+
|
183
|
+
```shell
|
184
|
+
yarn add --dev react-refresh @pmmmwh/react-refresh-webpack-plugin
|
185
|
+
```
|
186
|
+
|
187
|
+
9. Alter `config/webpack/webpack.config.js` like so:
|
188
|
+
|
189
|
+
```js
|
190
|
+
const { webpackConfig, inliningCss } = require('shakapacker');
|
191
|
+
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
|
192
|
+
const isDevelopment = process.env.NODE_ENV !== 'production';
|
193
|
+
|
194
|
+
if (isDevelopment && inliningCss) {
|
195
|
+
webpackConfig.plugins.push(
|
196
|
+
new ReactRefreshWebpackPlugin({
|
197
|
+
overlay: {
|
198
|
+
sockPort: webpackConfig.devServer.port,
|
199
|
+
},
|
200
|
+
})
|
201
|
+
);
|
202
|
+
}
|
203
|
+
|
204
|
+
module.exports = webpackConfig;
|
205
|
+
```
|
206
|
+
|
207
|
+
10. Remove the Babel configuration from `package.json`
|
208
|
+
```diff
|
209
|
+
- "babel": {
|
210
|
+
- "presets": [
|
211
|
+
- "./node_modules/shakapacker/package/babel/preset.js"
|
212
|
+
- ]
|
213
|
+
- },
|
214
|
+
```
|
215
|
+
|
216
|
+
11. Create a `babel.config.js` file in the project root and add the following [sample code](https://github.com/shakacode/shakapacker/blob/master/docs/customizing_babel_config.md#react-configuration):
|
217
|
+
```js
|
218
|
+
module.exports = function (api) {
|
219
|
+
const defaultConfigFunc = require('shakapacker/package/babel/preset.js')
|
220
|
+
const resultConfig = defaultConfigFunc(api)
|
221
|
+
const isDevelopmentEnv = api.env('development')
|
222
|
+
const isProductionEnv = api.env('production')
|
223
|
+
const isTestEnv = api.env('test')
|
224
|
+
|
225
|
+
const changesOnDefault = {
|
226
|
+
presets: [
|
227
|
+
[
|
228
|
+
'@babel/preset-react',
|
229
|
+
{
|
230
|
+
development: isDevelopmentEnv || isTestEnv,
|
231
|
+
useBuiltIns: true
|
232
|
+
}
|
233
|
+
]
|
234
|
+
].filter(Boolean),
|
235
|
+
plugins: [
|
236
|
+
isProductionEnv && ['babel-plugin-transform-react-remove-prop-types',
|
237
|
+
{
|
238
|
+
removeImport: true
|
239
|
+
}
|
240
|
+
],
|
241
|
+
process.env.WEBPACK_SERVE && 'react-refresh/babel'
|
242
|
+
].filter(Boolean),
|
243
|
+
}
|
244
|
+
|
245
|
+
resultConfig.presets = [...resultConfig.presets, ...changesOnDefault.presets]
|
246
|
+
resultConfig.plugins = [...resultConfig.plugins, ...changesOnDefault.plugins ]
|
247
|
+
|
248
|
+
return resultConfig
|
249
|
+
}
|
250
|
+
```
|
251
|
+
|
252
|
+
9. Start the Rails server and the webpack-dev-server in separate console windows:
|
253
|
+
```shell
|
254
|
+
rails s
|
255
|
+
./bin/webpacker-dev-server
|
256
|
+
```
|
257
|
+
|
258
|
+
10. Hit: <http://localhost:3000/site/index>
|
259
|
+
|
260
|
+
11. Edit either the React component at `app/javascript/App.js` or the CSS file at `app/javascript/App.css` and observe the HMR goodness.
|
261
|
+
|
262
|
+
Note that HMR will not work if you edit `app/javascript/application.js` and you experience a full refresh with a warning in the console. For more info on this, see here: https://github.com/pmmmwh/react-refresh-webpack-plugin/issues/177
|
data/docs/sprockets.md
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# Sprockets
|
2
|
+
|
3
|
+
### Note for Sprockets usage
|
4
|
+
|
5
|
+
If you are still using Sprockets for some of your assets, you might want to include files from `node_modules` directory in your asset pipeline. This is useful, for example, if you want to reference a stylesheet from a node package in your `.scss` stylesheet.
|
6
|
+
|
7
|
+
In order to enable this, make sure you add `node_modules` to the asset load path by adding the following in an initializer (for example `config/initializers/assets.rb`)
|
8
|
+
```ruby
|
9
|
+
Rails.application.config.assets.paths << Rails.root.join('node_modules')
|
10
|
+
```
|
data/docs/v6_upgrade.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
There are several substantial changes in Shakapacker v6 that you need to manually account for when coming from Webpacker 5. This guide will help you through it.
|
4
4
|
|
5
|
-
[ShakaCode](https://www.shakacode.com) offers
|
5
|
+
[ShakaCode](https://www.shakacode.com) offers support for upgrading from webpacker or using Shakapacker. If interested, contact [justin@shakacode.com](mailto:justin@shakacode.com).
|
6
6
|
|
7
7
|
## Webpacker/Shakapacker has become a slimmer wrapper around Webpack
|
8
8
|
|
@@ -2,11 +2,21 @@
|
|
2
2
|
|
3
3
|
default: &default
|
4
4
|
source_path: app/javascript
|
5
|
+
|
6
|
+
# You can have a subdirectory of the source_path, like 'packs' (recommended).
|
7
|
+
# Alternatively, you can use '/' to use the whole source_path directory.
|
5
8
|
source_entry_path: /
|
9
|
+
|
10
|
+
# If nested_entries is true, then we'll pick up subdirectories within the source_entry_path.
|
11
|
+
# You cannot set this option to true if you set source_entry_path to '/'
|
12
|
+
nested_entries: false
|
13
|
+
|
6
14
|
public_root_path: public
|
7
15
|
public_output_path: packs
|
8
16
|
cache_path: tmp/webpacker
|
9
17
|
webpack_compile_output: true
|
18
|
+
# See https://github.com/shakacode/shakapacker#deployment
|
19
|
+
webpacker_precompile: true
|
10
20
|
|
11
21
|
# Location for manifest.json, defaults to {public_output_path}/manifest.json if unset
|
12
22
|
# manifest_path: public/packs/manifest.json
|
@@ -24,9 +34,13 @@ default: &default
|
|
24
34
|
# Set to true to enable check for matching versions of shakapacker gem and NPM package - will raise an error if there is a mismatch or wildcard versioning is used
|
25
35
|
ensure_consistent_versioning: false
|
26
36
|
|
37
|
+
# Select whether the compiler will use SHA digest ('digest' option) or most most recent modified timestamp ('mtime') to determine freshness
|
38
|
+
compiler_strategy: digest
|
39
|
+
|
27
40
|
development:
|
28
41
|
<<: *default
|
29
42
|
compile: true
|
43
|
+
compiler_strategy: mtime
|
30
44
|
|
31
45
|
# Reference: https://webpack.js.org/configuration/dev-server/
|
32
46
|
dev_server:
|
@@ -11,9 +11,7 @@ namespace :webpacker do
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
unless skip_webpacker_clean
|
14
|
+
if Webpacker.config.webpacker_precompile?
|
17
15
|
# Run clean if the assets:clean is run
|
18
16
|
if Rake::Task.task_defined?("assets:clean")
|
19
17
|
Rake::Task["assets:clean"].enhance do
|
@@ -8,9 +8,7 @@ namespace :webpacker do
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
unless skip_webpacker_clobber
|
11
|
+
if Webpacker.config.webpacker_precompile?
|
14
12
|
# Run clobber if the assets:clobber is run
|
15
13
|
if Rake::Task.task_defined?("assets:clobber")
|
16
14
|
Rake::Task["assets:clobber"].enhance do
|
@@ -1,16 +1,8 @@
|
|
1
1
|
$stdout.sync = true
|
2
2
|
|
3
|
-
def yarn_install_available?
|
4
|
-
rails_major = Rails::VERSION::MAJOR
|
5
|
-
rails_minor = Rails::VERSION::MINOR
|
6
|
-
|
7
|
-
rails_major > 5 || (rails_major == 5 && rails_minor >= 1)
|
8
|
-
end
|
9
|
-
|
10
3
|
def enhance_assets_precompile
|
11
4
|
# yarn:install was added in Rails 5.1
|
12
|
-
|
13
|
-
Rake::Task["assets:precompile"].enhance(deps) do |task|
|
5
|
+
Rake::Task["assets:precompile"].enhance do |task|
|
14
6
|
prefix = task.name.split(/#|assets:precompile/).first
|
15
7
|
|
16
8
|
Rake::Task["#{prefix}webpacker:compile"].invoke
|
@@ -33,13 +25,10 @@ namespace :webpacker do
|
|
33
25
|
end
|
34
26
|
end
|
35
27
|
|
36
|
-
|
37
|
-
skip_webpacker_precompile = %w(no false n f).include?(ENV["WEBPACKER_PRECOMPILE"])
|
38
|
-
|
39
|
-
unless skip_webpacker_precompile
|
28
|
+
if Webpacker.config.webpacker_precompile?
|
40
29
|
if Rake::Task.task_defined?("assets:precompile")
|
41
30
|
enhance_assets_precompile
|
42
31
|
else
|
43
|
-
Rake::Task.define_task("assets:precompile" => ["webpacker:
|
32
|
+
Rake::Task.define_task("assets:precompile" => ["webpacker:compile"])
|
44
33
|
end
|
45
34
|
end
|
data/lib/tasks/webpacker.rake
CHANGED
@@ -9,7 +9,6 @@ tasks = {
|
|
9
9
|
"webpacker:check_binstubs" => "Verifies that bin/webpacker is present",
|
10
10
|
"webpacker:binstubs" => "Installs Webpacker binstubs in this application",
|
11
11
|
"webpacker:verify_install" => "Verifies if Webpacker is installed",
|
12
|
-
"webpacker:yarn_install" => "Support for older Rails versions. Install all JavaScript dependencies as specified via Yarn"
|
13
12
|
}.freeze
|
14
13
|
|
15
14
|
desc "Lists all available tasks in Webpacker"
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Webpacker
|
2
|
+
class BaseStrategy
|
3
|
+
def initialize
|
4
|
+
@config = Webpacker.config
|
5
|
+
end
|
6
|
+
|
7
|
+
def after_compile_hook
|
8
|
+
nil
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
attr_reader :config
|
14
|
+
|
15
|
+
def default_watched_paths
|
16
|
+
[
|
17
|
+
*config.additional_paths.map { |path| "#{path}{,/**/*}" },
|
18
|
+
"#{config.source_path}{,/**/*}",
|
19
|
+
"yarn.lock", "package.json",
|
20
|
+
"config/webpack{,/**/*}"
|
21
|
+
].freeze
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/webpacker/compiler.rb
CHANGED
@@ -1,18 +1,13 @@
|
|
1
1
|
require "open3"
|
2
|
-
require "
|
2
|
+
require "webpacker/compiler_strategy"
|
3
3
|
|
4
4
|
class Webpacker::Compiler
|
5
|
-
# Additional paths that test compiler needs to watch
|
6
|
-
# Webpacker::Compiler.watched_paths << 'bower_components'
|
7
|
-
#
|
8
|
-
# Deprecated. Use additional_paths in the YAML configuration instead.
|
9
|
-
cattr_accessor(:watched_paths) { [] }
|
10
|
-
|
11
5
|
# Additional environment variables that the compiler is being run with
|
12
6
|
# Webpacker::Compiler.env['FRONTEND_API_KEY'] = 'your_secret_key'
|
13
7
|
cattr_accessor(:env) { {} }
|
14
8
|
|
15
|
-
delegate :config, :logger, to: :webpacker
|
9
|
+
delegate :config, :logger, :strategy, to: :webpacker
|
10
|
+
delegate :fresh?, :stale?, :after_compile_hook, to: :strategy
|
16
11
|
|
17
12
|
def initialize(webpacker)
|
18
13
|
@webpacker = webpacker
|
@@ -21,11 +16,7 @@ class Webpacker::Compiler
|
|
21
16
|
def compile
|
22
17
|
if stale?
|
23
18
|
run_webpack.tap do |success|
|
24
|
-
|
25
|
-
# However, the output file is still written on error, meaning that the digest should still be updated.
|
26
|
-
# If it's not, you can end up in a situation where a recompile doesn't take place when it should.
|
27
|
-
# See https://github.com/rails/webpacker/issues/2113
|
28
|
-
record_compilation_digest
|
19
|
+
after_compile_hook
|
29
20
|
end
|
30
21
|
else
|
31
22
|
logger.debug "Everything's up-to-date. Nothing to do"
|
@@ -33,50 +24,9 @@ class Webpacker::Compiler
|
|
33
24
|
end
|
34
25
|
end
|
35
26
|
|
36
|
-
# Returns true if all the compiled packs are up to date with the underlying asset files.
|
37
|
-
def fresh?
|
38
|
-
last_compilation_digest&.== watched_files_digest
|
39
|
-
end
|
40
|
-
|
41
|
-
# Returns true if the compiled packs are out of date with the underlying asset files.
|
42
|
-
def stale?
|
43
|
-
!fresh?
|
44
|
-
end
|
45
|
-
|
46
27
|
private
|
47
28
|
attr_reader :webpacker
|
48
29
|
|
49
|
-
def last_compilation_digest
|
50
|
-
compilation_digest_path.read if compilation_digest_path.exist? && config.manifest_path.exist?
|
51
|
-
rescue Errno::ENOENT, Errno::ENOTDIR
|
52
|
-
end
|
53
|
-
|
54
|
-
def watched_files_digest
|
55
|
-
if Rails.env.development?
|
56
|
-
warn <<~MSG.strip
|
57
|
-
Webpacker::Compiler - Slow setup for development
|
58
|
-
|
59
|
-
Prepare JS assets with either:
|
60
|
-
1. Running `bin/webpacker-dev-server`
|
61
|
-
2. Set `compile` to false in webpacker.yml and run `bin/webpacker -w`
|
62
|
-
MSG
|
63
|
-
end
|
64
|
-
|
65
|
-
warn "Webpacker::Compiler.watched_paths has been deprecated. Set additional_paths in webpacker.yml instead." unless watched_paths.empty?
|
66
|
-
root_path = Pathname.new(File.expand_path(config.root_path))
|
67
|
-
expanded_paths = [*default_watched_paths, *watched_paths].map do |path|
|
68
|
-
root_path.join(path)
|
69
|
-
end
|
70
|
-
files = Dir[*expanded_paths].reject { |f| File.directory?(f) }
|
71
|
-
file_ids = files.sort.map { |f| "#{File.basename(f)}/#{Digest::SHA1.file(f).hexdigest}" }
|
72
|
-
Digest::SHA1.hexdigest(file_ids.join("/"))
|
73
|
-
end
|
74
|
-
|
75
|
-
def record_compilation_digest
|
76
|
-
config.cache_path.mkpath
|
77
|
-
compilation_digest_path.write(watched_files_digest)
|
78
|
-
end
|
79
|
-
|
80
30
|
def optionalRubyRunner
|
81
31
|
bin_webpack_path = config.root_path.join("bin/webpacker")
|
82
32
|
first_line = File.readlines(bin_webpack_path).first.chomp
|
@@ -107,19 +57,6 @@ class Webpacker::Compiler
|
|
107
57
|
status.success?
|
108
58
|
end
|
109
59
|
|
110
|
-
def default_watched_paths
|
111
|
-
[
|
112
|
-
*config.additional_paths.map { |path| "#{path}/**/*" },
|
113
|
-
"#{config.source_path}/**/*",
|
114
|
-
"yarn.lock", "package.json",
|
115
|
-
"config/webpack/**/*"
|
116
|
-
].freeze
|
117
|
-
end
|
118
|
-
|
119
|
-
def compilation_digest_path
|
120
|
-
config.cache_path.join("last-compilation-digest-#{webpacker.env}")
|
121
|
-
end
|
122
|
-
|
123
60
|
def webpack_env
|
124
61
|
return env unless defined?(ActionController::Base)
|
125
62
|
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "webpacker/mtime_strategy"
|
2
|
+
require "webpacker/digest_strategy"
|
3
|
+
|
4
|
+
module Webpacker
|
5
|
+
class CompilerStrategy
|
6
|
+
def self.from_config
|
7
|
+
strategy_from_config = Webpacker.config.compiler_strategy
|
8
|
+
|
9
|
+
case strategy_from_config
|
10
|
+
when "mtime"
|
11
|
+
Webpacker::MtimeStrategy.new
|
12
|
+
when "digest"
|
13
|
+
Webpacker::DigestStrategy.new
|
14
|
+
else
|
15
|
+
raise "Unknown strategy '#{strategy_from_config}'. " \
|
16
|
+
"Available options are 'mtime' and 'digest'."
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -27,6 +27,15 @@ class Webpacker::Configuration
|
|
27
27
|
fetch(:ensure_consistent_versioning)
|
28
28
|
end
|
29
29
|
|
30
|
+
def webpacker_precompile?
|
31
|
+
# ENV of false takes precedence
|
32
|
+
return false if %w(no false n f).include?(ENV["WEBPACKER_PRECOMPILE"])
|
33
|
+
return true if %w(yes true t).include?(ENV["WEBPACKER_PRECOMPILE"])
|
34
|
+
|
35
|
+
return false unless config_path.exist?
|
36
|
+
fetch(:webpacker_precompile)
|
37
|
+
end
|
38
|
+
|
30
39
|
def source_path
|
31
40
|
root_path.join(fetch(:source_path))
|
32
41
|
end
|
@@ -79,6 +88,10 @@ class Webpacker::Configuration
|
|
79
88
|
fetch(:webpack_compile_output)
|
80
89
|
end
|
81
90
|
|
91
|
+
def compiler_strategy
|
92
|
+
fetch(:compiler_strategy)
|
93
|
+
end
|
94
|
+
|
82
95
|
def fetch(key)
|
83
96
|
data.fetch(key, defaults[key])
|
84
97
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "digest/sha1"
|
2
|
+
require "webpacker/base_strategy"
|
3
|
+
|
4
|
+
module Webpacker
|
5
|
+
class DigestStrategy < BaseStrategy
|
6
|
+
# Returns true if all the compiled packs are up to date with the underlying asset files.
|
7
|
+
def fresh?
|
8
|
+
last_compilation_digest&.== watched_files_digest
|
9
|
+
end
|
10
|
+
|
11
|
+
# Returns true if the compiled packs are out of date with the underlying asset files.
|
12
|
+
def stale?
|
13
|
+
!fresh?
|
14
|
+
end
|
15
|
+
|
16
|
+
def after_compile_hook
|
17
|
+
# We used to only record the digest on success
|
18
|
+
# However, the output file is still written on error, meaning that the digest should still be updated.
|
19
|
+
# If it's not, you can end up in a situation where a recompile doesn't take place when it should.
|
20
|
+
# See https://github.com/rails/webpacker/issues/2113
|
21
|
+
record_compilation_digest
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def last_compilation_digest
|
27
|
+
compilation_digest_path.read if compilation_digest_path.exist? && config.manifest_path.exist?
|
28
|
+
rescue Errno::ENOENT, Errno::ENOTDIR
|
29
|
+
end
|
30
|
+
|
31
|
+
def watched_files_digest
|
32
|
+
if Rails.env.development?
|
33
|
+
warn <<~MSG.strip
|
34
|
+
Webpacker::Compiler - Slow setup for development
|
35
|
+
Prepare JS assets with either:
|
36
|
+
1. Running `bin/webpacker-dev-server`
|
37
|
+
2. Set `compile` to false in webpacker.yml and run `bin/webpacker -w`
|
38
|
+
MSG
|
39
|
+
end
|
40
|
+
|
41
|
+
root_path = Pathname.new(File.expand_path(config.root_path))
|
42
|
+
expanded_paths = [*default_watched_paths].map do |path|
|
43
|
+
root_path.join(path)
|
44
|
+
end
|
45
|
+
files = Dir[*expanded_paths].reject { |f| File.directory?(f) }
|
46
|
+
file_ids = files.sort.map { |f| "#{File.basename(f)}/#{Digest::SHA1.file(f).hexdigest}" }
|
47
|
+
Digest::SHA1.hexdigest(file_ids.join("/"))
|
48
|
+
end
|
49
|
+
|
50
|
+
def record_compilation_digest
|
51
|
+
config.cache_path.mkpath
|
52
|
+
compilation_digest_path.write(watched_files_digest)
|
53
|
+
end
|
54
|
+
|
55
|
+
def compilation_digest_path
|
56
|
+
config.cache_path.join("last-compilation-digest-#{Webpacker.env}")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/webpacker/helper.rb
CHANGED
@@ -101,9 +101,17 @@ module Webpacker::Helper
|
|
101
101
|
"Please refer to https://github.com/shakacode/shakapacker/blob/master/README.md#usage for the usage guide"
|
102
102
|
end
|
103
103
|
|
104
|
+
append_javascript_pack_tag(*names, defer: defer)
|
105
|
+
non_deferred = sources_from_manifest_entrypoints(javascript_pack_tag_queue[:non_deferred], type: :javascript)
|
106
|
+
deferred = sources_from_manifest_entrypoints(javascript_pack_tag_queue[:deferred], type: :javascript) - non_deferred
|
107
|
+
|
104
108
|
@javascript_pack_tag_loaded = true
|
105
109
|
|
106
|
-
|
110
|
+
capture do
|
111
|
+
concat javascript_include_tag(*deferred, **options.tap { |o| o[:defer] = true })
|
112
|
+
concat "\n" if non_deferred.any? && deferred.any?
|
113
|
+
concat javascript_include_tag(*non_deferred, **options.tap { |o| o[:defer] = false })
|
114
|
+
end
|
107
115
|
end
|
108
116
|
|
109
117
|
# Creates a link tag, for preloading, that references a given Webpacker asset.
|
@@ -153,8 +161,25 @@ module Webpacker::Helper
|
|
153
161
|
stylesheet_link_tag(*sources_from_manifest_entrypoints(names, type: :stylesheet), **options)
|
154
162
|
end
|
155
163
|
|
164
|
+
def append_javascript_pack_tag(*names, defer: true)
|
165
|
+
if @javascript_pack_tag_loaded
|
166
|
+
raise "You can only call append_javascript_pack_tag before javascript_pack_tag helper. " \
|
167
|
+
"Please refer to https://github.com/shakacode/shakapacker/blob/master/README.md#usage for the usage guide"
|
168
|
+
end
|
169
|
+
|
170
|
+
hash_key = defer ? :deferred : :non_deferred
|
171
|
+
javascript_pack_tag_queue[hash_key] |= names
|
172
|
+
end
|
173
|
+
|
156
174
|
private
|
157
175
|
|
176
|
+
def javascript_pack_tag_queue
|
177
|
+
@javascript_pack_tag_queue ||= {
|
178
|
+
deferred: [],
|
179
|
+
non_deferred: []
|
180
|
+
}
|
181
|
+
end
|
182
|
+
|
158
183
|
def sources_from_manifest_entrypoints(names, type:)
|
159
184
|
names.map { |name| current_webpacker_instance.manifest.lookup_pack_with_chunks!(name.to_s, type: type) }.flatten.uniq
|
160
185
|
end
|