shakapacker 9.3.0.beta.7 → 9.3.1

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.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/update-changelog.md +224 -0
  3. data/.github/actionlint-matcher.json +17 -0
  4. data/.github/workflows/dummy.yml +9 -0
  5. data/.github/workflows/generator.yml +13 -0
  6. data/.github/workflows/node.yml +83 -0
  7. data/.github/workflows/ruby.yml +11 -0
  8. data/.github/workflows/test-bundlers.yml +10 -0
  9. data/CHANGELOG.md +55 -111
  10. data/CLAUDE.md +6 -10
  11. data/CONTRIBUTING.md +57 -0
  12. data/Gemfile.lock +1 -1
  13. data/README.md +84 -8
  14. data/docs/api-reference.md +519 -0
  15. data/docs/configuration.md +38 -4
  16. data/docs/css-modules-export-mode.md +40 -6
  17. data/docs/rspack_migration_guide.md +238 -2
  18. data/docs/transpiler-migration.md +12 -9
  19. data/docs/troubleshooting.md +21 -21
  20. data/docs/using_swc_loader.md +13 -10
  21. data/docs/v9_upgrade.md +11 -2
  22. data/eslint.config.fast.js +128 -8
  23. data/eslint.config.js +89 -33
  24. data/knip.ts +8 -1
  25. data/lib/install/config/shakapacker.yml +20 -7
  26. data/lib/shakapacker/configuration.rb +274 -8
  27. data/lib/shakapacker/dev_server.rb +88 -1
  28. data/lib/shakapacker/dev_server_runner.rb +4 -0
  29. data/lib/shakapacker/doctor.rb +5 -5
  30. data/lib/shakapacker/instance.rb +85 -1
  31. data/lib/shakapacker/manifest.rb +85 -11
  32. data/lib/shakapacker/version.rb +1 -1
  33. data/lib/shakapacker.rb +143 -3
  34. data/lib/tasks/shakapacker/doctor.rake +1 -1
  35. data/lib/tasks/shakapacker/export_bundler_config.rake +4 -4
  36. data/package/config.ts +2 -4
  37. data/package/configExporter/buildValidator.ts +53 -29
  38. data/package/configExporter/cli.ts +106 -76
  39. data/package/configExporter/configFile.ts +33 -26
  40. data/package/configExporter/types.ts +64 -0
  41. data/package/configExporter/yamlSerializer.ts +118 -43
  42. data/package/dev_server.ts +3 -2
  43. data/package/env.ts +2 -2
  44. data/package/environments/__type-tests__/rspack-plugin-compatibility.ts +6 -6
  45. data/package/environments/base.ts +6 -6
  46. data/package/environments/development.ts +7 -9
  47. data/package/environments/production.ts +7 -8
  48. data/package/environments/test.ts +4 -2
  49. data/package/esbuild/index.ts +0 -2
  50. data/package/index.d.ts +1 -0
  51. data/package/index.d.ts.template +1 -0
  52. data/package/index.ts +28 -5
  53. data/package/loaders.d.ts +2 -2
  54. data/package/optimization/webpack.ts +29 -31
  55. data/package/plugins/rspack.ts +3 -1
  56. data/package/plugins/webpack.ts +5 -3
  57. data/package/rspack/index.ts +5 -4
  58. data/package/rules/file.ts +2 -1
  59. data/package/rules/jscommon.ts +1 -0
  60. data/package/rules/raw.ts +3 -1
  61. data/package/rules/rspack.ts +0 -2
  62. data/package/rules/sass.ts +0 -2
  63. data/package/rules/webpack.ts +0 -1
  64. data/package/swc/index.ts +0 -2
  65. data/package/types.ts +8 -11
  66. data/package/utils/debug.ts +0 -4
  67. data/package/utils/getStyleRule.ts +17 -9
  68. data/package/utils/helpers.ts +8 -4
  69. data/package/utils/pathValidation.ts +78 -18
  70. data/package/utils/requireOrError.ts +14 -5
  71. data/package/utils/typeGuards.ts +43 -46
  72. data/package/webpack-types.d.ts +2 -2
  73. data/package/webpackDevServerConfig.ts +5 -4
  74. data/package.json +2 -3
  75. data/test/package/configExporter/cli.test.js +440 -0
  76. data/test/package/configExporter/types.test.js +163 -0
  77. data/test/package/configExporter.test.js +264 -0
  78. data/test/package/transpiler-defaults.test.js +42 -0
  79. data/test/package/yamlSerializer.test.js +204 -0
  80. data/test/typescript/pathValidation.test.js +44 -0
  81. data/test/typescript/requireOrError.test.js +49 -0
  82. data/yarn.lock +0 -32
  83. metadata +14 -5
  84. data/.eslintrc.fast.js +0 -40
  85. data/.eslintrc.js +0 -84
