shakapacker 9.3.0.beta.2 → 9.3.0.beta.4
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 +4 -4
- data/ESLINT_TECHNICAL_DEBT.md +13 -14
- data/Gemfile.lock +1 -1
- data/README.md +9 -6
- data/docs/configuration.md +101 -0
- data/docs/preventing_fouc.md +132 -0
- data/docs/troubleshooting.md +4 -0
- data/eslint.config.js +2 -6
- data/lib/shakapacker/build_config_loader.rb +147 -0
- data/lib/shakapacker/configuration.rb +6 -2
- data/lib/shakapacker/dev_server_runner.rb +73 -0
- data/lib/shakapacker/runner.rb +239 -14
- data/lib/shakapacker/version.rb +1 -1
- data/package/configExporter/buildValidator.ts +2 -2
- data/package/configExporter/cli.ts +251 -160
- data/package/configExporter/configFile.ts +182 -46
- data/package/configExporter/fileWriter.ts +8 -1
- data/package/configExporter/types.ts +2 -0
- data/package/utils/pathValidation.ts +3 -3
- data/package-lock.json +2 -2
- data/package.json +1 -1
- data/test/configExporter/configFile.test.js +3 -2
- data/test/configExporter/integration.test.js +14 -27
- metadata +6 -4
- /data/bin/{export-bundler-config → shakapacker-config} +0 -0
- /data/lib/install/bin/{export-bundler-config → shakapacker-config} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2f2fafddfa10d364b8e62946786c16d9206ecffadf82b719e3ffeb2100608da0
|
4
|
+
data.tar.gz: 0e0cdc08d53e64e8066d7232aba1022b6a7708067d56ff7ba738803bfbc49a78
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dcbb4604f956d78acaea611bb760ecea97a1afcc15bc390b9c5a64c5d9654f5d08e83c57ed24da55aca0a1a98da074a0047a754cadf5f74ee8ba832fb6e93b3e
|
7
|
+
data.tar.gz: 47b7d7887f770f5712b5f020afd260fcefcb4b09a0c707e1b2bc921c38d2944bf6045b6b9752295790806018d98cd95d43313f9079719bcb3c808ff9f6e6c2ad
|
data/ESLINT_TECHNICAL_DEBT.md
CHANGED
@@ -6,7 +6,7 @@ This document tracks the ESLint errors currently suppressed in the codebase and
|
|
6
6
|
|
7
7
|
**As of 2025-10-14**: All TypeScript files in `package/` directory are temporarily excluded from linting via the ignore pattern `package/**/*.ts` in `eslint.config.js`. This allows the project to adopt ESLint configuration without requiring immediate fixes to all existing issues.
|
8
8
|
|
9
|
-
**Latest Update**: Fixed all `
|
9
|
+
**Latest Update**: Fixed all `no-param-reassign` violations by refactoring to create new objects instead of mutating parameters (7 violations resolved).
|
10
10
|
|
11
11
|
## Current Linting Status
|
12
12
|
|
@@ -19,12 +19,12 @@ This document tracks the ESLint errors currently suppressed in the codebase and
|
|
19
19
|
|
20
20
|
**TypeScript files** (currently ignored via `package/**/*.ts`):
|
21
21
|
|
22
|
-
- **Estimated suppressed errors: ~
|
23
|
-
- TypeScript type-safety issues: ~114 (
|
24
|
-
- Style/convention issues: ~
|
22
|
+
- **Estimated suppressed errors: ~163** (from sample analysis)
|
23
|
+
- TypeScript type-safety issues: ~114 (70%)
|
24
|
+
- Style/convention issues: ~49 (30%)
|
25
25
|
|
26
26
|
**Target**: Reduce suppressed errors by 50% within Q1 2025
|
27
|
-
**Last Updated**: 2025-10-
|
27
|
+
**Last Updated**: 2025-10-15
|
28
28
|
|
29
29
|
## Priority Matrix
|
30
30
|
|
@@ -33,9 +33,9 @@ This document tracks the ESLint errors currently suppressed in the codebase and
|
|
33
33
|
| `@typescript-eslint/no-explicit-any` | High | High | P1 | 22 |
|
34
34
|
| `@typescript-eslint/no-unsafe-*` | High | High | P1 | 85 |
|
35
35
|
| `config.ts` type safety | High | Medium | P1 | 7 |
|
36
|
-
| `no-param-reassign` | Medium | Low | P2 |
|
36
|
+
| `no-param-reassign` | Medium | Low | P2 | 0 |
|
37
37
|
| `class-methods-use-this` | Low | Low | P3 | 0 |
|
38
|
-
| `no-nested-ternary` | Low | Low | P3 |
|
38
|
+
| `no-nested-ternary` | Low | Low | P3 | 0 |
|
39
39
|
| `import/prefer-default-export` | Low | Medium | P3 | 9 |
|
40
40
|
| `global-require` | Medium | High | P2 | 3 |
|
41
41
|
| Other style issues | Low | Low | P3 | 31 |
|
@@ -80,15 +80,13 @@ This document tracks the ESLint errors currently suppressed in the codebase and
|
|
80
80
|
|
81
81
|
✅ **FIXED** - All FileWriter methods that didn't use instance state have been converted to static methods
|
82
82
|
|
83
|
-
#### `no-nested-ternary` (
|
83
|
+
#### `no-nested-ternary` (0 instances)
|
84
84
|
|
85
|
-
**
|
85
|
+
✅ **FIXED** - All nested ternary expressions have been refactored to if-else statements for better readability
|
86
86
|
|
87
|
-
#### `no-param-reassign` (
|
87
|
+
#### `no-param-reassign` (0 instances)
|
88
88
|
|
89
|
-
**
|
90
|
-
**Why suppressed:** Common pattern for option objects
|
91
|
-
**Fix strategy:** Create new objects instead of mutating parameters
|
89
|
+
✅ **FIXED** - Refactored `applyDefaults` function to return new objects instead of mutating parameters
|
92
90
|
|
93
91
|
#### `no-underscore-dangle` (2 instances)
|
94
92
|
|
@@ -115,10 +113,11 @@ This document tracks the ESLint errors currently suppressed in the codebase and
|
|
115
113
|
- Added proper type annotations for `requireOrError` calls
|
116
114
|
- Configured appropriate global rule disables (`no-console`, `no-restricted-syntax`)
|
117
115
|
- ✅ **Fixed `class-methods-use-this`** - Converted FileWriter methods to static methods
|
116
|
+
- ✅ **Fixed `no-nested-ternary`** - Refactored to if-else statements for better readability
|
117
|
+
- ✅ **Fixed `no-param-reassign`** - Refactored `applyDefaults` to return new objects instead of mutating parameters
|
118
118
|
|
119
119
|
🔧 Could still fix (low risk):
|
120
120
|
|
121
|
-
- `no-nested-ternary` - Refactor conditionals
|
122
121
|
- `no-useless-escape` - Remove unnecessary escapes
|
123
122
|
- Unused variables - Remove or prefix with underscore
|
124
123
|
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -477,8 +477,8 @@ And the main layout has:
|
|
477
477
|
is the same as using this in the main layout:
|
478
478
|
|
479
479
|
```erb
|
480
|
-
<%= javascript_pack_tag 'calendar', 'map', application' %>
|
481
|
-
<%= stylesheet_pack_tag 'calendar', 'map', application' %>
|
480
|
+
<%= javascript_pack_tag 'calendar', 'map', 'application' %>
|
481
|
+
<%= stylesheet_pack_tag 'calendar', 'map', 'application' %>
|
482
482
|
```
|
483
483
|
|
484
484
|
However, you typically can't do that in the main layout, as the view and partial codes will depend on the route.
|
@@ -490,12 +490,13 @@ Thus, you can distribute the logic of what packs are needed for any route. All t
|
|
490
490
|
The typical issue is that your layout might reference some partials that need to configure packs. A good way to solve this problem is to use `content_for` to ensure that the code to render your partial comes before the call to `javascript_pack_tag`.
|
491
491
|
|
492
492
|
```erb
|
493
|
-
<% content_for :footer do
|
494
|
-
|
493
|
+
<% content_for :footer do %>
|
494
|
+
<%= render 'shared/footer' %>
|
495
|
+
<% end %>
|
495
496
|
|
496
497
|
<%= javascript_pack_tag %>
|
497
498
|
|
498
|
-
<%=
|
499
|
+
<%= yield :footer %>
|
499
500
|
```
|
500
501
|
|
501
502
|
There is also `prepend_javascript_pack_tag` that will put the entry at the front of the queue. This is handy when you want an entry in the main layout to go before the partial and main layout `append_javascript_pack_tag` entries.
|
@@ -522,11 +523,13 @@ And the main layout has:
|
|
522
523
|
is the same as using this in the main layout:
|
523
524
|
|
524
525
|
```erb
|
525
|
-
<%= javascript_pack_tag 'main', 'calendar', 'map', application' %>
|
526
|
+
<%= javascript_pack_tag 'main', 'calendar', 'map', 'application' %>
|
526
527
|
```
|
527
528
|
|
528
529
|
For alternative options for setting the additional packs, [see this discussion](https://github.com/shakacode/shakapacker/issues/39).
|
529
530
|
|
531
|
+
**Important:** To prevent FOUC (Flash of Unstyled Content), always place `stylesheet_pack_tag` in the `<head>` section of your layout. When using `append_*` helpers with dynamic pack loading (e.g., React on Rails), use the `content_for` pattern to control execution order. See the [Preventing FOUC guide](./docs/preventing_fouc.md) for detailed examples.
|
532
|
+
|
530
533
|
#### View Helper: `asset_pack_path`
|
531
534
|
|
532
535
|
If you want to link a static asset for `<img />` tag, you can use the `asset_pack_path` helper:
|
data/docs/configuration.md
CHANGED
@@ -12,6 +12,7 @@ This guide covers all configuration options available in `config/shakapacker.yml
|
|
12
12
|
- [Compilation Options](#compilation-options)
|
13
13
|
- [Advanced Options](#advanced-options)
|
14
14
|
- [Environment-Specific Configuration](#environment-specific-configuration)
|
15
|
+
- [Build Configurations (config/shakapacker-builds.yml)](#build-configurations-configshakapacker-buildsyml)
|
15
16
|
|
16
17
|
## Basic Configuration
|
17
18
|
|
@@ -608,6 +609,106 @@ default: &default
|
|
608
609
|
- packages/ui-components
|
609
610
|
```
|
610
611
|
|
612
|
+
## Build Configurations (config/shakapacker-builds.yml)
|
613
|
+
|
614
|
+
Shakapacker supports defining reusable build configurations in `config/shakapacker-builds.yml`. This allows you to run predefined builds with a simple command, making it easy to switch between different build scenarios.
|
615
|
+
|
616
|
+
### Creating a Build Configuration File
|
617
|
+
|
618
|
+
Generate `config/shakapacker-builds.yml` with example builds:
|
619
|
+
|
620
|
+
```bash
|
621
|
+
bin/shakapacker --init # Creates config/shakapacker-builds.yml
|
622
|
+
```
|
623
|
+
|
624
|
+
This generates a file with example builds for common scenarios (HMR development, standard development, and production).
|
625
|
+
|
626
|
+
### Running Builds by Name
|
627
|
+
|
628
|
+
Once you have `config/shakapacker-builds.yml`, you can run builds by name:
|
629
|
+
|
630
|
+
```bash
|
631
|
+
# List available builds
|
632
|
+
bin/shakapacker --list-builds
|
633
|
+
|
634
|
+
# Run a specific build
|
635
|
+
bin/shakapacker --build dev-hmr # Client bundle with HMR (automatically uses dev server)
|
636
|
+
bin/shakapacker --build prod # Client and server bundles for production
|
637
|
+
bin/shakapacker --build dev # Client bundle for development
|
638
|
+
```
|
639
|
+
|
640
|
+
### Build Configuration Format
|
641
|
+
|
642
|
+
Example `config/shakapacker-builds.yml`:
|
643
|
+
|
644
|
+
```yaml
|
645
|
+
builds:
|
646
|
+
dev-hmr:
|
647
|
+
description: Client bundle with HMR (React Fast Refresh)
|
648
|
+
bundler: rspack # Optional: override assets_bundler from config/shakapacker.yml
|
649
|
+
environment:
|
650
|
+
NODE_ENV: development
|
651
|
+
RAILS_ENV: development
|
652
|
+
WEBPACK_SERVE: "true" # Automatically uses bin/shakapacker-dev-server
|
653
|
+
outputs:
|
654
|
+
- client
|
655
|
+
config: config/${BUNDLER}/custom.config.js # Optional: custom config file with variable substitution
|
656
|
+
|
657
|
+
prod:
|
658
|
+
description: Production client and server bundles
|
659
|
+
environment:
|
660
|
+
NODE_ENV: production
|
661
|
+
RAILS_ENV: production
|
662
|
+
outputs:
|
663
|
+
- client # Multiple outputs - builds both client and server bundles
|
664
|
+
- server
|
665
|
+
```
|
666
|
+
|
667
|
+
### Build Configuration Options
|
668
|
+
|
669
|
+
- **`description`** (optional): Human-readable description of the build
|
670
|
+
- **`bundler`** (optional): Override the default bundler from `config/shakapacker.yml` (`webpack` or `rspack`)
|
671
|
+
- **`dev_server`** (optional): Boolean flag to force routing to dev server (overrides environment variable detection)
|
672
|
+
- **`environment`**: Environment variables to set when running the build
|
673
|
+
- **`outputs`**: Array of output types - can include `client`, `server`, or both for multiple bundles in a single build
|
674
|
+
- **`config`** (optional): Custom config file path (supports `${BUNDLER}` variable substitution)
|
675
|
+
- **`bundler_env`** (optional): Key-value pairs passed as bundler `--env` flags (e.g., `{ analyze: true }` becomes `--env analyze=true`)
|
676
|
+
|
677
|
+
### Automatic Dev Server Detection
|
678
|
+
|
679
|
+
Shakapacker automatically uses `bin/shakapacker-dev-server` instead of the regular build command when:
|
680
|
+
|
681
|
+
1. The build has `dev_server: true` explicitly set (preferred method - takes precedence over environment variables), OR
|
682
|
+
2. The build has `WEBPACK_SERVE=true` or `HMR=true` in its environment variables (fallback for backward compatibility)
|
683
|
+
|
684
|
+
Example:
|
685
|
+
|
686
|
+
```bash
|
687
|
+
# These are equivalent:
|
688
|
+
bin/shakapacker --build dev-hmr
|
689
|
+
WEBPACK_SERVE=true bin/shakapacker-dev-server # (with dev-hmr environment vars)
|
690
|
+
```
|
691
|
+
|
692
|
+
### Variable Substitution
|
693
|
+
|
694
|
+
The `config` field supports `${BUNDLER}` substitution:
|
695
|
+
|
696
|
+
```yaml
|
697
|
+
builds:
|
698
|
+
custom:
|
699
|
+
bundler: rspack
|
700
|
+
config: config/${BUNDLER}/custom.config.js # Becomes: config/rspack/custom.config.js
|
701
|
+
```
|
702
|
+
|
703
|
+
### When to Use Build Configurations
|
704
|
+
|
705
|
+
Build configurations are useful for:
|
706
|
+
|
707
|
+
- **Multiple build scenarios**: Use when you need different builds for HMR development, standard development, and production
|
708
|
+
- **CI/CD pipelines**: Use when you want predefined builds that can be referenced in deployment scripts
|
709
|
+
- **Team consistency**: Use to ensure all developers use the same build configurations
|
710
|
+
- **Complex setups**: Use to manage different bundler configs or environment variables for different scenarios
|
711
|
+
|
611
712
|
## Troubleshooting
|
612
713
|
|
613
714
|
If you encounter configuration issues:
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# Preventing FOUC (Flash of Unstyled Content)
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
|
5
|
+
FOUC (Flash of Unstyled Content) occurs when content is rendered before stylesheets load, causing a brief flash of unstyled content. This guide explains how to prevent FOUC when using Shakapacker's view helpers.
|
6
|
+
|
7
|
+
## Basic Solution
|
8
|
+
|
9
|
+
Place `stylesheet_pack_tag` in the `<head>` section of your layout, not at the bottom of the `<body>`. This ensures styles load before content is rendered.
|
10
|
+
|
11
|
+
**Recommended layout structure:**
|
12
|
+
|
13
|
+
```erb
|
14
|
+
<!DOCTYPE html>
|
15
|
+
<html>
|
16
|
+
<head>
|
17
|
+
<meta charset="UTF-8">
|
18
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
19
|
+
<title>My App</title>
|
20
|
+
|
21
|
+
<%= stylesheet_pack_tag 'application', media: 'all' %>
|
22
|
+
<%= javascript_pack_tag 'application', defer: true %>
|
23
|
+
</head>
|
24
|
+
<body>
|
25
|
+
<%= yield %>
|
26
|
+
</body>
|
27
|
+
</html>
|
28
|
+
```
|
29
|
+
|
30
|
+
## Advanced: Using `content_for` with Dynamic Pack Loading
|
31
|
+
|
32
|
+
If you're using libraries that dynamically append packs during rendering (like React on Rails with `auto_load_bundle`), or if you need to append packs from views/partials, you must ensure that all `append_*` helpers execute before the pack tags render.
|
33
|
+
|
34
|
+
Rails' `content_for` pattern solves this execution order problem.
|
35
|
+
|
36
|
+
### The `content_for :body_content` Pattern
|
37
|
+
|
38
|
+
This pattern renders the body content first, allowing all append calls to register before the pack tags in the head are rendered:
|
39
|
+
|
40
|
+
```erb
|
41
|
+
<% content_for :body_content do %>
|
42
|
+
<%= render 'shared/header' %>
|
43
|
+
<%= yield %>
|
44
|
+
<%= render 'shared/footer' %>
|
45
|
+
<% end %>
|
46
|
+
|
47
|
+
<!DOCTYPE html>
|
48
|
+
<html>
|
49
|
+
<head>
|
50
|
+
<meta charset="UTF-8">
|
51
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
52
|
+
<title>My App</title>
|
53
|
+
|
54
|
+
<%= stylesheet_pack_tag 'application', media: 'all' %>
|
55
|
+
<%= javascript_pack_tag 'application', defer: true %>
|
56
|
+
</head>
|
57
|
+
<body>
|
58
|
+
<%= yield :body_content %>
|
59
|
+
</body>
|
60
|
+
</html>
|
61
|
+
```
|
62
|
+
|
63
|
+
**How this works:**
|
64
|
+
|
65
|
+
1. The `content_for :body_content` block executes first during template rendering
|
66
|
+
2. Any `append_stylesheet_pack_tag` or `append_javascript_pack_tag` calls in your views/partials register their packs
|
67
|
+
3. Libraries like React on Rails can auto-append component-specific packs during rendering
|
68
|
+
4. The pack tags in `<head>` then render with all registered appends
|
69
|
+
5. Finally, `yield :body_content` outputs the pre-rendered content
|
70
|
+
|
71
|
+
**Result:**
|
72
|
+
|
73
|
+
- ✅ All appends (explicit + auto) happen before pack tags
|
74
|
+
- ✅ Stylesheets load in head, eliminating FOUC
|
75
|
+
- ✅ Works with `auto_load_bundle` and similar features
|
76
|
+
|
77
|
+
### Alternative: Using `yield :head` for Explicit Appends
|
78
|
+
|
79
|
+
For simpler cases where you know which packs you need upfront and can explicitly specify them, you can use `content_for :head` in your views and yield it in your layout.
|
80
|
+
|
81
|
+
**Layout (app/views/layouts/application.html.erb):**
|
82
|
+
|
83
|
+
```erb
|
84
|
+
<!DOCTYPE html>
|
85
|
+
<html>
|
86
|
+
<head>
|
87
|
+
<meta charset="UTF-8">
|
88
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
89
|
+
<title>My App</title>
|
90
|
+
|
91
|
+
<%= yield :head %>
|
92
|
+
<%= stylesheet_pack_tag 'application', media: 'all' %>
|
93
|
+
<%= javascript_pack_tag 'application', defer: true %>
|
94
|
+
</head>
|
95
|
+
<body>
|
96
|
+
<%= yield %>
|
97
|
+
</body>
|
98
|
+
</html>
|
99
|
+
```
|
100
|
+
|
101
|
+
**View (app/views/pages/show.html.erb):**
|
102
|
+
|
103
|
+
```erb
|
104
|
+
<% content_for :head do %>
|
105
|
+
<%= append_stylesheet_pack_tag 'my-component' %>
|
106
|
+
<%= append_javascript_pack_tag 'my-component' %>
|
107
|
+
<% end %>
|
108
|
+
|
109
|
+
<h1>My Page</h1>
|
110
|
+
<p>Content goes here...</p>
|
111
|
+
```
|
112
|
+
|
113
|
+
This approach works when:
|
114
|
+
|
115
|
+
- You're not using auto-appending libraries
|
116
|
+
- You can explicitly list all required packs in each view
|
117
|
+
- You don't need dynamic pack determination
|
118
|
+
|
119
|
+
## Key Takeaways
|
120
|
+
|
121
|
+
- Always place `stylesheet_pack_tag` in `<head>` to prevent FOUC
|
122
|
+
- Use `content_for` to control execution order when using `append_*` helpers
|
123
|
+
- Ensure `append_*` helpers execute before the main pack tags
|
124
|
+
- JavaScript can use `defer: true` (default) or be placed at end of `<body>`
|
125
|
+
- The `content_for :body_content` pattern is essential when using auto-appending libraries
|
126
|
+
|
127
|
+
## Related
|
128
|
+
|
129
|
+
- [View Helpers Documentation](../README.md#view-helpers)
|
130
|
+
- [Troubleshooting Guide](./troubleshooting.md)
|
131
|
+
- Original issue: [#720](https://github.com/shakacode/shakapacker/issues/720)
|
132
|
+
- Working implementation: [react-webpack-rails-tutorial PR #686](https://github.com/shakacode/react-webpack-rails-tutorial/pull/686)
|
data/docs/troubleshooting.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Troubleshooting
|
2
2
|
|
3
|
+
## Flash of Unstyled Content (FOUC)
|
4
|
+
|
5
|
+
If you're experiencing FOUC where content briefly appears unstyled before CSS loads, see the [Preventing FOUC guide](./preventing_fouc.md) for solutions including proper `stylesheet_pack_tag` placement and `content_for` patterns for dynamic pack loading.
|
6
|
+
|
3
7
|
## Debugging your webpack config
|
4
8
|
|
5
9
|
1. Read the error message carefully. The error message will tell you the precise key value
|
data/eslint.config.js
CHANGED
@@ -177,8 +177,7 @@ module.exports = [
|
|
177
177
|
"@typescript-eslint/no-unsafe-argument": "off",
|
178
178
|
"@typescript-eslint/no-explicit-any": "off",
|
179
179
|
"no-useless-escape": "off",
|
180
|
-
"no-continue": "off"
|
181
|
-
"no-nested-ternary": "off"
|
180
|
+
"no-continue": "off"
|
182
181
|
}
|
183
182
|
},
|
184
183
|
{
|
@@ -193,9 +192,7 @@ module.exports = [
|
|
193
192
|
"@typescript-eslint/no-unsafe-function-type": "off",
|
194
193
|
"@typescript-eslint/no-unused-vars": "off",
|
195
194
|
"@typescript-eslint/require-await": "off",
|
196
|
-
"no-param-reassign": "off",
|
197
195
|
"no-await-in-loop": "off",
|
198
|
-
"no-nested-ternary": "off",
|
199
196
|
"import/prefer-default-export": "off",
|
200
197
|
"global-require": "off",
|
201
198
|
"no-underscore-dangle": "off"
|
@@ -215,8 +212,7 @@ module.exports = [
|
|
215
212
|
"@typescript-eslint/no-unsafe-argument": "off",
|
216
213
|
"@typescript-eslint/no-explicit-any": "off",
|
217
214
|
"no-useless-escape": "off",
|
218
|
-
"no-continue": "off"
|
219
|
-
"no-nested-ternary": "off"
|
215
|
+
"no-continue": "off"
|
220
216
|
}
|
221
217
|
},
|
222
218
|
{
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
module Shakapacker
|
4
|
+
class BuildConfigLoader
|
5
|
+
attr_reader :config_file_path
|
6
|
+
|
7
|
+
def initialize(config_file_path = nil)
|
8
|
+
@config_file_path = config_file_path || File.join(Dir.pwd, "config", "shakapacker-builds.yml")
|
9
|
+
end
|
10
|
+
|
11
|
+
def exists?
|
12
|
+
File.exist?(@config_file_path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def load_build(build_name)
|
16
|
+
unless exists?
|
17
|
+
raise ArgumentError, "Config file not found: #{@config_file_path}\n" \
|
18
|
+
"Run 'bin/shakapacker --init' to generate a sample config file."
|
19
|
+
end
|
20
|
+
|
21
|
+
config = load_config
|
22
|
+
fetch_build_or_raise(config, build_name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def resolve_build_config(build_name, default_bundler: "webpack")
|
26
|
+
config = load_config
|
27
|
+
build = fetch_build_or_raise(config, build_name)
|
28
|
+
|
29
|
+
# Resolve bundler with precedence: build.bundler > config.default_bundler > default_bundler
|
30
|
+
bundler = build["bundler"] || config["default_bundler"] || default_bundler
|
31
|
+
|
32
|
+
# Get environment variables
|
33
|
+
environment = build["environment"] || {}
|
34
|
+
|
35
|
+
# Get config file path if specified
|
36
|
+
config_file = build["config"]
|
37
|
+
if config_file
|
38
|
+
# Expand ${BUNDLER} variable
|
39
|
+
config_file = config_file.gsub("${BUNDLER}", bundler)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Get bundler_env for --env flags
|
43
|
+
bundler_env = build["bundler_env"] || {}
|
44
|
+
|
45
|
+
# Get outputs
|
46
|
+
outputs = build["outputs"] || []
|
47
|
+
|
48
|
+
# Validate outputs
|
49
|
+
if outputs.empty?
|
50
|
+
raise ArgumentError, "Build '#{build_name}' has empty outputs array. " \
|
51
|
+
"Please specify at least one output type (client, server, or all)."
|
52
|
+
end
|
53
|
+
|
54
|
+
{
|
55
|
+
name: build_name,
|
56
|
+
description: build["description"],
|
57
|
+
bundler: bundler,
|
58
|
+
dev_server: build["dev_server"],
|
59
|
+
environment: environment,
|
60
|
+
bundler_env: bundler_env,
|
61
|
+
outputs: outputs,
|
62
|
+
config_file: config_file
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def uses_dev_server?(build_config)
|
67
|
+
# Check explicit dev_server flag first (preferred)
|
68
|
+
# Only return early if the value is explicitly set (not nil)
|
69
|
+
return build_config[:dev_server] unless build_config[:dev_server].nil?
|
70
|
+
|
71
|
+
# Fallback: check environment variables for backward compatibility
|
72
|
+
env = build_config[:environment]
|
73
|
+
return false unless env
|
74
|
+
|
75
|
+
# Handle both string "true" and boolean true from YAML
|
76
|
+
%w[WEBPACK_SERVE HMR].any? do |key|
|
77
|
+
value = env[key]
|
78
|
+
value.to_s.strip.casecmp("true").zero?
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def list_builds
|
83
|
+
config = load_config
|
84
|
+
builds = config["builds"]
|
85
|
+
|
86
|
+
puts "\nAvailable builds in #{@config_file_path}:\n\n"
|
87
|
+
|
88
|
+
builds.each do |name, build|
|
89
|
+
bundler = build["bundler"] || config["default_bundler"] || "webpack (default)"
|
90
|
+
outputs = build["outputs"] ? build["outputs"].join(", ") : "missing (invalid)"
|
91
|
+
|
92
|
+
puts " #{name}"
|
93
|
+
puts " Description: #{build["description"]}" if build["description"]
|
94
|
+
puts " Bundler: #{bundler}"
|
95
|
+
puts " Outputs: #{outputs}"
|
96
|
+
puts ""
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def fetch_build_or_raise(config, build_name)
|
103
|
+
build = config["builds"][build_name]
|
104
|
+
unless build
|
105
|
+
available = config["builds"].keys.join(", ")
|
106
|
+
raise ArgumentError, "Build '#{build_name}' not found in config file.\n" \
|
107
|
+
"Available builds: #{available}\n" \
|
108
|
+
"Use 'bin/shakapacker --list-builds' to see all available builds."
|
109
|
+
end
|
110
|
+
build
|
111
|
+
end
|
112
|
+
|
113
|
+
# Load YAML config file safely with Ruby version compatibility
|
114
|
+
# Ruby 3.1+ supports safe_load_file with aliases, older versions need safe_load
|
115
|
+
def load_config
|
116
|
+
begin
|
117
|
+
config = if YAML.respond_to?(:safe_load_file)
|
118
|
+
# Ruby 3.1+: Use safe_load_file with aliases enabled
|
119
|
+
YAML.safe_load_file(@config_file_path, aliases: true)
|
120
|
+
else
|
121
|
+
# Ruby 2.7-3.0: Use safe_load with aliases enabled
|
122
|
+
YAML.safe_load(
|
123
|
+
File.read(@config_file_path),
|
124
|
+
permitted_classes: [],
|
125
|
+
permitted_symbols: [],
|
126
|
+
aliases: true
|
127
|
+
)
|
128
|
+
end
|
129
|
+
rescue ArgumentError
|
130
|
+
# Fallback for older Psych versions without aliases support
|
131
|
+
config = YAML.safe_load(
|
132
|
+
File.read(@config_file_path),
|
133
|
+
permitted_classes: [],
|
134
|
+
permitted_symbols: []
|
135
|
+
)
|
136
|
+
end
|
137
|
+
|
138
|
+
unless config["builds"]&.is_a?(Hash)
|
139
|
+
raise ArgumentError, "Config file must contain a 'builds' object"
|
140
|
+
end
|
141
|
+
|
142
|
+
config
|
143
|
+
rescue Psych::SyntaxError => e
|
144
|
+
raise ArgumentError, "Invalid YAML in config file: #{e.message}"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -8,12 +8,13 @@ class Shakapacker::Configuration
|
|
8
8
|
attr_accessor :installing
|
9
9
|
end
|
10
10
|
|
11
|
-
attr_reader :root_path, :config_path, :env
|
11
|
+
attr_reader :root_path, :config_path, :env, :bundler_override
|
12
12
|
|
13
|
-
def initialize(root_path:, config_path:, env:)
|
13
|
+
def initialize(root_path:, config_path:, env:, bundler_override: nil)
|
14
14
|
@root_path = root_path
|
15
15
|
@env = env
|
16
16
|
@config_path = config_path
|
17
|
+
@bundler_override = bundler_override
|
17
18
|
end
|
18
19
|
|
19
20
|
def dev_server
|
@@ -97,6 +98,9 @@ class Shakapacker::Configuration
|
|
97
98
|
end
|
98
99
|
|
99
100
|
def assets_bundler
|
101
|
+
# CLI --bundler flag takes highest precedence
|
102
|
+
return @bundler_override if @bundler_override
|
103
|
+
|
100
104
|
# Show deprecation warning if using old 'bundler' key
|
101
105
|
if data.has_key?(:bundler) && !data.has_key?(:assets_bundler)
|
102
106
|
$stderr.puts "⚠️ DEPRECATION WARNING: The 'bundler' configuration option is deprecated. Please use 'assets_bundler' instead to avoid confusion with Ruby's Bundler gem manager."
|