shakapacker 6.3.0.pre.rc.1 → 6.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 862dd1243830eb271661d0a7be8ad35b4837ab8aaabfa2a39cc9a65e5c6d01f3
4
- data.tar.gz: a9b58deb1b8038aecbac2df1ca3516d59c03dfd2cc5a6a71e06edf480ba3443c
3
+ metadata.gz: ceac7ca1c43918c99f5c5f50eec4f34fc591ef9de90bac0f2a35c7d0e7c4cc69
4
+ data.tar.gz: 4f571c49df398b3ff2dfd3faf476908c4492b361a9ebaf64d4c91e4c6074df5e
5
5
  SHA512:
6
- metadata.gz: c03da71f446adc4f470acdca8de67487ff7934650abca9278a4e26c854329432d6a94b02368f5825cca0214f1fb878399711114b67c5f8e1dbb9d27620cf22b9
7
- data.tar.gz: 900fdb88440208f4860c7255b5ca71ba316d6ba69223f720a67b4564663f633eb11e31403b41247c94e5755488804dd9f6745b9ddbfb6c82c5f928ec360abdd5
6
+ metadata.gz: 4bcccce7cc22a42f20ac8738904a97d8288abb862a5d0c98e487393450d60a6a703559aa1b3add2f70f5211643fb02769ca16ef118cee5870f7de580a07cfcbb
7
+ data.tar.gz: 6f27881ee63c1cc94c416c9d59e8354a3425707e055b035bb89d1da86905c2ae876e3d329da59f9015f29f3219f716f042dccabaf563a01cf327a0d8be89b41e
data/CHANGELOG.md CHANGED
@@ -8,7 +8,17 @@ Changes since last non-beta release.
8
8
 
9
9
  *Please add entries here for your pull requests that are not yet released.*
10
10
 
