shakapacker 8.3.0 → 8.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 85c3a527653e307cedc881f7879b245feaca4d79b47f7f7abac358f38a1e8305
4
- data.tar.gz: eae6e26f151f0e6d346f9cef659854b91b7e0a4a3f388aa5cef016d6323f2c52
3
+ metadata.gz: 6ccbba3c39a550ae683e13c45e3ac4f79733ae6ffebabf2d6ab55f37b7427d99
4
+ data.tar.gz: 569998bb9475e8ed2b8ba786d1ccdf2b84d562d61ba8ef78d08ad195c3ea2230
5
5
  SHA512:
6
- metadata.gz: b04c921e57bb1fe74f9811d5e7cc9ba137600f9da55b906db3d51cc754533c7ed62f69bf9800172e07a2c4676a61bb13e0b939e12f25322c3065060a8c46d015
7
- data.tar.gz: 870d7895ec05e62183f8958bae438b417b490df5c22d30f656a818d3e9c2747b8a16adb2f973bb0a6a36ac497ce4e725a7d2b139ece7ef1e96ac4171285673da
6
+ metadata.gz: 2c529d5f970394ca40b79d7ac1ddb430c197cbd508bf533195f13d81937cca872232436bcb92c5a8f38eafc9968ef0d1a82c280120545e421d143dcf72d12f0c
7
+ data.tar.gz: b341f56e92124b2418b56483dab20374aee03fca5de1e946e5b09ce1f2ca7d0daa16c47ed365a90f51a8f5bdf55235dbe9107cf95dc0a3b9b00e19056f72f343
data/CHANGELOG.md CHANGED
@@ -8,6 +8,19 @@
8
8
  ## [Unreleased]
9
9
  Changes since the last non-beta release.
10
10
 