@@ -3,13 +3,62 @@ require "json"
3
3
  require "active_support/core_ext/hash/keys"
4
4
  require "active_support/core_ext/hash/indifferent_access"
5
5
 
6
+ # Configuration management for Shakapacker
7
+ #
8
+ # Loads and provides access to settings from +config/shakapacker.yml+, including:
9
+ # - Source and output paths
10
+ # - Compilation settings
11
+ # - Dev server configuration
12
+ # - Asset bundler selection (webpack vs rspack)
13
+ # - JavaScript transpiler configuration (babel, swc, esbuild)
14
+ #
15
+ # Configuration values can be overridden via environment variables:
16
+ # - +SHAKAPACKER_CONFIG+ - path to config file
17
+ # - +SHAKAPACKER_PRECOMPILE+ - whether to precompile assets
18
+ # - +SHAKAPACKER_ASSETS_BUNDLER+ - which bundler to use
19
+ # - +SHAKAPACKER_ASSET_HOST+ - CDN or asset host URL
20
+ #
21
+ # @example Accessing configuration
22
+ # config = Shakapacker.config
23
+ # config.source_path
24
+ # #=> #<Pathname:/app/app/packs>
25
+ # config.webpack?
26
+ # #=> true
27
+ #
28
+ # @see https://github.com/shakacode/shakapacker/blob/main/docs/shakapacker.yml.md
6
29
  class Shakapacker::Configuration
7
30
  class << self
31
+ # Flag indicating whether Shakapacker is currently being installed
32
+ # Used to suppress certain validations during installation
33
+ # @return [Boolean] true if installation is in progress
34
+ # @api private
8
35
  attr_accessor :installing
9
36
  end
10
37
 
11
- attr_reader :root_path, :config_path, :env, :bundler_override
12
-
38
+ # The application root path
39
+ # @return [Pathname] the root path
40
+ attr_reader :root_path
41
+
42
+ # The path to the shakapacker.yml configuration file
43
+ # @return [Pathname] the config file path
44
+ attr_reader :config_path
45
+
46
+ # The current Rails environment
47
+ # @return [ActiveSupport::StringInquirer] the environment
48
+ attr_reader :env
49
+
50
+ # Override for the assets bundler (set via CLI flag)
51
+ # @return [String, nil] the bundler override or nil
52
+ # @api private
53
+ attr_reader :bundler_override
54
+
55
+ # Creates a new configuration instance
56
+ #
57
+ # @param root_path [Pathname] the application root path
58
+ # @param config_path [Pathname] the path to shakapacker.yml
59
+ # @param env [ActiveSupport::StringInquirer] the Rails environment
60
+ # @param bundler_override [String, nil] optional bundler override (webpack or rspack)
61
+ # @return [Shakapacker::Configuration] the new configuration instance
13
62
  def initialize(root_path:, config_path:, env:, bundler_override: nil)
14
63
  @root_path = root_path
15
64
  @env = env
@@ -17,22 +66,52 @@ class Shakapacker::Configuration
17
66
  @bundler_override = bundler_override
18
67
  end
19
68
 
69
+ # Returns the dev server configuration hash
70
+ #
71
+ # Contains settings like host, port, https, hmr, etc. for the webpack-dev-server.
72
+ #
73
+ # @return [Hash] the dev server configuration
20
74
  def dev_server
21
75
  fetch(:dev_server)