11
- ## [v6.3.0-rc.1] - April 24, 2024
11
+ ## [v6.3.0] - May 19, 2022
12
+
13
+ ### Improved
14
+ - Add ability to configure usage of either last modified timestamp and digest strategies when checking asset freshness. [PR 112](https://github.com/shakacode/shakapacker/pull/112) by [tomdracz](https://github.com/tomdracz).
15
+
16
+ ### Fixed
17
+ - On Windows CSS urls no longer contain backslashes resulting in 404 errors. [PR 115](https://github.com/shakacode/shakapacker/pull/115) by [daniel-rikowski](https://github.com/daniel-rikowski).
18
+
19
+ ## [v6.3.0-rc.1] - April 24, 2022
20
+
21
+ Note: [Rubygem is 6.3.0.pre.rc.1](https://rubygems.org/gems/shakapacker/versions/6.3.0.pre.rc.1) and [NPM is 6.3.0-rc.1](https://www.npmjs.com/package/shakapacker/v/6.3.0-rc.1).
12
22
 
13
23
  ### Changed
14
24
  - Remove Loose mode from the default @babel-preset/env configuration. [PR 107](https://github.com/shakacode/shakapacker/pull/107) by [Jeremy Liberman](https://github.com/MrLeebo).
@@ -115,8 +125,9 @@ Changes since last non-beta release.
115
125
  ## v5.4.3 and prior changes from rails/webpacker
116
126
  See [CHANGELOG.md in rails/webpacker (up to v5.4.3)](https://github.com/rails/webpacker/blob/master/CHANGELOG.md)
117
127
 
118
- [Unreleased]: https://github.com/shakacode/shakapacker/compare/v6.3.0-rc.1...master
119
- [v6.3.0-rc.0]: https://github.com/shakacode/shakapacker/compare/v6.2.1...v6.3.0-rc.1
128
+ [Unreleased]: https://github.com/shakacode/shakapacker/compare/v6.3.0...master
129
+ [v6.3.0]: https://github.com/shakacode/shakapacker/compare/v6.3.0-rc.1...v6.3.0
130
+ [v6.3.0-rc.1]: https://github.com/shakacode/shakapacker/compare/v6.2.1...v6.3.0-rc.1
120
131
  [v6.2.1]: https://github.com/shakacode/shakapacker/compare/v6.2.0...v6.2.1
121
132
  [v6.2.0]: https://github.com/shakacode/shakapacker/compare/v6.1.1...v6.2.0
122
133
  [v6.1.1]: https://github.com/shakacode/shakapacker/compare/v6.1.0...v6.1.1
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shakapacker (6.2.1)
4
+ shakapacker (6.3.0.pre.rc.1)
5
5
  activesupport (>= 5.2)
6
6
  rack-proxy (>= 0.6.1)
7
7
  railties (>= 5.2)
data/README.md CHANGED
@@ -62,6 +62,7 @@ Discussion forum and Slack to discuss debugging and troubleshooting tips. Please
62
62
  - [Other frameworks](#other-frameworks)
63
63
  - [Custom Rails environments](#custom-rails-environments)
64
64
  - [Upgrading](#upgrading)
65
+ - [Compiler strategies](#compiler-strategies)
65
66
  - [Paths](#paths)
66
67
  - [Additional paths](#additional-paths)
67
68
  - [Deployment](#deployment)
@@ -431,7 +432,9 @@ Webpacker out of the box supports JS and static assets (fonts, images etc.) comp
431
432
 
432
433
  #### React
433
434
 
434
- See customization example the [Customizing Babel Config](./docs/customizing_babel_config.md) for React configuration.
435
+ See here for detailed instructions on how to [configure Shakapacker to bundle a React app](./docs/react.md) (with optional HMR).
436
+
437
+ See also [Customizing Babel Config](./docs/customizing_babel_config.md) for an example React configuration.
435
438
 
436
439
  #### Typescript
437
440
  ...if you are using typescript, update your `tsconfig.json`
@@ -671,6 +674,17 @@ yarn add shakapacker@next
671
674
 
672
675
  Also, consult the [CHANGELOG](./CHANGELOG.md) for additional upgrade links.
673
676
 
677
+ ### Compiler strategies
678
+
679
+ Shakapacker ships with two different strategies that are used to determine whether assets need recompilation.
680
+
681
+ - `digest` - This strategy calculates SHA1 digest of files in your watched paths (see below). The calculated digest is then stored in a temp file. To check whether the assets need to be recompiled, Shakapacker calculates the SHA1 of the watched files and compares it with the one stored. If the digests are equal, no recompilation occurs. If the digests are different or the temp file is missing, files are recompiled.
682
+ - `mtime` - This strategy looks at last modified at timestamps of both files AND directories in your watched paths. The timestamp of the most recent file or directory is then compared with the timestamp of `manifest.json` file generated. If the manifest file timestamp is newer than one of the most recently modified file or directory in the watched paths, no recompilation occurs. If the manifest file is order, files are recompiled.
683
+
684
+ `mtime` strategy is generally faster than the `digest` one, but it requires stable timestamps, this makes it perfect for development environment. In production or CI environments, the `digest` strategy is more suitable, unless you are using incremental builds or caching and can guarantee that the timestamps will not change after e.g. cache restore.
685
+
686
+ You can control what strategy is used by `compiler_strategy` option in `webpacker.yml` config file. By default `mtime` strategy is used in development environment, `digest` is used elsewhere.
687
+
674
688
  ### Paths
675
689
 
676
690
  By default, Webpacker ships with simple conventions for where the JavaScript app files and compiled webpack bundles will go in your Rails app. All these options are configurable from `config/webpacker.yml` file.
@@ -718,7 +732,7 @@ import 'images/rails.png'
718
732
 
719
733
  **Note:** Please be careful when adding paths here otherwise it will make the compilation slow, consider adding specific paths instead of whole parent directory if you just need to reference one or two modules
720
734
 
721
- **Also note:** While importing assets living outside your `source_path` defined in webpacker.yml (like, for instance, assets under `app/assets`) from within your packs using _relative_ paths like `import '../../assets/javascripts/file.js'` will work in development, Webpacker won't recompile the bundle in production unless a file that lives in one of it's watched paths has changed (check out `Webpacker::Compiler#latest_modified_timestamp`). That's why you'd need to add `app/assets` to the additional_paths as stated above and use `import 'javascripts/file.js'` instead.
735
+ **Also note:** While importing assets living outside your `source_path` defined in webpacker.yml (like, for instance, assets under `app/assets`) from within your packs using _relative_ paths like `import '../../assets/javascripts/file.js'` will work in development, Webpacker won't recompile the bundle in production unless a file that lives in one of it's watched paths has changed (check out `Webpacker::MtimeStrategy#latest_modified_timestamp` or `Webpacker::DigestStrategy#watched_files_digest` depending on strategy configured by `compiler_strategy` option in `webpacker.yml`). That's why you'd need to add `app/assets` to the additional_paths as stated above and use `import 'javascripts/file.js'` instead.
722
736
 
723
737
 
724
738
  ## Deployment
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
@@ -26,9 +26,13 @@ default: &default
26
26
  # 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
27
27
  ensure_consistent_versioning: false
28
28
 
29
+ # Select whether the compiler will use SHA digest ('digest' option) or most most recent modified timestamp ('mtime') to determine freshness
30
+ compiler_strategy: digest
31
+
29
32
  development:
30
33
  <<: *default
31
34
  compile: true
35
+ compiler_strategy: mtime
32
36
 
33
37
  # Reference: https://webpack.js.org/configuration/dev-server/
34
38
  dev_server:
@@ -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
@@ -1,17 +1,13 @@
1
1
  require "open3"
2
+ require "webpacker/compiler_strategy"
2
3
 
3
4
  class Webpacker::Compiler
4
- # Additional paths that test compiler needs to watch
5
- # Webpacker::Compiler.watched_paths << 'bower_components'
6
- #
7
- # Deprecated. Use additional_paths in the YAML configuration instead.
8
- cattr_accessor(:watched_paths) { [] }
9
-
10
5
  # Additional environment variables that the compiler is being run with
11
6
  # Webpacker::Compiler.env['FRONTEND_API_KEY'] = 'your_secret_key'
12
7
  cattr_accessor(:env) { {} }
13
8
 
14
- delegate :config, :logger, to: :webpacker
9
+ delegate :config, :logger, :strategy, to: :webpacker
10
+ delegate :fresh?, :stale?, :after_compile_hook, to: :strategy
15
11
 
16
12
  def initialize(webpacker)
17
13
  @webpacker = webpacker
@@ -19,50 +15,18 @@ class Webpacker::Compiler
19
15
 
20
16
  def compile
21
17
  if stale?
22
- run_webpack
18
+ run_webpack.tap do |success|
19
+ after_compile_hook
20
+ end
23
21
  else
24
22
  logger.debug "Everything's up-to-date. Nothing to do"
25
23
  true
26
24
  end
27
25
  end
28
26
 
29
- # Returns true if manifest file mtime is newer than the timestamp of the last modified watched file
30
- def fresh?
31
- manifest_mtime > latest_modified_timestamp
32
- end
33
-
34
- # Returns true if manifest file mtime is older than the timestamp of the last modified watched file
35
- def stale?
36
- !fresh?
37
- end
38
-
39
27
  private
40
28
  attr_reader :webpacker
41
29
 
42
- def manifest_mtime
43
- config.manifest_path.exist? ? File.mtime(config.manifest_path).to_i : 0
44
- end
45
-
46
- def latest_modified_timestamp
47
- if Rails.env.development?
48
- warn <<~MSG.strip
49
- Webpacker::Compiler - Slow setup for development
50
-
51
- Prepare JS assets with either:
52
- 1. Running `bin/webpacker-dev-server`
53
- 2. Set `compile` to false in webpacker.yml and run `bin/webpacker -w`
54
- MSG
55
- end
56
-
57
- warn "Webpacker::Compiler.watched_paths has been deprecated. Set additional_paths in webpacker.yml instead." unless watched_paths.empty?
58
- root_path = Pathname.new(File.expand_path(config.root_path))
59
- expanded_paths = [*default_watched_paths, *watched_paths].map do |path|
60
- root_path.join(path)
61
- end
62
- latest_modified = Dir[*expanded_paths].max_by { |f| File.mtime(f) }
63
- File.mtime(latest_modified).to_i
64
- end
65
-
66
30
  def optionalRubyRunner
67
31
  bin_webpack_path = config.root_path.join("bin/webpacker")
68
32
  first_line = File.readlines(bin_webpack_path).first.chomp
@@ -93,15 +57,6 @@ class Webpacker::Compiler
93
57
  status.success?
94
58
  end
95
59
 
96
- def default_watched_paths
97
- [
98
- *config.additional_paths.map { |path| "#{path}{,/**/*}" },
99
- "#{config.source_path}{,/**/*}",
100
- "yarn.lock", "package.json",
101
- "config/webpack{,/**/*}"
102
- ].freeze
103
- end
104
-
105
60
  def webpack_env
106
61
  return env unless defined?(ActionController::Base)
107
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
@@ -85,6 +85,10 @@ class Webpacker::Configuration
85
85
  fetch(:webpack_compile_output)
86
86
  end
87
87
 
88
+ def compiler_strategy
89
+ fetch(:compiler_strategy)
90
+ end
91
+
88
92
  def fetch(key)
89
93
  data.fetch(key, defaults[key])
90
94
  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
@@ -19,6 +19,10 @@ class Webpacker::Instance
19
19
  )
20
20
  end
21
21
 
22
+ def strategy
23
+ @strategy ||= Webpacker::CompilerStrategy.from_config
24
+ end
25
+
22
26
  def compiler
23
27
  @compiler ||= Webpacker::Compiler.new self
24
28
  end
@@ -0,0 +1,40 @@
1
+ require "webpacker/base_strategy"
2
+
3
+ module Webpacker
4
+ class MtimeStrategy < BaseStrategy
5
+ # Returns true if manifest file mtime is newer than the timestamp of the last modified watched file
6
+ def fresh?
7
+ manifest_mtime > latest_modified_timestamp
8
+ end
9
+
10
+ # Returns true if manifest file mtime is older than the timestamp of the last modified watched file
11
+ def stale?
12
+ !fresh?
13
+ end
14
+
15
+ private
16
+
17
+ def manifest_mtime
18
+ config.manifest_path.exist? ? File.mtime(config.manifest_path).to_i : 0
19
+ end
20
+
21
+ def latest_modified_timestamp
22
+ if Rails.env.development?
23
+ warn <<~MSG.strip
24
+ Webpacker::Compiler - Slow setup for development
25
+
26
+ Prepare JS assets with either:
27
+ 1. Running `bin/webpacker-dev-server`
28
+ 2. Set `compile` to false in webpacker.yml and run `bin/webpacker -w`
29
+ MSG
30
+ end
31
+
32
+ root_path = Pathname.new(File.expand_path(config.root_path))
33
+ expanded_paths = [*default_watched_paths].map do |path|
34
+ root_path.join(path)
35
+ end
36
+ latest_modified = Dir[*expanded_paths].max_by { |f| File.mtime(f) }
37
+ File.mtime(latest_modified).to_i
38
+ end
39
+ end
40
+ end
@@ -1,4 +1,4 @@
1
1
  module Webpacker
2
2
  # Change the version in package.json too, please!
3
- VERSION = "6.3.0-rc.1".freeze
3
+ VERSION = "6.3.0".freeze
4
4
  end
@@ -1,4 +1,4 @@
1
- const { dirname, join } = require('path')
1
+ const { dirname } = require('path')
2
2
  const { source_path: sourcePath } = require('../config')
3
3
 
4
4
  module.exports = {
@@ -12,7 +12,7 @@ module.exports = {
12
12
  .split('/')
13
13
  .slice(1)
14
14
 
15
- const foldersWithStatic = join('static', ...folders)
15
+ const foldersWithStatic = ['static', ...folders].join('/')
16
16
  return `${foldersWithStatic}/[name]-[hash][ext][query]`
17
17
  }
18
18
  }
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shakapacker",
3
- "version": "6.3.0-rc.1",
3
+ "version": "6.3.0",
4
4
  "description": "Use webpack to manage app-like JavaScript modules in Rails",
5
5
  "main": "package/index.js",
6
6
  "files": [
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "peerDependencies": {
15
15
  "@babel/core": "^7.17.9",
16
- "@babel/plugin-transform-runtime": "^7.17.9",
16
+ "@babel/plugin-transform-runtime": "^7.17.0",
17
17
  "@babel/preset-env": "^7.16.11",
18
18
  "@babel/runtime": "^7.17.9",
19
19
  "babel-loader": "^8.2.4",
@@ -22,7 +22,7 @@
22
22
  "webpack": "^5.72.0",
23
23
  "webpack-assets-manifest": "^5.0.6",
24
24
  "webpack-cli": "^4.9.2",
25
- "webpack-dev-server": "^4.8.1",
25
+ "webpack-dev-server": "^4.9.0",
26
26
  "webpack-merge": "^5.8.0"
27
27
  },
28
28
  "dependencies": {
@@ -0,0 +1,27 @@
1
+ require "test_helper"
2
+
3
+ class CompilerStrategyTest < Minitest::Test
4
+ def test_mtime_strategy_returned
5
+ Webpacker.config.stub :compiler_strategy, "mtime" do
6
+ assert_instance_of Webpacker::MtimeStrategy, Webpacker::CompilerStrategy.from_config
7
+ end
8
+ end
9
+
10
+ def test_digest_strategy_returned
11
+ Webpacker.config.stub :compiler_strategy, "digest" do
12
+ assert_instance_of Webpacker::DigestStrategy, Webpacker::CompilerStrategy.from_config
13
+ end
14
+ end
15
+
16
+ def test_raise_on_unknown_strategy
17
+ Webpacker.config.stub :compiler_strategy, "other" do
18
+ error = assert_raises do
19
+ Webpacker::CompilerStrategy.from_config
20
+ end
21
+
22
+ assert_equal \
23
+ "Unknown strategy 'other'. Available options are 'mtime' and 'digest'.",
24
+ error.message
25
+ end
26
+ end
27
+ end
@@ -9,46 +9,43 @@ class CompilerTest < Minitest::Test
9
9
  Webpacker.compiler.env = {}
10
10
  end
11
11
 
12
- def setup
13
- @manifest_timestamp = Time.parse("2021-01-01 12:34:56 UTC")
14
- end
15
-
16
- def with_stubs(latest_timestamp:, manifest_exists: true)
17
- Webpacker.compiler.stub :latest_modified_timestamp, latest_timestamp do
18
- FileTest.stub :exist?, manifest_exists do
19
- File.stub :mtime, @manifest_timestamp do
20
- yield
21
- end
22
- end
12
+ def test_compile_true_when_fresh
13
+ mock = Minitest::Mock.new
14
+ mock.expect(:stale?, false)
15
+ Webpacker.compiler.stub(:strategy, mock) do
16
+ assert Webpacker.compiler.compile
23
17
  end
18
+ assert_mock mock
24
19
  end
25
20
 
26
- def test_freshness_when_manifest_missing
27
- latest_timestamp = @manifest_timestamp + 3600
28
-
29
- with_stubs(latest_timestamp: latest_timestamp.to_i, manifest_exists: false) do
30
- assert Webpacker.compiler.stale?
31
- end
32
- end
21
+ def test_after_compile_hook_called_on_success
22
+ mock = Minitest::Mock.new
23
+ mock.expect(:stale?, true)
24
+ mock.expect(:after_compile_hook, nil)
33
25
 
34
- def test_freshness_when_manifest_older
35
- latest_timestamp = @manifest_timestamp + 3600
26
+ status = OpenStruct.new(success?: true)
36
27
 
37
- with_stubs(latest_timestamp: latest_timestamp.to_i) do
38
- assert Webpacker.compiler.stale?
28
+ Webpacker.compiler.stub(:strategy, mock) do
29
+ Open3.stub :capture3, [:sterr, :stdout, status] do
30
+ Webpacker.compiler.compile
31
+ end
39
32
  end
33
+ assert_mock mock
40
34
  end
41
35
 
42
- def test_freshness_when_manifest_newer
43
- latest_timestamp = @manifest_timestamp - 3600
36
+ def test_after_compile_hook_called_on_failure
37
+ mock = Minitest::Mock.new
38
+ mock.expect(:stale?, true)
39
+ mock.expect(:after_compile_hook, nil)
44
40
 
45
- with_stubs(latest_timestamp: latest_timestamp.to_i) do
46
- assert Webpacker.compiler.fresh?
47
- end
48
- end
41
+ status = OpenStruct.new(success?: false)
49
42
 
50
- def test_compile
51
- assert !Webpacker.compiler.compile
43
+ Webpacker.compiler.stub(:strategy, mock) do
44
+ Open3.stub :capture3, [:sterr, :stdout, status] do
45
+ Webpacker.compiler.compile
46
+ end
47
+ end
48
+ assert_mock mock
52
49
  end
53
50
 
54
51
  def test_external_env_variables
@@ -0,0 +1,33 @@
1
+ require "test_helper"
2
+
3
+ class DigestStrategyTest < Minitest::Test
4
+ def remove_compilation_digest_path
5
+ @digest_strategy.send(:compilation_digest_path).tap do |path|
6
+ path.delete if path.exist?
7
+ end
8
+ end
9
+
10
+ def setup
11
+ @digest_strategy = Webpacker::DigestStrategy.new
12
+ remove_compilation_digest_path
13
+ end
14
+
15
+ def teardown
16
+ remove_compilation_digest_path
17
+ end
18
+
19
+ def test_freshness
20
+ assert @digest_strategy.stale?
21
+ assert !@digest_strategy.fresh?
22
+ end
23
+
24
+ def test_freshness_after_compilation_hook
25
+ @digest_strategy.after_compile_hook
26
+ assert @digest_strategy.fresh?
27
+ assert !@digest_strategy.stale?
28
+ end
29
+
30
+ def test_compilation_digest_path
31
+ assert_equal @digest_strategy.send(:compilation_digest_path).basename.to_s, "last-compilation-digest-#{Webpacker.env}"
32
+ end
33
+ end
@@ -0,0 +1,42 @@
1
+ require "test_helper"
2
+
3
+ class MtimeStrategyTest < Minitest::Test
4
+ def setup
5
+ @mtime_strategy = Webpacker::MtimeStrategy.new
6
+ @manifest_timestamp = Time.parse("2021-01-01 12:34:56 UTC")
7
+ end
8
+
9
+ def with_stubs(latest_timestamp:, manifest_exists: true)
10
+ @mtime_strategy.stub :latest_modified_timestamp, latest_timestamp do
11
+ FileTest.stub :exist?, manifest_exists do
12
+ File.stub :mtime, @manifest_timestamp do
13
+ yield
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ def test_freshness_when_manifest_missing
20
+ latest_timestamp = @manifest_timestamp + 3600
21
+
22
+ with_stubs(latest_timestamp: latest_timestamp.to_i, manifest_exists: false) do
23
+ assert @mtime_strategy.stale?
24
+ end
25
+ end
26
+
27
+ def test_freshness_when_manifest_older
28
+ latest_timestamp = @manifest_timestamp + 3600
29
+
30
+ with_stubs(latest_timestamp: latest_timestamp.to_i) do
31
+ assert @mtime_strategy.stale?
32
+ end
33
+ end
34
+
35
+ def test_freshness_when_manifest_newer
36
+ latest_timestamp = @manifest_timestamp - 3600
37
+
38
+ with_stubs(latest_timestamp: latest_timestamp.to_i) do
39
+ assert @mtime_strategy.fresh?
40
+ end
41
+ end
42
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shakapacker
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.3.0.pre.rc.1
4
+ version: 6.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2022-04-24 00:00:00.000000000 Z
13
+ date: 2022-05-19 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -142,6 +142,7 @@ files:
142
142
  - docs/customizing_babel_config.md
143
143
  - docs/deployment.md
144
144
  - docs/developing_webpacker.md
145
+ - docs/react.md
145
146
  - docs/style_loader_vs_mini_css.md
146
147
  - docs/troubleshooting.md
147
148
  - docs/using_esbuild_loader.md
@@ -179,16 +180,20 @@ files:
179
180
  - lib/tasks/webpacker/yarn_install.rake
180
181
  - lib/tasks/yarn.rake
181
182
  - lib/webpacker.rb
183
+ - lib/webpacker/base_strategy.rb
182
184
  - lib/webpacker/commands.rb
183
185
  - lib/webpacker/compiler.rb
186
+ - lib/webpacker/compiler_strategy.rb
184
187
  - lib/webpacker/configuration.rb
185
188
  - lib/webpacker/dev_server.rb
186
189
  - lib/webpacker/dev_server_proxy.rb
187
190
  - lib/webpacker/dev_server_runner.rb
191
+ - lib/webpacker/digest_strategy.rb
188
192
  - lib/webpacker/env.rb
189
193
  - lib/webpacker/helper.rb
190
194
  - lib/webpacker/instance.rb
191
195
  - lib/webpacker/manifest.rb
196
+ - lib/webpacker/mtime_strategy.rb
192
197
  - lib/webpacker/railtie.rb
193
198
  - lib/webpacker/runner.rb
194
199
  - lib/webpacker/version.rb
@@ -236,10 +241,12 @@ files:
236
241
  - package/utils/helpers.js
237
242
  - rakelib/release.rake
238
243
  - test/command_test.rb
244
+ - test/compiler_strategy_test.rb
239
245
  - test/compiler_test.rb
240
246
  - test/configuration_test.rb
241
247
  - test/dev_server_runner_test.rb
242
248
  - test/dev_server_test.rb
249
+ - test/digest_strategy_test.rb
243
250
  - test/engine_rake_tasks_test.rb
244
251
  - test/env_test.rb
245
252
  - test/fixtures/beta_package.json
@@ -261,6 +268,7 @@ files:
261
268
  - test/mounted_app/test/dummy/config/environment.rb
262
269
  - test/mounted_app/test/dummy/config/webpacker.yml
263
270
  - test/mounted_app/test/dummy/package.json
271
+ - test/mtime_strategy_test.rb
264
272
  - test/rake_tasks_test.rb
265
273
  - test/test_app/Rakefile
266
274
  - test/test_app/app/packs/entrypoints/application.js
@@ -292,7 +300,7 @@ homepage: https://github.com/shakacode/shakapacker
292
300
  licenses:
293
301
  - MIT
294
302
  metadata:
295
- source_code_uri: https://github.com/shakacode/shakapacker/tree/v6.3.0-rc.1
303
+ source_code_uri: https://github.com/shakacode/shakapacker/tree/v6.3.0
296
304
  post_install_message:
297
305
  rdoc_options: []
298
306
  require_paths:
@@ -304,9 +312,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
304
312
  version: 2.6.0
305
313
  required_rubygems_version: !ruby/object:Gem::Requirement
306
314
  requirements:
307
- - - ">"
315
+ - - ">="
308
316
  - !ruby/object:Gem::Version
309
- version: 1.3.1
317
+ version: '0'
310
318
  requirements: []
311
319
  rubygems_version: 3.2.32
312
320
  signing_key:
@@ -314,10 +322,12 @@ specification_version: 4
314
322
  summary: Use webpack to manage app-like JavaScript modules in Rails
315
323
  test_files:
316
324
  - test/command_test.rb
325
+ - test/compiler_strategy_test.rb
317
326
  - test/compiler_test.rb
318
327
  - test/configuration_test.rb
319
328
  - test/dev_server_runner_test.rb
320
329
  - test/dev_server_test.rb
330
+ - test/digest_strategy_test.rb
321
331
  - test/engine_rake_tasks_test.rb
322
332
  - test/env_test.rb
323
333
  - test/fixtures/beta_package.json
@@ -339,6 +349,7 @@ test_files:
339
349
  - test/mounted_app/test/dummy/config/environment.rb
340
350
  - test/mounted_app/test/dummy/config/webpacker.yml
341
351
  - test/mounted_app/test/dummy/package.json
352
+ - test/mtime_strategy_test.rb
342
353
  - test/rake_tasks_test.rb
343
354
  - test/test_app/Rakefile
344
355
  - test/test_app/app/packs/entrypoints/application.js