11
+ ## [v8.4.0] - September 8, 2025
12
+
13
+ ### Added
14
+
15
+ - Support for subresource integrity. [PR 570](https://github.com/shakacode/shakapacker/pull/570) by [panagiotisplytas](https://github.com/panagiotisplytas)
16
+
17
+ ### Fixed
18
+
19
+ - Install the latest major version of peer dependencies [PR 576](https://github.com/shakacode/shakapacker/pull/576) by [G-Rath](https://github.com/g-rath).
20
+ - Remove duplicate word in comment from generated `shakapacker.yml` config [PR 572](https://github.com/shakacode/shakapacker/pull/572) by [G-Rath](https://github.com/g-rath).
21
+ - fix: update webpack-dev-server to secure versions (^4.15.2 || ^5.2.2) [PR 585](https://github.com/shakacode/shakapacker/pull/585) by [justin808](https://github.com/justin808)
22
+
23
+
11
24
  ## [v8.3.0] - April 25, 2025
12
25
  ### Added
13
26
 
@@ -419,7 +432,8 @@ Note: [Rubygem is 6.3.0.pre.rc.1](https://rubygems.org/gems/shakapacker/versions
419
432
  ## v5.4.3 and prior changes from rails/webpacker
420
433
  See [CHANGELOG.md in rails/webpacker (up to v5.4.3)](https://github.com/rails/webpacker/blob/master/CHANGELOG.md)
421
434
 
422
- [Unreleased]: https://github.com/shakacode/shakapacker/compare/v8.3.0...main
435
+ [Unreleased]: https://github.com/shakacode/shakapacker/compare/v8.4.0...main
436
+ [v8.4.0]: https://github.com/shakacode/shakapacker/compare/v8.3.0...v8.4.0
423
437
  [v8.3.0]: https://github.com/shakacode/shakapacker/compare/v8.2.0...v8.3.0
424
438
  [v8.2.0]: https://github.com/shakacode/shakapacker/compare/v8.1.0...v8.2.0
425
439
  [v8.1.0]: https://github.com/shakacode/shakapacker/compare/v8.0.2...v8.1.0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shakapacker (8.3.0)
4
+ shakapacker (8.4.0)
5
5
  activesupport (>= 5.2)
6
6
  package_json
7
7
  rack-proxy (>= 0.6.1)
data/README.md CHANGED
@@ -625,7 +625,7 @@ By default, you will find the Shakapacker preset in your `package.json`. Note, y
625
625
  ]
626
626
  },
627
627
  ```
628
- Optionally, you can change your Babel configuration by removing these lines in your `package.json` and adding [a Babel](https://babeljs.io/docs/en/config-files) configuration file](https://babeljs.io/docs/en/config-files) to your project. For an example of customization based on the original, see [Customizing Babel Config](./docs/customizing_babel_config.md).
628
+ Optionally, you can change your Babel configuration by removing these lines in your `package.json` and adding [a Babel configuration file](https://babeljs.io/docs/en/config-files) to your project. For an example of customization based on the original, see [Customizing Babel Config](./docs/customizing_babel_config.md).
629
629
 
630
630
 
631
631
  ### SWC configuration
data/docs/react.md CHANGED
@@ -61,11 +61,7 @@ const webpackConfig = generateWebpackConfig();
61
61
 
62
62
  if (isDevelopment && inliningCss) {
63
63
  webpackConfig.plugins.push(
64
- new ReactRefreshWebpackPlugin({
65
- overlay: {
66
- sockPort: webpackConfig.devServer.port,
67
- },
68
- })
64
+ new ReactRefreshWebpackPlugin()
69
65
  );
70
66
  }
71
67
 
@@ -201,11 +197,7 @@ const webpackConfig = generateWebpackConfig();
201
197
 
202
198
  if (isDevelopment && inliningCss) {
203
199
  webpackConfig.plugins.push(
204
- new ReactRefreshWebpackPlugin({
205
- overlay: {
206
- sockPort: webpackConfig.devServer.port,
207
- },
208
- })
200
+ new ReactRefreshWebpackPlugin()
209
201
  );
210
202
  }
211
203
 
@@ -0,0 +1,54 @@
1
+ # Subresource integrity
2
+ It's a cryptographic hash that helps browsers check that the served js or css file has not been tampered in any way.
3
+
4
+ [MDN - Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity)
5
+
6
+ ## Important notes
7
+ - If you somehow modify the file after the hash was generated, it will automatically be considered as tampered, and the browser will not allow it to be executed.
8
+ - Enabling subresource integrity generation, will change the structure of `manifest.json`. Keep that in mind if you utilize this file in any other custom implementation.
9
+
10
+ Before:
11
+ ```json
12
+ {
13
+ "application.js": "/path_to_asset"
14
+ }
15
+ ```
16
+
17
+ After:
18
+ ```json
19
+ {
20
+ "application.js": {
21
+ "src": "/path_to_asset",
22
+ "integrity": "<sha256-hash> <sha384-hash> <sha512-hash>"
23
+ }
24
+ }
25
+ ```
26
+
27
+ ## Possible CORS issues
28
+ Enabling subresource integrity for an asset, actually enforces CORS checks on that resource too. Which means that
29
+ if you haven't set that up properly beforehand, it will probably lead to CORS errors with cached assets.
30
+
31
+ [MDN - How browsers handle Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity#how_browsers_handle_subresource_integrity)
32
+
33
+ ## Configuration
34
+
35
+ By default, this setting is disabled, to ensure backwards compatibility, and let developers adapt at their own pace.
36
+ This may change in the future, as it is a very nice security feature, and it should be enabled by default.
37
+
38
+ To enable it, just add this in `shakapacker.yml`
39
+ ```yml
40
+ integrity:
41
+ enabled: true
42
+ ```
43
+
44
+ For further customization, you can also utilize the options `hash_functions` that control the functions used to generate
45
+ integrity hashes. And `cross_origin` that sets the cross-origin loading attribute.
46
+
47
+ ```yml
48
+ integrity:
49
+ enabled: true
50
+ hash_functions: ["sha256", "sha384", "sha512"]
51
+ cross_origin: "anonymous" # or "use-credentials"
52
+ ```
53
+
54
+ This will utilize under the hood webpack-subresource-integrity plugin and will modify `manifest.json` to include integrity hashes.
@@ -42,7 +42,7 @@ default: &default
42
42
  # Raises an error if there is a mismatch in the shakapacker gem and npm package being used
43
43
  ensure_consistent_versioning: true
44
44
 
45
- # Select whether the compiler will use SHA digest ('digest' option) or most most recent modified timestamp ('mtime') to determine freshness
45
+ # Select whether the compiler will use SHA digest ('digest' option) or most recent modified timestamp ('mtime') to determine freshness
46
46
  compiler_strategy: digest
47
47
 
48
48
  # Select whether the compiler will always use a content hash and not just in production
@@ -55,6 +55,16 @@ default: &default
55
55
  # SHAKAPACKER_ASSET_HOST will override both configurations.
56
56
  # asset_host: custom-path
57
57
 
58
+ # Utilizing webpack-subresource-integrity plugin, will generate integrity hashes for all entries in manifest.json
59
+ # https://github.com/waysact/webpack-subresource-integrity/tree/main/webpack-subresource-integrity
60
+ integrity:
61
+ enabled: false
62
+ # Which cryptographic function(s) to use, for generating the integrity hash(es). Default sha-384. Other possible values sha256, sha512
63
+ hash_functions: ["sha384"]
64
+ # Default "anonymous". Other possible value "use-credentials"
65
+ # https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity#cross-origin_resource_sharing_and_subresource_integrity
66
+ cross_origin: "anonymous"
67
+
58
68
  development:
59
69
  <<: *default
60
70
  compile: true
@@ -71,7 +81,7 @@ development:
71
81
  # Hot Module Replacement updates modules while the application is running without a full reload
72
82
  # Used instead of the `hot` key in https://webpack.js.org/configuration/dev-server/#devserverhot
73
83
  hmr: false
74
- # If HMR is on, CSS will by inlined by delivering it as part of the script payload via style-loader. Be sure
84
+ # If HMR is on, CSS will be inlined by delivering it as part of the script payload via style-loader. Be sure
75
85
  # that you add style-loader to your project dependencies.
76
86
  #
77
87
  # If you want to instead deliver CSS via <link> with the mini-css-extract-plugin, set inline_css to false.
@@ -114,7 +124,7 @@ production:
114
124
  # Production depends on precompilation of packs prior to booting for performance.
115
125
  compile: false
116
126
 
117
- # Use content hash for naming assets. Cannot be overridden by for production.
127
+ # Use content hash for naming assets. Cannot be overridden in production.
118
128
  useContentHash: true
119
129
 
120
130
  # Cache manifest.json for performance
@@ -136,7 +136,7 @@ Dir.chdir(Rails.root) do
136
136
  dev_dependencies_to_add = []
137
137
 
138
138
  peers.each do |(package, version)|
139
- major_version = version.match(/(\d+)/)[1]
139
+ major_version = version.split("||").last.match(/(\d+)/)[1]
140
140
  entry = "#{package}@#{major_version}"
141
141
 
142
142
  if dev_dependency_packages.include? package
@@ -99,6 +99,10 @@ class Shakapacker::Configuration
99
99
  )
100
100
  end
101
101
 
102
+ def integrity
103
+ fetch(:integrity)
104
+ end
105
+
102
106
  private
103
107
  def data
104
108
  @data ||= load
@@ -109,11 +109,11 @@ module Shakapacker::Helper
109
109
  @javascript_pack_tag_loaded = true
110
110
 
111
111
  capture do
112
- concat javascript_include_tag(*async, **options.dup.tap { |o| o[:async] = true })
112
+ render_tags(async, :javascript, **options.dup.tap { |o| o[:async] = true })
113
113
  concat "\n" if async.any? && deferred.any?
114
- concat javascript_include_tag(*deferred, **options.dup.tap { |o| o[:defer] = true })
114
+ render_tags(deferred, :javascript, **options.dup.tap { |o| o[:defer] = true })
115
115
  concat "\n" if sync.any? && deferred.any?
116
- concat javascript_include_tag(*sync, **options)
116
+ render_tags(sync, :javascript, options)
117
117
  end
118
118
  end
119
119
 
@@ -166,7 +166,9 @@ module Shakapacker::Helper
166
166
 
167
167
  @stylesheet_pack_tag_loaded = true
168
168
 
169
- stylesheet_link_tag(*(requested_packs | appended_packs), **options)
169
+ capture do
170
+ render_tags(requested_packs | appended_packs, :stylesheet, options)
171
+ end
170
172
  end
171
173
 
172
174
  def append_stylesheet_pack_tag(*names)
@@ -238,4 +240,38 @@ module Shakapacker::Helper
238
240
  rescue
239
241
  path_to_asset(current_shakapacker_instance.manifest.lookup!(name), options)
240
242
  end
243
+
244
+ def lookup_integrity(source)
245
+ (source.respond_to?(:dig) && source.dig("integrity")) || nil
246
+ end
247
+
248
+ def lookup_source(source)
249
+ (source.respond_to?(:dig) && source.dig("src")) || source
250
+ end
251
+
252
+ # Handles rendering javascript and stylesheet tags with integrity, if that's enabled.
253
+ def render_tags(sources, type, options)
254
+ return unless sources.present? || type.present?
255
+
256
+ sources.each.with_index do |source, index|
257
+ tag_source = lookup_source(source)
258
+
259
+ if current_shakapacker_instance.config.integrity[:enabled]
260
+ integrity = lookup_integrity(source)
261
+
262
+ if integrity.present?
263
+ options[:integrity] = integrity
264
+ options[:crossorigin] = current_shakapacker_instance.config.integrity[:cross_origin]
265
+ end
266
+ end
267
+
268
+ if type == :javascript
269
+ concat javascript_include_tag(tag_source, **options)
270
+ else
271
+ concat stylesheet_link_tag(tag_source, **options)
272
+ end
273
+
274
+ concat "\n" unless index == sources.size - 1
275
+ end
276
+ end
241
277
  end
@@ -67,7 +67,12 @@ class Shakapacker::Manifest
67
67
  end
68
68
 
69
69
  def find(name)
70
- data[name.to_s].presence
70
+ return nil unless data[name.to_s].present?
71
+
72
+ return data[name.to_s] unless data[name.to_s].respond_to?(:dig)
73
+
74
+ # Try to return src, if that fails, (ex. entrypoints object) return the whole object.
75
+ data[name.to_s].dig("src") || data[name.to_s]
71
76
  end
72
77
 
73
78
  def full_pack_name(name, pack_type)
@@ -1,4 +1,4 @@
1
1
  module Shakapacker
2
2
  # Change the version in package.json too, please!
3
- VERSION = "8.3.0".freeze
3
+ VERSION = "8.4.0".freeze
4
4
  end
data/package/config.js CHANGED
@@ -50,5 +50,7 @@ if (config.manifest_path) {
50
50
  } else {
51
51
  config.manifestPath = resolve(config.outputPath, "manifest.json")
52
52
  }
53
+ // Ensure no duplicate hash functions exist in the returned config object
54
+ config.integrity.hash_functions = [...new Set(config.integrity.hash_functions)]
53
55
 
54
56
  module.exports = config
@@ -86,7 +86,9 @@ const getPlugins = () => {
86
86
  writeToDisk: true,
87
87
  output: config.manifestPath,
88
88
  entrypointsUseAssets: true,
89
- publicPath: config.publicPathWithoutCDN
89
+ publicPath: config.publicPathWithoutCDN,
90
+ integrity: config.integrity.enabled,
91
+ integrityHashes: config.integrity.hash_functions
90
92
  })
91
93
  ]
92
94
 
@@ -105,6 +107,22 @@ const getPlugins = () => {
105
107
  )
106
108
  }
107
109
 
110
+ if (
111
+ moduleExists("webpack-subresource-integrity") &&
112
+ config.integrity.enabled
113
+ ) {
114
+ const {
115
+ SubresourceIntegrityPlugin
116
+ } = require("webpack-subresource-integrity")
117
+
118
+ plugins.push(
119
+ new SubresourceIntegrityPlugin({
120
+ hashFuncNames: config.integrity.hash_functions,
121
+ enabled: isProduction
122
+ })
123
+ )
124
+ }
125
+
108
126
  return plugins
109
127
  }
110
128
 
@@ -121,7 +139,12 @@ module.exports = {
121
139
  // https://webpack.js.org/configuration/output/#outputhotupdatechunkfilename
122
140
  hotUpdateChunkFilename: "js/[id].[fullhash].hot-update.js",
123
141
  path: config.outputPath,
124
- publicPath: config.publicPath
142
+ publicPath: config.publicPath,
143
+
144
+ // This is required for SRI to work.
145
+ crossOriginLoading: config.integrity.enabled
146
+ ? config.integrity.cross_origin
147
+ : false
125
148
  },
126
149
  entry: getEntryObject(),
127
150
  resolve: {
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shakapacker",
3
- "version": "8.3.0",
3
+ "version": "8.4.0",
4
4
  "description": "Use webpack to manage app-like JavaScript modules in Rails",
5
5
  "homepage": "https://github.com/shakacode/shakapacker",
6
6
  "bugs": {
@@ -46,6 +46,7 @@
46
46
  "thenify": "^3.3.1",
47
47
  "webpack": "5.93.0",
48
48
  "webpack-assets-manifest": "^5.0.6",
49
+ "webpack-subresource-integrity": "^5.1.0",
49
50
  "webpack-merge": "^5.8.0"
50
51
  },
51
52
  "peerDependencies": {
@@ -60,8 +61,9 @@
60
61
  "terser-webpack-plugin": "^5.3.1",
61
62
  "webpack": "^5.76.0",
62
63
  "webpack-assets-manifest": "^5.0.6 || ^6.0.0",
64
+ "webpack-subresource-integrity": "^5.1.0",
63
65
  "webpack-cli": "^4.9.2 || ^5.0.0 || ^6.0.0",
64
- "webpack-dev-server": "^4.9.0 || ^5.0.0",
66
+ "webpack-dev-server": "^4.15.2 || ^5.2.2",
65
67
  "webpack-merge": "^5.8.0 || ^6.0.0"
66
68
  },
67
69
  "peerDependenciesMeta": {
@@ -70,6 +72,9 @@
70
72
  },
71
73
  "@types/webpack": {
72
74
  "optional": true
75
+ },
76
+ "webpack-subresource-integrity": {
77
+ "optional": true
73
78
  }
74
79
  },
75
80
  "packageManager": "yarn@1.22.22",
@@ -54,4 +54,44 @@ describe("Config", () => {
54
54
  resolve("app/javascript/manifest.json")
55
55
  )
56
56
  })
57
+
58
+ test("should have integrity disabled by default", () => {
59
+ const config = require("../../package/config")
60
+ expect(config.integrity.enabled).toBe(false)
61
+ })
62
+
63
+ test("should have sha384 as default hash function", () => {
64
+ const config = require("../../package/config")
65
+ expect(config.integrity.hash_functions).toStrictEqual(["sha384"])
66
+ })
67
+
68
+ test("should have anonymous as default crossorigin", () => {
69
+ const config = require("../../package/config")
70
+ expect(config.integrity.cross_origin).toBe("anonymous")
71
+ })
72
+
73
+ test("should allow enabling integrity", () => {
74
+ process.env.SHAKAPACKER_CONFIG = "config/shakapacker_integrity.yml"
75
+ const config = require("../../package/config")
76
+
77
+ expect(config.integrity.enabled).toBe(true)
78
+ })
79
+
80
+ test("should allow configuring hash functions", () => {
81
+ process.env.SHAKAPACKER_CONFIG = "config/shakapacker_integrity.yml"
82
+ const config = require("../../package/config")
83
+
84
+ expect(config.integrity.hash_functions).toStrictEqual([
85
+ "sha384",
86
+ "sha256",
87
+ "sha512"
88
+ ])
89
+ })
90
+
91
+ test("should allow configuring crossorigin", () => {
92
+ process.env.SHAKAPACKER_CONFIG = "config/shakapacker_integrity.yml"
93
+ const config = require("../../package/config")
94
+
95
+ expect(config.integrity.cross_origin).toBe("use-credentials")
96
+ })
57
97
  })
data/yarn.lock CHANGED
@@ -4457,6 +4457,11 @@ typed-array-length@^1.0.7:
4457
4457
  possible-typed-array-names "^1.0.0"
4458
4458
  reflect.getprototypeof "^1.0.6"
4459
4459
 
4460
+ typed-assert@^1.0.8:
4461
+ version "1.0.9"
4462
+ resolved "https://registry.yarnpkg.com/typed-assert/-/typed-assert-1.0.9.tgz#8af9d4f93432c4970ec717e3006f33f135b06213"
4463
+ integrity sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==
4464
+
4460
4465
  unbox-primitive@^1.1.0:
4461
4466
  version "1.1.0"
4462
4467
  resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2"
@@ -4551,6 +4556,13 @@ webpack-sources@^3.2.3:
4551
4556
  resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
4552
4557
  integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
4553
4558
 
4559
+ webpack-subresource-integrity@^5.1.0:
4560
+ version "5.1.0"
4561
+ resolved "https://registry.yarnpkg.com/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz#8b7606b033c6ccac14e684267cb7fb1f5c2a132a"
4562
+ integrity sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==
4563
+ dependencies:
4564
+ typed-assert "^1.0.8"
4565
+
4554
4566
  webpack@5.93.0:
4555
4567
  version "5.93.0"
4556
4568
  resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.93.0.tgz#2e89ec7035579bdfba9760d26c63ac5c3462a5e5"
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: 8.3.0
4
+ version: 8.4.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: 2025-04-29 00:00:00.000000000 Z
13
+ date: 2025-09-09 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -165,6 +165,7 @@ files:
165
165
  - docs/react.md
166
166
  - docs/sprockets.md
167
167
  - docs/style_loader_vs_mini_css.md
168
+ - docs/subresource_integrity.md
168
169
  - docs/troubleshooting.md
169
170
  - docs/using_esbuild_loader.md
170
171
  - docs/using_swc_loader.md
@@ -284,7 +285,7 @@ homepage: https://github.com/shakacode/shakapacker
284
285
  licenses:
285
286
  - MIT
286
287
  metadata:
287
- source_code_uri: https://github.com/shakacode/shakapacker/tree/v8.3.0
288
+ source_code_uri: https://github.com/shakacode/shakapacker/tree/v8.4.0
288
289
  post_install_message:
289
290
  rdoc_options: []
290
291
  require_paths:
@@ -300,7 +301,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
300
301
  - !ruby/object:Gem::Version
301
302
  version: '0'
302
303
  requirements: []
303
- rubygems_version: 3.5.11
304
+ rubygems_version: 3.5.22
304
305
  signing_key:
305
306
  specification_version: 4
306
307
  summary: Use webpack to manage app-like JavaScript modules in Rails