22
76
  end
23
77
 
78
+ # Returns whether automatic compilation is enabled
79
+ #
80
+ # When true, Shakapacker will automatically compile assets when they're requested
81
+ # and are stale. This is typically enabled in development and disabled in production.
82
+ #
83
+ # @return [Boolean] true if automatic compilation is enabled
24
84
  def compile?
25
85
  fetch(:compile)
26
86
  end
27
87
 
88
+ # Returns whether nested entries are enabled
89
+ #
90
+ # When true, allows organizing entry points in subdirectories within the
91
+ # source entry path.
92
+ #
93
+ # @return [Boolean] true if nested entries are allowed
28
94
  def nested_entries?
29
95
  fetch(:nested_entries)
30
96
  end
31
97
 
98
+ # Returns whether consistent versioning check is enabled
99
+ #
100
+ # When true, verifies that package.json and Gemfile versions of shakapacker match.
101
+ #
102
+ # @return [Boolean] true if version consistency checking is enabled
32
103
  def ensure_consistent_versioning?
33
104
  fetch(:ensure_consistent_versioning)
34
105
  end
35
106
 
107
+ # Returns whether Shakapacker should precompile assets
108
+ #
109
+ # Checks in order:
110
+ # 1. SHAKAPACKER_PRECOMPILE environment variable (yes/true/y/t or no/false/n/f)
111
+ # 2. shakapacker_precompile setting in config file
112
+ # 3. Defaults to false if config file doesn't exist
113
+ #
114
+ # @return [Boolean] true if assets should be precompiled
36
115
  def shakapacker_precompile?
37
116
  # ENV of false takes precedence
38
117
  return false if %w(no false n f).include?(ENV["SHAKAPACKER_PRECOMPILE"])
@@ -42,18 +121,40 @@ class Shakapacker::Configuration
42
121
  fetch(:shakapacker_precompile)
43
122
  end
44
123
 
124
+ # Returns the absolute path to the source directory
125
+ #
126
+ # This is where your JavaScript/CSS source files live (e.g., app/packs).
127
+ #
128
+ # @return [Pathname] the absolute source path
45
129
  def source_path
46
130
  root_path.join(fetch(:source_path))
47
131
  end
48
132
 
133
+ # Returns additional paths to include in compilation
134
+ #
135
+ # These paths are added to webpack/rspack's resolve configuration to allow
136
+ # importing modules from additional directories.
137
+ #
138
+ # @return [Array<String>] array of additional paths
49
139
  def additional_paths
50
140
  fetch(:additional_paths)
51
141
  end
52
142
 
143
+ # Returns the absolute path to the source entry directory
144
+ #
145
+ # Entry points (application.js, etc.) are found in this directory.
146
+ #
147
+ # @return [Pathname] the absolute entry path
53
148
  def source_entry_path
54
149
  source_path.join(relative_path(fetch(:source_entry_path)))
55
150
  end
56
151
 
152
+ # Returns the absolute path to the manifest.json file
153
+ #
154
+ # The manifest maps source file names to their compiled output paths with digests.
155
+ # Defaults to manifest.json in the public output directory if not configured.
156
+ #
157
+ # @return [Pathname] the absolute manifest path
57
158
  def manifest_path
58
159
  if data.has_key?(:manifest_path)
59
160
  root_path.join(fetch(:manifest_path))
@@ -62,14 +163,30 @@ class Shakapacker::Configuration
62
163
  end
63
164
  end
64
165
 
166
+ # Alias for {#manifest_path}
167
+ #
168
+ # @return [Pathname] the absolute manifest path
169
+ # @see #manifest_path
65
170
  def public_manifest_path
66
171
  manifest_path
67
172
  end
68
173
 
174
+ # Returns the absolute path to the public root directory
175
+ #
176
+ # This is typically the Rails +public/+ directory where compiled assets
177
+ # are served from.
178
+ #
179
+ # @return [Pathname] the absolute public path
69
180
  def public_path
70
181
  root_path.join(fetch(:public_root_path))
71
182
  end
72
183
 
