shakapacker 9.6.0.beta.0 → 9.6.0.rc.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: 16a87e8e339c085afc34f5f70af6fee7aa534590949f02faa1863d9c5e7f9f7d
4
- data.tar.gz: 19d1f859e00dc29bfe4c593ba1b4a1e5b64c6347fc1b56a2cbabcc8c8020205b
3
+ metadata.gz: a5240d4875989d1257b2c24656db7b29c5d052a9c99e22b9203565bf615aaf43
4
+ data.tar.gz: 855e3c61ff9169c2f4a34cc864d80f41299e48fd2530fb0def09e5bd9e07651f
5
5
  SHA512:
6
- metadata.gz: f4286284b7cbbec5324ce2ad26c6d6892cfcecae96e31c7324ff0a533f332301b4008e7083592b3b4b639be131758ab6fa5bc81ed46ac0903f2370f20a05fae0
7
- data.tar.gz: e4e6bfdd5837dabfd9f5d031e5a1d905ca88b6c1d50b1f3d440fb3f3c6a23d0abbec1d8839a769536af52d67eed5657ec602786c824dd57156f1678026ec300e
6
+ metadata.gz: 719593b91e0dd86f68db334fe533a76e05ebf7862e04162039b066182bc52642e4c200d0394e6f3cbfaea8a8600d5340d20d9a5cd54bd389c40e4d4ff8c1f055
7
+ data.tar.gz: 80f22641a2e9cba2ab9a8ccda6095133e636165185914b8ae1b6f7a85f1b860834913dc4a697db7d1785f001420e47aefad34584389c0221dc7eb7e0f9778957
@@ -3,31 +3,19 @@ name: Claude Code Review
3
3
  on:
4
4
  pull_request:
5
5
  types: [opened, synchronize, ready_for_review, reopened]
6
- # Optional: Only run on specific file changes
7
- # paths:
8
- # - "src/**/*.ts"
9
- # - "src/**/*.tsx"
10
- # - "src/**/*.js"
11
- # - "src/**/*.jsx"
12
6
 
13
7
  jobs:
14
8
  claude-review:
15
- # Optional: Filter by PR author
16
- # if: |
17
- # github.event.pull_request.user.login == 'external-contributor' ||
18
- # github.event.pull_request.user.login == 'new-developer' ||
19
- # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
20
-
9
+ if: github.event.pull_request.head.repo.full_name == github.repository
21
10
  runs-on: ubuntu-latest
22
11
  permissions:
23
12
  contents: read
24
- pull-requests: read
25
- issues: read
26
- id-token: write
13
+ pull-requests: write
14
+ issues: write
27
15
 
28
16
  steps:
29
17
  - name: Checkout repository
30
- uses: actions/checkout@v4
18
+ uses: actions/checkout@v6
31
19
  with:
32
20
  fetch-depth: 1
33
21
 
@@ -36,9 +24,21 @@ jobs:
36
24
  uses: anthropics/claude-code-action@v1
37
25
  with:
38
26
  claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
39
- plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
40
- plugins: 'code-review@claude-code-plugins'
41
- prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}'
42
- # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
43
- # or https://code.claude.com/docs/en/cli-reference for available options
27
+ prompt: |
28
+ REPO: ${{ github.repository }}
29
+ PR NUMBER: ${{ github.event.pull_request.number }}
30
+
31
+ Please review this pull request with a focus on:
32
+ - Code quality and best practices
33
+ - Potential bugs or issues
34
+ - Security implications
35
+ - Performance considerations
36
+
37
+ Note: The PR branch is already checked out in the current working directory.
38
+
39
+ Use `gh pr comment` for top-level feedback.
40
+ Use `mcp__github_inline_comment__create_inline_comment` to highlight specific code issues.
41
+ Only post GitHub comments - don't submit review text as messages.
44
42
 
43
+ claude_args: |
44
+ --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)"
@@ -24,6 +24,8 @@ jobs:
24
24
  issues: read
25
25
  id-token: write
26
26
  actions: read # Required for Claude to read CI results on PRs
27
+ checks: read
28
+ statuses: read
27
29
  steps:
28
30
  - name: Checkout repository
29
31
  uses: actions/checkout@v4