184
+ # Returns the absolute path to the private output directory
185
+ #
186
+ # The private output path is for server-side bundles (e.g., SSR) that should
187
+ # not be publicly accessible. Returns nil if not configured.
188
+ #
189
+ # @return [Pathname, nil] the absolute private output path or nil
73
190
  def private_output_path
74
191
  private_path = fetch(:private_output_path)
75
192
  return nil unless private_path
@@ -77,26 +194,66 @@ class Shakapacker::Configuration
77
194
  root_path.join(private_path)
78
195
  end
79
196
 
197
+ # Returns the absolute path to the public output directory
198
+ #
199
+ # This is where compiled assets are written for public serving
200
+ # (typically +public/packs+).
201
+ #
202
+ # @return [Pathname] the absolute public output path
80
203
  def public_output_path
81
204
  public_path.join(fetch(:public_output_path))
82
205
  end
83
206
 
207
+ # Returns whether manifest caching is enabled
208
+ #
209
+ # When true, the manifest.json file is cached in memory and only reloaded
210
+ # when it changes. This improves performance in production.
211
+ #
212
+ # @return [Boolean] true if manifest should be cached
84
213
  def cache_manifest?
85
214
  fetch(:cache_manifest)
86
215
  end
87
216
 
217
+ # Returns the absolute path to the compilation cache directory
218
+ #
219
+ # Webpack/rspack uses this directory to cache compilation results for faster
220
+ # subsequent builds.
221
+ #
222
+ # @return [Pathname] the absolute cache path
88
223
  def cache_path
89
224
  root_path.join(fetch(:cache_path))
90
225
  end
91
226
 
227
+ # Returns whether webpack/rspack compilation output should be shown
228
+ #
229
+ # When true, displays webpack/rspack's compilation progress and results.
230
+ #
231
+ # @return [Boolean] true if compilation output should be displayed
92
232
  def webpack_compile_output?
93
233
  fetch(:webpack_compile_output)
94
234
  end
95
235
 
236
+ # Returns the compiler strategy for determining staleness
237
+ #
238
+ # Options:
239
+ # - +"mtime"+ - use file modification times (faster, less accurate)
240
+ # - +"digest"+ - use file content digests (slower, more accurate)
241
+ #
242
+ # @return [String] the compiler strategy ("mtime" or "digest")
96
243
  def compiler_strategy
97
244
  fetch(:compiler_strategy)
98
245
  end
99
246
 
247
+ # Returns the assets bundler to use (webpack or rspack)
248
+ #
249
+ # Resolution order:
250
+ # 1. CLI --bundler flag (via bundler_override)
251
+ # 2. SHAKAPACKER_ASSETS_BUNDLER environment variable
252
+ # 3. assets_bundler setting in config file
253
+ # 4. bundler setting in config file (deprecated)
254
+ # 5. Defaults to "webpack"
255
+ #
256
+ # @return [String] "webpack" or "rspack"
100
257
  def assets_bundler
101
258
  # CLI --bundler flag takes highest precedence
102
259
  return @bundler_override if @bundler_override
@@ -108,19 +265,35 @@ class Shakapacker::Configuration
108
265
  ENV["SHAKAPACKER_ASSETS_BUNDLER"] || fetch(:assets_bundler) || fetch(:bundler) || "webpack"
109
266
  end
110
267
 
111
- # Deprecated: Use assets_bundler instead
268
+ # Deprecated alias for {#assets_bundler}
269
+ #
270
+ # @deprecated Use {#assets_bundler} instead
271
+ # @return [String] the assets bundler
272
+ # @see #assets_bundler
112
273
  def bundler
113
274
  assets_bundler
114
275
  end
115
276
 
277
+ # Returns whether rspack is the configured bundler
278
+ #
279
+ # @return [Boolean] true if using rspack
116
280
  def rspack?
117
281
  assets_bundler == "rspack"
118
282
  end
119
283
 
284
+ # Returns whether webpack is the configured bundler
285
+ #
286
+ # @return [Boolean] true if using webpack
120
287
  def webpack?
121
288
  assets_bundler == "webpack"
122
289
  end
123
290
 
291
+ # Returns the precompile hook command to run after compilation
292
+ #
293
+ # The hook is a shell command that runs after successful compilation,
294
+ # useful for post-processing tasks.
295
+ #
296
+ # @return [String, nil] the hook command or nil if not configured
124
297
  def precompile_hook
125
298
  hook = fetch(:precompile_hook)
126
299
  return nil if hook.nil? || (hook.is_a?(String) && hook.strip.empty?)
@@ -132,6 +305,16 @@ class Shakapacker::Configuration
132
305
  hook.strip
133
306
  end
134
307
 
308
+ # Returns the JavaScript transpiler to use (babel, swc, or esbuild)
309
+ #
310
+ # Resolution order:
311
+ # 1. javascript_transpiler setting in config file
312
+ # 2. webpack_loader setting in config file (deprecated)
313
+ # 3. Default based on bundler (swc for rspack, babel for webpack)
314
+ #
315
+ # Validates that the configured transpiler matches installed packages.
316
+ #
317
+ # @return [String] "babel", "swc", or "esbuild"
135
318
  def javascript_transpiler
136
319
  # Show deprecation warning if using old 'webpack_loader' key
137
320
  if data.has_key?(:webpack_loader) && !data.has_key?(:javascript_transpiler)
@@ -147,11 +330,46 @@ class Shakapacker::Configuration
147
330
  transpiler
148
331
  end
149
332
 
150
- # Deprecated: Use javascript_transpiler instead
333
+ # Deprecated alias for {#javascript_transpiler}
334
+ #
335
+ # @deprecated Use {#javascript_transpiler} instead
336
+ # @return [String] the JavaScript transpiler
337
+ # @see #javascript_transpiler
151
338
  def webpack_loader
152
339
  javascript_transpiler
153
340
  end
154
341
 
342
+ # Returns the CSS Modules export mode configuration
343
+ #
344
+ # Controls how CSS Module class names are exported in JavaScript:
345
+ # - "named" (default): Use named exports with camelCase conversion (v9 behavior)
346
+ # - "default": Use default export with both original and camelCase names (v8 behavior)
347
+ #
348
+ # @return [String] "named" or "default"
349
+ # @raise [ArgumentError] if an invalid value is configured
350
+ def css_modules_export_mode
351
+ @css_modules_export_mode ||= begin
352
+ mode = fetch(:css_modules_export_mode) || "named"
353
+
354
+ # Validate the configuration value
355
+ valid_modes = ["named", "default"]
356
+ unless valid_modes.include?(mode)
357
+ raise ArgumentError,
358
+ "Invalid css_modules_export_mode: '#{mode}'. " \
359
+ "Valid values are: #{valid_modes.map { |m| "'#{m}'" }.join(', ')}. " \
360
+ "See https://github.com/shakacode/shakapacker/blob/main/docs/css-modules-export-mode.md"
361
+ end
362
+
363
+ mode
364
+ end
365
+ end
366
+
367
+ # Returns the path to the bundler configuration directory
368
+ #
369
+ # This is where webpack.config.js or rspack.config.js should be located.
370
+ # Defaults to config/webpack for webpack or config/rspack for rspack.
371
+ #
372
+ # @return [String] the relative path to the bundler config directory
155
373
  def assets_bundler_config_path
156
374
  custom_path = fetch(:assets_bundler_config_path)
157
375
  return custom_path if custom_path
@@ -160,6 +378,25 @@ class Shakapacker::Configuration
160
378
  rspack? ? "config/rspack" : "config/webpack"
161
379
  end
162
380
 
381
+ # Returns the raw configuration data hash
382
+ #
383
+ # Returns the merged configuration from the shakapacker.yml file for the current environment.
384
+ # The hash has symbolized keys loaded from the config file. Individual config values can be
385
+ # accessed through specific accessor methods like {#source_path}, which apply defaults via {#fetch}.
386
+ #
387
+ # The returned hash is frozen to prevent accidental mutations. To access config values,
388
+ # use the provided accessor methods instead of modifying this hash directly.
389
+ #
390
+ # @return [Hash<Symbol, Object>] the raw configuration data with symbolized keys (frozen)
391
+ # @example
392
+ # config.data[:source_path] #=> "app/javascript"
393
+ # config.data[:compile] #=> true
394
+ # @note The hash is frozen to prevent mutations. Use accessor methods for safe config access.
395
+ # @api public
396
+ def data
397
+ @data ||= load.freeze
398
+ end
399
+
163
400
  private