@@ -34,11 +36,14 @@ jobs:
34
36
  id: claude
35
37
  uses: anthropics/claude-code-action@v1
36
38
  with:
39
+ github_token: ${{ github.token }}
37
40
  claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
38
41
 
39
42
  # This is an optional setting that allows Claude to read CI results on PRs
40
43
  additional_permissions: |
41
44
  actions: read
45
+ checks: read
46
+ statuses: read
42
47
 
43
48
  # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
44
49
  # prompt: 'Update the pull request description to include a summary of changes.'
data/CHANGELOG.md CHANGED
@@ -11,8 +11,31 @@
11
11
 
12
12
  Changes since the last non-beta release.
13
13
 
14
+ ### Security
15
+
16
+ - Removed default `Access-Control-Allow-Origin: *` header from dev server configuration. This header allowed any website to access dev server resources. **If your setup runs webpack-dev-server on a different port from your Rails server, uncomment the `headers` section in `config/shakapacker.yml` to restore cross-origin asset loading.** [PR #936](https://github.com/shakacode/shakapacker/pull/936) by [justin808](https://github.com/justin808). Fixes [#935](https://github.com/shakacode/shakapacker/issues/935).
17
+
18
+ ### Added
19
+
20
+ - **Added `SKIP=true` installer mode to preserve existing files**. [PR #926](https://github.com/shakacode/shakapacker/pull/926) by [justin808](https://github.com/justin808). Running `rails shakapacker:install SKIP=true` now skips conflicting files instead of overwriting them. This is useful for CI/CD pipelines and automated setups where you want to install only missing files without touching existing configuration.
21
+ - **Export bundler utility functions for Webpack/Rspack compatibility**. [PR #922](https://github.com/shakacode/shakapacker/pull/922) by [justin808](https://github.com/justin808). New utility functions that make it easier to write bundler-agnostic configuration code: `isRspack`, `isWebpack`, `getBundler()`, `getCssExtractPlugin()`, `getCssExtractPluginLoader()`, `getDefinePlugin()`, `getEnvironmentPlugin()`, and `getProvidePlugin()`. Users no longer need to write conditional logic to handle differences between Webpack and Rspack.
22
+
23
+ ```javascript
24
+ // Before: manual conditional logic
25
+ const { config } = require("shakapacker")
26
+ const CssPlugin =
27
+ config.assets_bundler === "rspack"
28
+ ? require("@rspack/core").CssExtractRspackPlugin
29
+ : require("mini-css-extract-plugin")
30
+
31
+ // After: use bundler utilities
32
+ const { getCssExtractPlugin } = require("shakapacker")
33
+ const CssPlugin = getCssExtractPlugin()
34
+ ```
35
+
14
36
  ### Changed
15
37
 
38
+ - Allow `compression-webpack-plugin` v12. [PR #937](https://github.com/shakacode/shakapacker/pull/937) by [G-Rath](https://github.com/G-Rath).
16
39
  - **BREAKING: sass-loader now defaults to modern Sass API**. [PR #879](https://github.com/shakacode/shakapacker/pull/879) by [justin808](https://github.com/justin808). The sass-loader configuration now uses `api: "modern"` instead of the deprecated legacy API. This improves compatibility with plugins like sass-resources-loader that require the modern API. If you experience issues after upgrading, you can revert to the legacy API by customizing your webpack config:
17
40
 
18
41
  ```javascript
@@ -36,9 +59,16 @@ Changes since the last non-beta release.
36
59
 
37
60
  ### Fixed
38
61
 
62
+ - **Fixed hidden dotfiles and dot-directories being treated as entrypoints**. [PR #915](https://github.com/shakacode/shakapacker/pull/915) by [justin808](https://github.com/justin808). Entry discovery now ignores files and directories whose names start with `.` when traversing `source_entry_path`, preventing unintended bundles from being created. Closes [#853](https://github.com/shakacode/shakapacker/issues/853).
39
63
  - **Fixed orphaned webpack/rspack processes when foreman receives SIGTERM**. [PR #888](https://github.com/shakacode/shakapacker/pull/888) by [jordan-brough](https://github.com/jordan-brough). When running under foreman, sending SIGTERM to foreman (e.g. `kill <pid>`) would kill the Ruby shakapacker process but leave the webpack/rspack child process running as an orphan. DevServerRunner now uses `exec` to replace the Ruby process entirely, and Runner uses `spawn` with SIGTERM forwarding to ensure the child process is properly terminated.
64
+ - **Fixed missing-environment fallback to use production instead of development**. [PR #894](https://github.com/shakacode/shakapacker/pull/894) by [justin808](https://github.com/justin808). When a Rails environment (e.g., staging) is not defined in `shakapacker.yml`, Shakapacker now falls back to the `production` configuration instead of `development`. This ensures unknown environments get production-optimized webpack/rspack builds by default.
40
65
  - **Fixed installer writing wrong shakapacker version in package.json**. [PR #899](https://github.com/shakacode/shakapacker/pull/899) by [justin808](https://github.com/justin808). The `shakapacker:install` generator now keeps the `package.json` dependency value in sync with the exact version or path that was requested, instead of relying on the post-install value which could differ.
66
+ - **Fixed installer not updating `shakapacker.yml` when selecting a non-default transpiler**. [PR #895](https://github.com/shakacode/shakapacker/pull/895) by [codex-rs](https://github.com/apps/codex-rs). Installing with `JAVASCRIPT_TRANSPILER=babel` (or `esbuild`) now correctly updates `config/shakapacker.yml` to match the selected transpiler instead of leaving it set to `swc`. Previously, a quote mismatch in the `gsub_file` call meant the config was never actually updated, and the condition also excluded `JAVASCRIPT_TRANSPILER=babel` from the update entirely. Additionally, `JAVASCRIPT_TRANSPILER=babel` no longer installs SWC packages.
67
+ - **Fixed ENOENT crash on clean builds when using `webpack-assets-manifest` v6 with `merge: true`**. [PR #931](https://github.com/shakacode/shakapacker/pull/931) by [justin808](https://github.com/justin808). Seeds an empty `{}` manifest file before instantiating the plugin, so the merge read succeeds on first build rather than throwing an unhandled ENOENT.
68
+ - **Improved error message when manifest is empty or missing**. [PR #872](https://github.com/shakacode/shakapacker/pull/872) by [justin808](https://github.com/justin808). When the bundler is still compiling (empty manifest) or hasn't run yet (missing manifest file), users now see clear, actionable error messages instead of the generic 7-point checklist.
41
69
  - **Fixed NODE_ENV=test causing DefinePlugin warnings**. [PR #870](https://github.com/shakacode/shakapacker/pull/870) by [justin808](https://github.com/justin808). When RAILS_ENV=test, Shakapacker now sets NODE_ENV=development instead of NODE_ENV=test. This prevents webpack/rspack DefinePlugin conflicts since these bundlers only recognize "development" and "production" as valid NODE_ENV values.
70
+ - **Fixed `--json` flag output being corrupted by log messages**. [PR #869](https://github.com/shakacode/shakapacker/pull/869) by [justin808](https://github.com/justin808). When `--json` is in the command arguments, `[Shakapacker]` log messages are now written to stderr instead of stdout, keeping stdout clean for valid JSON output. This allows `bin/shakapacker --profile --json` to be piped to tools like `webpack-bundle-analyzer`. Normal (non-JSON) usage is unchanged. Resolves [#868](https://github.com/shakacode/shakapacker/issues/868).
71
+ - **Require explicit truthy values for `SKIP` and `FORCE` installer env vars**. [PR #926](https://github.com/shakacode/shakapacker/pull/926) by [justin808](https://github.com/justin808). Previously, any set value (including `"false"` or `"0"`) would activate skip/force mode. Now only explicit truthy values (`true`, `1`, `yes`, case-insensitive) are recognized. This behavior change may require CI/scripts that relied on `FORCE=<any non-empty value>` to switch to `FORCE=true`.
42
72
 
43
73
  ### Documentation
44
74
 
data/CLAUDE.md CHANGED
@@ -40,6 +40,14 @@
40
40
  - **Version management**: Run `bundle exec rake update_changelog` after releases to update version headers
41
41
  - **Examples**: Run `grep -A 3 "^### " CHANGELOG.md | head -30` to see real formatting examples
42
42
 
43
+ ## Open Source Maintainability
44
+
45
+ - **Prefer removing complexity over adding configuration.** If a default causes problems, consider removing the default rather than adding an option to disable it.
46
+ - **Every config option is maintenance surface.** Prefer convention over configuration. Don't add options for <10% of users — let them customize via existing extension points (e.g., custom webpack config).
47
+ - **"No is temporary, yes is forever."** Adding a feature creates a permanent maintenance obligation. Reject features that solve one user's niche problem but add complexity for everyone.
48
+ - **Security-safe defaults over convenient defaults.** Don't ship permissive defaults (e.g., `Access-Control-Allow-Origin: *`) just for convenience — make users opt in to less-secure configurations.
49
+ - **Don't refactor adjacent code in feature PRs.** Keep PRs focused. If you spot something to clean up, do it in a separate PR.
50
+
43
51
  ## Shakapacker-Specific
44
52
 
45
53
  - This gem supports both webpack and rspack configurations
data/README.md CHANGED
@@ -166,7 +166,7 @@ Then run the following to install Shakapacker:
166
166
  bundle exec rake shakapacker:install
167
167
  ```
168
168
 
169
- Before initiating the installation process, ensure you have committed all the changes. While installing Shakapacker, there might be some conflict between the existing file content and what Shakapacker tries to copy. You can either approve all the prompts for overriding these files or use the `FORCE=true` environment variable before the installation command to force the override without any prompt.
169
+ Before initiating the installation process, ensure you have committed all changes. During install, Shakapacker may encounter conflicts with existing files. You can approve prompts interactively, use `FORCE=true` to overwrite without prompting, or use `SKIP=true` to preserve existing files and only create missing ones. Accepted truthy values are `true`, `1`, and `yes` (case-insensitive). If both are set, `FORCE` takes precedence.
170
170
 
171
171
  Shakapacker uses the [`package_json`](https://github.com/shakacode/package_json) gem to handle updating the `package.json` and interacting with the underlying package manager of choice for managing dependencies and running commands; the package manager is managed using the [`packageManager`](https://nodejs.org/api/packages.html#packagemanager) property in the `package.json`, otherwise falling back to the value of `PACKAGE_JSON_FALLBACK_MANAGER` if set or otherwise `npm`.
172
172
 
@@ -362,9 +362,11 @@ dev_server:
362
362
  # pathname: '/ws'
363
363
  # port: 8080
364
364
 
365
- # Headers for CORS
366
- headers:
367
- "Access-Control-Allow-Origin": "*"
365
+ # Custom headers for dev server responses
366
+ # Uncomment to enable CORS (e.g., when webpack-dev-server runs on a different
367
+ # port than your Rails server and the browser blocks cross-origin asset requests):
368
+ # headers:
369
+ # "Access-Control-Allow-Origin": "*"
368
370
 
369
371
  # Static file serving
370
372
  static:
@@ -35,13 +35,13 @@ before launching long-running processes instead of using `precompile_hook`.
35
35
 
36
36
  ### Comparison
37
37
 
38
- | Aspect | `precompile_hook` | Explicit setup in `bin/dev` |
39
- |--------|-------------------|-------------------------------|
40
- | Best for | Default/consistent pre-build tasks | Custom multi-step dev boot flows |
41
- | Runs when | Immediately before compilation starts | Wherever you place it in startup script |
42
- | Production integration | Automatic via `assets:precompile` | Requires explicit production wiring |
43
- | Process manager complexity | Lower | Higher (you own orchestration) |
44
- | Debugging | Centralized hook command | Fully explicit command-by-command flow |
38
+ | Aspect | `precompile_hook` | Explicit setup in `bin/dev` |
39
+ | -------------------------- | ------------------------------------- | --------------------------------------- |
40
+ | Best for | Default/consistent pre-build tasks | Custom multi-step dev boot flows |
41
+ | Runs when | Immediately before compilation starts | Wherever you place it in startup script |
42
+ | Production integration | Automatic via `assets:precompile` | Requires explicit production wiring |
43
+ | Process manager complexity | Lower | Higher (you own orchestration) |
44
+ | Debugging | Centralized hook command | Fully explicit command-by-command flow |
45
45
 
46
46
  ### `shakapacker_precompile` Interaction
47
47
 
data/eslint.config.js CHANGED
@@ -233,6 +233,7 @@ module.exports = [
233
233
  // Remaining utils files that need type safety improvements
234
234
  // These use dynamic requires and helper functions that return `any`
235
235
  files: [
236
+ "package/utils/bundlerUtils.ts",
236
237
  "package/utils/inliningCss.ts",
237
238
  "package/utils/errorCodes.ts",
238
239
  "package/utils/errorHelpers.ts",
@@ -1,6 +1,10 @@
1
- force_option = ENV["FORCE"] ? { force: true } : {}
1
+ require "shakapacker/install/env"
2
+
3
+ # template.rb sets @conflict_option first during normal install flow.
4
+ # ||= keeps binstubs runnable standalone (e.g., rake shakapacker:binstubs).
5
+ @conflict_option ||= Shakapacker::Install::Env.conflict_option
2
6
 
3
7
  say "Copying binstubs"
4
- directory "#{__dir__}/bin", "bin", force_option
8
+ directory "#{__dir__}/bin", "bin", @conflict_option
5
9
 
6
10
  chmod "bin", 0755 & ~File.umask, verbose: false
@@ -161,8 +161,10 @@ development:
161
161
  allowed_hosts: "auto"
162
162
  # Shows progress and colorizes output of bin/shakapacker[-dev-server]
163
163
  pretty: true
164
- headers:
165
- "Access-Control-Allow-Origin": "*"
164
+ # Uncomment to enable CORS (e.g., when webpack-dev-server runs on a different
165
+ # port than your Rails server and the browser blocks cross-origin asset requests):
166
+ # headers:
167
+ # "Access-Control-Allow-Origin": "*"
166
168
  static:
167
169
  watch:
168
170
  ignored: "**/node_modules/**"
@@ -1,13 +1,14 @@
1
1
  require "shakapacker/utils/misc"
2
2
  require "shakapacker/utils/manager"
3
3
  require "shakapacker/utils/version_syntax_converter"
4
+ require "shakapacker/install/env"
4
5
  require "package_json"
5
6
  require "yaml"
6
7
  require "json"
7
8
 
8
9
  # Install Shakapacker
9
10
 
10
- force_option = ENV["FORCE"] ? { force: true } : {}
11
+ @conflict_option = Shakapacker::Install::Env.conflict_option
11
12
 
12
13
  # Initialize variables for use throughout the template
13
14
  # Using instance variable to avoid method definition issues in Rails templates
@@ -19,25 +20,33 @@ install_dir = File.expand_path(File.dirname(__FILE__))
19
20
  # - Otherwise install only the specified transpiler
20
21
  if ENV["USE_BABEL_PACKAGES"] == "true" || ENV["USE_BABEL_PACKAGES"] == "1"
21
22
  @transpiler_to_install = "babel"
23
+ @install_swc_compat_packages = true
22
24
  say "📦 Installing Babel packages (USE_BABEL_PACKAGES is set)", :yellow
23
25
  say "✨ Also installing SWC packages for default config compatibility", :green
24
26
  elsif ENV["JAVASCRIPT_TRANSPILER"]
25
27
  @transpiler_to_install = ENV["JAVASCRIPT_TRANSPILER"]
28
+ @install_swc_compat_packages = false
26
29
  say "📦 Installing #{@transpiler_to_install} packages", :blue
27
30
  else
28
31
  # Default to swc (matches the default in shakapacker.yml)
29
32
  @transpiler_to_install = "swc"
33
+ @install_swc_compat_packages = false
30
34
  say "✨ Installing SWC packages (20x faster than Babel)", :green
31
35
  end
32
36
 
33
37
  # Copy config file
34
- copy_file "#{install_dir}/config/shakapacker.yml", "config/shakapacker.yml", force_option
35
-
36
- # Update config if USE_BABEL_PACKAGES is set to ensure babel is used at runtime
37
- if @transpiler_to_install == "babel" && !ENV["JAVASCRIPT_TRANSPILER"]
38
- # When USE_BABEL_PACKAGES is set, update the config to use babel
39
- gsub_file "config/shakapacker.yml", "javascript_transpiler: 'swc'", "javascript_transpiler: 'babel'"
40
- say " 📝 Updated config/shakapacker.yml to use Babel transpiler", :green
38
+ shakapacker_config_preexisting = Rails.root.join("config/shakapacker.yml").exist?
39
+ copy_file "#{install_dir}/config/shakapacker.yml", "config/shakapacker.yml", @conflict_option
40
+
41
+ # Update config to match the selected transpiler
42
+ # Skip modification only when SKIP mode preserved a pre-existing user file
43
+ if Shakapacker::Install::Env.update_transpiler_config?(
44
+ transpiler_to_install: @transpiler_to_install,
45
+ conflict_option: @conflict_option,
46
+ config_preexisting: shakapacker_config_preexisting
47
+ )
48
+ gsub_file "config/shakapacker.yml", 'javascript_transpiler: "swc"', "javascript_transpiler: \"#{@transpiler_to_install}\""
49
+ say " 📝 Updated config/shakapacker.yml to use #{@transpiler_to_install} transpiler", :green
41
50
  end
42
51
 
43
52
  # Detect TypeScript usage
@@ -53,7 +62,7 @@ source_config = "#{install_dir}/config/#{assets_bundler}/#{config_file}"
53
62
  dest_config = "config/#{assets_bundler}/#{config_file}"
54
63
 
55
64
  empty_directory "config/#{assets_bundler}"
56
- copy_file source_config, dest_config, force_option
65
+ copy_file source_config, dest_config, @conflict_option
57
66
 
58
67
  if @use_typescript
59
68
  say " ✨ Using TypeScript config for enhanced type safety", :green
@@ -223,22 +232,21 @@ Dir.chdir(Rails.root) do
223
232
  # Inline the logic here since methods can't be called before they're defined in Rails templates
224
233
 
225
234
  # Install transpiler-specific dependencies
226
- # When USE_BABEL_PACKAGES is set, install both babel AND swc
227
- # This ensures backward compatibility while supporting the default config
228
235
  if @transpiler_to_install == "babel"
229
236
  # Install babel packages
230
237
  babel_deps = PackageJson.read(install_dir).fetch("babel")
231
238
  peers = peers.merge(babel_deps)
232
239
 
233
- # Also install SWC since that's what the default config uses
234
- # This ensures the runtime works regardless of config
235
- swc_deps = PackageJson.read(install_dir).fetch("swc")
236
- peers = peers.merge(swc_deps)
240
+ # Also install SWC only when USE_BABEL_PACKAGES requested compatibility mode.
241
+ if @install_swc_compat_packages
242
+ swc_deps = PackageJson.read(install_dir).fetch("swc")
243
+ peers = peers.merge(swc_deps)
237
244
 
238
- say "ℹ️ Installing both Babel and SWC packages for compatibility:", :blue
239
- say " - Babel packages are installed as requested via USE_BABEL_PACKAGES", :blue
240
- say " - SWC packages are also installed to ensure the default config works", :blue
241
- say " - Your actual transpiler will be determined by your shakapacker.yml configuration", :blue
245
+ say "ℹ️ Installing both Babel and SWC packages for compatibility:", :blue
246
+ say " - Babel packages are installed as requested via USE_BABEL_PACKAGES", :blue
247
+ say " - SWC packages are also installed to ensure the default config works", :blue
248
+ say " - Your actual transpiler will be determined by your shakapacker.yml configuration", :blue
249
+ end
242
250
  elsif @transpiler_to_install == "swc"
243
251
  swc_deps = PackageJson.read(install_dir).fetch("swc")
244
252
  peers = peers.merge(swc_deps)
@@ -1,4 +1,6 @@
1
1
  class Shakapacker::Env
2
+ FALLBACK_ENV = "production".freeze
3
+
2
4
  delegate :config_path, :logger, to: :@instance
3
5
 
4
6
  def self.inquire(instance)
@@ -11,7 +13,7 @@ class Shakapacker::Env
11
13
 
12
14
  def inquire
13
15
  fallback_env_warning if config_path.exist? && !current
14
- current || Shakapacker::DEFAULT_ENV.inquiry
16
+ current || FALLBACK_ENV.inquiry
15
17
  end
16
18
 
17
19
  private
@@ -20,7 +22,7 @@ class Shakapacker::Env
20
22
  end
21
23
 
22
24
  def fallback_env_warning
23
- logger.info "RAILS_ENV=#{Rails.env} environment is not defined in #{config_path}, falling back to #{Shakapacker::DEFAULT_ENV} environment"
25
+ logger.info "RAILS_ENV=#{Rails.env} environment is not defined in #{config_path}, falling back to #{FALLBACK_ENV} environment"
24
26
  end
25
27
 
26
28
  def available_environments
@@ -0,0 +1,33 @@
1
+ module Shakapacker
2
+ module Install
3
+ module Env
4
+ TRUTHY_VALUES = %w[true 1 yes].freeze
5
+
6
+ module_function
7
+
8
+ def truthy_env?(name)
9
+ TRUTHY_VALUES.include?(ENV[name].to_s.downcase)
10
+ end
11
+
12
+ def conflict_option
13
+ if truthy_env?("FORCE")
14
+ { force: true }
15
+ elsif truthy_env?("SKIP")
16
+ { skip: true }
17
+ else
18
+ {}
19
+ end
20
+ end
21
+
22
+ # Preserve existing shakapacker.yml when SKIP mode is active, but still
23
+ # update newly-copied files on fresh installs.
24
+ def update_transpiler_config?(transpiler_to_install:, conflict_option:, config_preexisting:)
25
+ return false if transpiler_to_install == "swc"
26
+ return true if conflict_option[:force]
27
+ return true unless conflict_option[:skip]
28
+
29
+ !config_preexisting
30
+ end
31
+ end
32
+ end
33
+ end
@@ -33,6 +33,10 @@ class Shakapacker::Manifest
33
33
  # Raised when an asset cannot be found in the manifest
34
34
  class MissingEntryError < StandardError; end
35
35
 
36
+ # Holds the result of loading the manifest file, keeping data and
37
+ # file-existence state in a single atomic value.
38
+ LoadResult = Struct.new(:data, :manifest_existed, keyword_init: true)
39
+
36
40
  delegate :config, :compiler, :dev_server, to: :@instance
37
41
 
38
42
  # Creates a new manifest instance
@@ -50,7 +54,8 @@ class Shakapacker::Manifest
50
54
  #
51
55
  # @return [Hash] the loaded manifest data
52
56
  def refresh
53
- @data = load
57
+ @load_result = load
58
+ @load_result.data
54
59
  end
55
60
 
56
61
  # Looks up an entry point with all its chunks (split code)
@@ -132,14 +137,19 @@ class Shakapacker::Manifest
132
137
  Shakapacker.logger.tagged("Shakapacker") { compiler.compile }
133
138
  end
134
139
 
135
- def data
140
+ def load_result
136
141
  if config.cache_manifest?
137
- @data ||= load
142
+ @load_result ||= load
138
143
  else
139
144
  refresh
145
+ @load_result
140
146
  end
141
147
  end
142
148
 
149
+ def data
150
+ load_result.data
151
+ end
152
+
143
153
  def find(name)
144
154
  return nil unless data[name.to_s].present?
145
155
 
@@ -155,14 +165,27 @@ class Shakapacker::Manifest
155
165
  end
156
166
 
157
167
  def handle_missing_entry(name, pack_type)
158
- raise Shakapacker::Manifest::MissingEntryError, missing_file_from_manifest_error(full_pack_name(name, pack_type[:type]))
168
+ # @load_result is always set by the preceding data call (even in
169
+ # non-cached mode), so we read it directly to avoid a second file read.
170
+ result = @load_result
171
+
172
+ # An empty data hash covers both a 0-byte / whitespace-only manifest file
173
+ # and a missing manifest file. Either way the bundler has not produced
174
+ # usable output yet, so we show a targeted "unavailable" message.
175
+ if result.data.empty?
176
+ raise Shakapacker::Manifest::MissingEntryError, manifest_unavailable_error(result)
177
+ end
178
+
179
+ raise Shakapacker::Manifest::MissingEntryError, missing_file_from_manifest_error(full_pack_name(name, pack_type[:type]), result.data)
159
180
  end
160
181
 
161
182
  def load
162
183
  if config.manifest_path.exist?
163
- JSON.parse config.manifest_path.read
184
+ contents = config.manifest_path.read
185
+ parsed = contents.strip.empty? ? {} : JSON.parse(contents)
186
+ LoadResult.new(data: parsed, manifest_existed: true)
164
187
  else
165
- {}
188
+ LoadResult.new(data: {}, manifest_existed: false)
166
189
  end
167
190
  end
168
191
 
@@ -181,21 +204,59 @@ class Shakapacker::Manifest
181
204
  end
182
205
  end
183
206
 
184
- def missing_file_from_manifest_error(bundle_name)
207
+ def missing_file_from_manifest_error(bundle_name, manifest_data)
185
208
  bundler_name = config.assets_bundler
186
- <<-MSG
187
- Shakapacker can't find #{bundle_name} in #{config.manifest_path}. Possible causes:
188
- 1. You forgot to install javascript packages or are running an incompatible javascript runtime version
189
- 2. Your app has code with a non-standard extension (like a `.jsx` file) but the extension is not in the `extensions` config in `config/shakapacker.yml`
190
- 3. You have set compile: false (see `config/shakapacker.yml`) for this environment
191
- (unless you are using the `bin/shakapacker -w` or the `bin/shakapacker-dev-server`, in which case maybe you aren't running the dev server in the background?)
192
- 4. Your #{bundler_name} has not yet FINISHED running to reflect updates.
193
- 5. You have misconfigured Shakapacker's `config/shakapacker.yml` file.
194
- 6. Your #{bundler_name} configuration is not creating a manifest with the expected structure.
195
- 7. Ensure the 'assets_bundler' in config/shakapacker.yml is set correctly (currently: #{bundler_name}).
196
-
197
- Your manifest contains:
198
- #{JSON.pretty_generate(@data)}
209
+ <<~MSG
210
+ Shakapacker can't find #{bundle_name} in #{config.manifest_path}. Possible causes:
211
+ 1. You forgot to install javascript packages or are running an incompatible javascript runtime version
212
+ 2. Your app has code with a non-standard extension (like a `.jsx` file) but the extension is not in the `extensions` config in `config/shakapacker.yml`
213
+ 3. You have set compile: false (see `config/shakapacker.yml`) for this environment
214
+ (unless you are using the `bin/shakapacker -w` or the `bin/shakapacker-dev-server`, in which case maybe you aren't running the dev server in the background?)
215
+ 4. Your #{bundler_name} has not yet FINISHED running to reflect updates.
216
+ 5. You have misconfigured Shakapacker's `config/shakapacker.yml` file.
217
+ 6. Your #{bundler_name} configuration is not creating a manifest with the expected structure.
218
+ 7. Ensure the 'assets_bundler' in config/shakapacker.yml is set correctly (currently: #{bundler_name}).
219
+
220
+ Your manifest contains:
221
+ #{JSON.pretty_generate(manifest_data)}
199
222
  MSG
200
223
  end
224
+
225
+ def manifest_unavailable_error(result)
226
+ bundler_name = config.assets_bundler
227
+
228
+ if result.manifest_existed
229
+ <<~MSG
230
+ Shakapacker manifest is empty. #{bundler_name} is likely still compiling.
231
+
232
+ This typically happens when:
233
+ 1. You just started the dev server and it's still compiling
234
+ 2. The dev server crashed during startup
235
+ 3. #{bundler_name} compilation hasn't completed yet
236
+
237
+ What to do:
238
+ - Wait a few seconds and refresh the page
239
+ - Check your terminal for #{bundler_name} build progress
240
+ - Look for errors in the #{bundler_name} output
241
+
242
+ Manifest path: #{config.manifest_path}
243
+ MSG
244
+ else
245
+ <<~MSG
246
+ Shakapacker manifest file not found. #{bundler_name} has not yet built assets.
247
+
248
+ This typically happens when:
249
+ 1. You haven't started the dev server yet
250
+ 2. The compile process hasn't created the manifest file
251
+ 3. The manifest_path configuration is incorrect
252
+
253
+ What to do:
254
+ - Start the dev server: bin/shakapacker-dev-server
255
+ - Or run a manual build: bin/shakapacker
256
+ - Verify manifest_path in config/shakapacker.yml
257
+
258
+ Expected manifest path: #{config.manifest_path}
259
+ MSG
260
+ end
261
+ end
201
262
  end