164
401
 
165
402
  def default_javascript_transpiler
@@ -170,6 +407,9 @@ class Shakapacker::Configuration
170
407
  def validate_transpiler_configuration(transpiler)
171
408
  return unless ENV["NODE_ENV"] != "test" # Skip validation in test environment
172
409
 
410
+ # Skip validation if transpiler is set to 'none' (custom webpack config)
411
+ return if transpiler == "none"
412
+
173
413
  # Check if package.json exists
174
414
  package_json_path = root_path.join("package.json")
175
415
  return unless package_json_path.exist?
@@ -220,10 +460,28 @@ class Shakapacker::Configuration
220
460
 
221
461
  public
222
462
 
463
+ # Fetches a configuration value
464
+ #
465
+ # Looks up the value in the loaded configuration data, falling back to
466
+ # the default configuration if not found.
467
+ #
468
+ # @param key [Symbol] the configuration key to fetch
469
+ # @return [Object] the configuration value
470
+ # @api private
223
471
  def fetch(key)
224
472
  data.fetch(key, defaults[key])
225
473
  end
226
474
 
475
+ # Returns the asset host URL for serving assets
476
+ #
477
+ # Resolution order:
478
+ # 1. SHAKAPACKER_ASSET_HOST environment variable
479
+ # 2. asset_host setting in config file
480
+ # 3. Rails ActionController::Base.helpers.compute_asset_host
481
+ #
482
+ # Used to serve assets from a CDN or different domain.
483
+ #
484
+ # @return [String, nil] the asset host URL or nil
227
485
  def asset_host
228
486
  ENV.fetch(
229
487
  "SHAKAPACKER_ASSET_HOST",
@@ -231,10 +489,22 @@ class Shakapacker::Configuration
231
489
  )
232
490
  end
233
491
 
492
+ # Returns whether subresource integrity (SRI) is enabled
493
+ #
494
+ # When true, generates integrity hashes for script and link tags to
495
+ # protect against compromised CDNs or man-in-the-middle attacks.
496
+ #
497
+ # @return [Boolean] true if integrity checking is enabled
234
498
  def integrity
235
499
  fetch(:integrity)
236
500
  end
237
501
 
502
+ # Returns whether HTTP/2 Early Hints are enabled
503
+ #
504
+ # When true, sends Early Hints headers to start loading assets before
505
+ # the full response is ready.
506
+ #
507
+ # @return [Boolean] true if early hints are enabled
238
508
  def early_hints
239
509
  fetch(:early_hints)
240
510
  end
@@ -272,10 +542,6 @@ class Shakapacker::Configuration
272
542
  [private_full_path.cleanpath.to_s, public_full_path.cleanpath.to_s]
273
543
  end
274
544
 
275
- def data
276
- @data ||= load
277
- end
278
-
279
545
  def load
280
546
  config = begin
281
547
  YAML.load_file(config_path.to_s, aliases: true)
@@ -1,16 +1,50 @@
1
+ # Development server status and configuration
2
+ #
3
+ # Provides methods to query the status and configuration of the webpack-dev-server
4
+ # or rspack-dev-server. This includes checking if the server is running, accessing
5
+ # its host/port, and querying features like HMR (Hot Module Replacement).
6
+ #
7
+ # The dev server runs during development to provide live reloading and hot module
8
+ # replacement. In production, the dev server is not used.
9
+ #
10
+ # @example Checking dev server status
11
+ # dev_server = Shakapacker.dev_server
12
+ # dev_server.running?
13
+ # #=> true
14
+ # dev_server.host_with_port
15
+ # #=> "localhost:3035"
16
+ # dev_server.hmr?
17
+ # #=> true
18
+ #
19
+ # @see Shakapacker::DevServerRunner
1
20
  class Shakapacker::DevServer
21
+ # Default environment variable prefix for dev server settings
2
22
  DEFAULT_ENV_PREFIX = "SHAKAPACKER_DEV_SERVER".freeze
3
23
 
4
24
  # Configure dev server connection timeout (in seconds), default: 0.1
5
- # Shakapacker.dev_server.connect_timeout = 1
25
+ # @example
26
+ # Shakapacker::DevServer.connect_timeout = 1
27
+ # @return [Float] the connection timeout in seconds
6
28
  cattr_accessor(:connect_timeout) { 0.1 }
7
29
 
30
+ # The Shakapacker configuration
31
+ # @return [Shakapacker::Configuration] the configuration
8
32
  attr_reader :config
9
33
 
34
+ # Creates a new dev server instance
35
+ #
36
+ # @param config [Shakapacker::Configuration] the Shakapacker configuration
37
+ # @return [Shakapacker::DevServer] the new dev server instance
10
38
  def initialize(config)
11
39
  @config = config
12
40
  end
13
41
 
42
+ # Returns whether the dev server is currently running
43
+ #
44
+ # Checks by attempting to open a TCP connection to the configured host and port.
45
+ # Returns false if the connection fails or if dev server is not configured.
46
+ #
47
+ # @return [Boolean] true if the dev server is running
14
48
  def running?
15
49
  if config.dev_server.present?
16
50
  Socket.tcp(host, port, connect_timeout: connect_timeout).close
@@ -22,14 +56,30 @@ class Shakapacker::DevServer
22
56
  false
23
57
  end
24
58
 
59
+ # Returns the dev server host
60
+ #
61
+ # Can be overridden via SHAKAPACKER_DEV_SERVER_HOST environment variable.
62
+ #
63
+ # @return [String] the host (e.g., "localhost", "0.0.0.0")
25
64
  def host
26
65
  fetch(:host)
27
66
  end
28
67
 
68
+ # Returns the dev server port
69
+ #
70
+ # Can be overridden via SHAKAPACKER_DEV_SERVER_PORT environment variable.
71
+ #
72
+ # @return [Integer] the port number (typically 3035)
29
73
  def port
30
74
  fetch(:port)
31
75
  end
32
76
 
77
+ # Returns the server type (http or https)
78
+ #
79
+ # Can be overridden via SHAKAPACKER_DEV_SERVER_SERVER environment variable.
80
+ # Validates that the value is "http" or "https", falling back to "http" if invalid.
81
+ #
82
+ # @return [String] "http" or "https"
33
83
  def server
34
84
  server_value = fetch(:server)
35
85
  server_type = server_value.is_a?(Hash) ? server_value[:type] : server_value
@@ -49,24 +99,55 @@ class Shakapacker::DevServer
49
99
  "http"
50
100
  end
51
101
 
102
+ # Returns the protocol for the dev server
103
+ #
104
+ # This is an alias that returns "https" if server is "https", otherwise "http".
105
+ #
106
+ # @return [String] "http" or "https"
52
107
  def protocol
53
108
  return "https" if server == "https"
54
109
 
55
110
  "http"
56
111
  end
57
112
 
113
+ # Returns the host and port as a single string
114
+ #
115
+ # @return [String] the host:port combination (e.g., "localhost:3035")
116
+ # @example
117
+ # dev_server.host_with_port
118
+ # #=> "localhost:3035"
58
119
  def host_with_port
59
120
  "#{host}:#{port}"
60
121
  end
61
122
 
123
+ # Returns whether pretty output is enabled
124
+ #
125
+ # When true, the dev server produces prettier, more readable output.
126
+ # Can be overridden via SHAKAPACKER_DEV_SERVER_PRETTY environment variable.
127
+ #
128
+ # @return [Boolean] true if pretty output is enabled
62
129
  def pretty?
63
130
  fetch(:pretty)
64
131
  end
65
132
 
133
+ # Returns whether Hot Module Replacement (HMR) is enabled
134
+ #
135
+ # When true, the dev server updates modules in the browser without a full
136
+ # page reload, preserving application state.
137
+ # Can be overridden via SHAKAPACKER_DEV_SERVER_HMR environment variable.
138
+ #
139
+ # @return [Boolean] true if HMR is enabled
66
140
  def hmr?
67
141
  fetch(:hmr)
68
142
  end
69
143
 
144
+ # Returns whether CSS inlining is enabled
145
+ #
146
+ # When true, CSS is injected inline via JavaScript instead of being loaded
147
+ # as separate stylesheet files. This enables HMR for CSS.
148
+ # Can be overridden via SHAKAPACKER_DEV_SERVER_INLINE_CSS environment variable.
149
+ #
150
+ # @return [Boolean] true if CSS should be inlined
70
151
  def inline_css?
71
152
  case fetch(:inline_css)
72
153
  when false, "false"
@@ -76,6 +157,12 @@ class Shakapacker::DevServer
76
157
  end
77
158
  end
78
159
 
160
+ # Returns the environment variable prefix for dev server settings
161
+ #
162
+ # Environment variables for dev server settings use this prefix (default: "SHAKAPACKER_DEV_SERVER").
163
+ # For example, SHAKAPACKER_DEV_SERVER_PORT sets the port.
164
+ #
165
+ # @return [String] the env var prefix (typically "SHAKAPACKER_DEV_SERVER")
79
166
  def env_prefix
80
167
  config.dev_server.fetch(:env_prefix, DEFAULT_ENV_PREFIX)
81
168
  end
@@ -18,6 +18,8 @@ module Shakapacker
18
18
  exit(0)
19
19
  end
20
20
 
21
+ Shakapacker.ensure_node_env!
22
+
21
23
  # Check for --build flag
22
24
  build_index = argv.index("--build")
23
25
  if build_index
@@ -65,6 +67,8 @@ module Shakapacker
65
67
  end
66
68
 
67
69
  def self.run_with_build_config(argv, build_config)
70
+ Shakapacker.ensure_node_env!
71
+
68
72
  # Apply build config environment variables
69
73
  build_config[:environment].each do |key, value|
70
74
  ENV[key] = value.to_s
@@ -391,7 +391,7 @@ module Shakapacker
391
391
  expected_binstubs = {
392
392
  "bin/shakapacker" => "Main Shakapacker binstub",
393
393
  "bin/shakapacker-dev-server" => "Development server binstub",
394
- "bin/export-bundler-config" => "Config export binstub"
394
+ "bin/shakapacker-config" => "Config export binstub"
395
395
  }
396
396
 
397
397
  expected_binstubs.each do |path, description|
@@ -914,7 +914,7 @@ module Shakapacker
914
914
  return unless doctor.config.config_path.exist?
915
915
 
916
916
  puts "\nConfiguration values for '#{doctor.config.env}' environment:"
917
- config_data = doctor.config.send(:data)
917
+ config_data = doctor.config.data
918
918
 
919
919
  if config_data.any?
920
920
  print_config_data(config_data)
@@ -1013,7 +1013,7 @@ module Shakapacker
1013
1013
  binstubs = [
1014
1014
  "bin/shakapacker",
1015
1015
  "bin/shakapacker-dev-server",
1016
- "bin/export-bundler-config"
1016
+ "bin/shakapacker-config"
1017
1017
  ]
1018
1018
 
1019
1019
  existing_binstubs = binstubs.select { |b| doctor.root_path.join(b).exist? }
@@ -1171,10 +1171,10 @@ module Shakapacker
1171
1171
  puts " #{package_manager_install_command(package_manager)}"
1172
1172
  puts ""
1173
1173
  puts "For debugging configuration issues, export your webpack/rspack config:"
1174
- puts " bin/export-bundler-config --doctor"
1174
+ puts " bin/shakapacker-config --doctor"
1175
1175
  puts " (Exports annotated YAML configs for dev and production - best for troubleshooting)"
1176
1176
  puts ""
1177
- puts " See 'bin/export-bundler-config --help' for more options"
1177
+ puts " See 'bin/shakapacker-config --help' for more options"
1178
1178
  end
1179
1179
 
1180
1180
  def package_manager_install_command(manager)