shakapacker 9.3.0.beta.1 → 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/.github/workflows/eslint-validation.yml +46 -0
- data/ESLINT_TECHNICAL_DEBT.md +159 -0
- data/Gemfile.lock +1 -1
- data/README.md +9 -6
- data/docs/common-upgrades.md +80 -0
- data/docs/configuration.md +101 -0
- data/docs/preventing_fouc.md +132 -0
- data/docs/troubleshooting.md +4 -0
- data/docs/v9_upgrade.md +90 -8
- data/eslint.config.js +119 -7
- 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 +263 -15
- data/lib/shakapacker/version.rb +1 -1
- data/package/configExporter/buildValidator.ts +2 -2
- data/package/configExporter/cli.ts +260 -173
- data/package/configExporter/configFile.ts +182 -46
- data/package/configExporter/fileWriter.ts +20 -13
- 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/buildValidator.test.js +31 -28
- data/test/configExporter/configFile.test.js +3 -2
- data/test/configExporter/integration.test.js +14 -27
- data/test/package/configExporter.test.js +4 -8
- metadata +8 -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
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: ESLint Validation
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
paths:
|
|
6
|
+
- "eslint.config.js"
|
|
7
|
+
- "package.json"
|
|
8
|
+
- "package/**/*.js"
|
|
9
|
+
- "test/**/*.js"
|
|
10
|
+
- ".github/workflows/eslint-validation.yml"
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
validate:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Setup Node
|
|
19
|
+
uses: actions/setup-node@v4
|
|
20
|
+
with:
|
|
21
|
+
node-version: "20"
|
|
22
|
+
cache: "yarn"
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: yarn install --frozen-lockfile
|
|
26
|
+
|
|
27
|
+
- name: Validate ESLint config
|
|
28
|
+
run: |
|
|
29
|
+
echo "Validating ESLint configuration..."
|
|
30
|
+
node -e "const config = require('./eslint.config.js'); console.log('✓ Config is valid with', config.length, 'rule sets')"
|
|
31
|
+
|
|
32
|
+
- name: Run ESLint
|
|
33
|
+
run: |
|
|
34
|
+
echo "Running ESLint on allowed files..."
|
|
35
|
+
yarn eslint . --max-warnings 5
|
|
36
|
+
|
|
37
|
+
- name: Check warning count
|
|
38
|
+
run: |
|
|
39
|
+
echo "Checking warning count..."
|
|
40
|
+
WARNING_COUNT=$(yarn eslint . 2>&1 | grep -E "^✖.*warning" | grep -oE "[0-9]+ warning" | cut -d' ' -f1)
|
|
41
|
+
echo "Current warning count: $WARNING_COUNT"
|
|
42
|
+
if [ "$WARNING_COUNT" -gt "5" ]; then
|
|
43
|
+
echo "❌ Too many warnings: $WARNING_COUNT (max allowed: 5)"
|
|
44
|
+
exit 1
|
|
45
|
+
fi
|
|
46
|
+
echo "✓ Warning count is acceptable"
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# ESLint Technical Debt Documentation
|
|
2
|
+
|
|
3
|
+
This document tracks the ESLint errors currently suppressed in the codebase and outlines the plan to address them.
|
|
4
|
+
|
|
5
|
+
## Current Approach
|
|
6
|
+
|
|
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
|
+
|
|
9
|
+
**Latest Update**: Fixed all `no-param-reassign` violations by refactoring to create new objects instead of mutating parameters (7 violations resolved).
|
|
10
|
+
|
|
11
|
+
## Current Linting Status
|
|
12
|
+
|
|
13
|
+
**Files currently linted** (`test/**/*.js`, `scripts/*.js`):
|
|
14
|
+
|
|
15
|
+
- ✅ **0 errors** (CI passing)
|
|
16
|
+
- ⚠️ **3 warnings** (acceptable, won't block CI)
|
|
17
|
+
- 1x unused eslint-disable directive in `scripts/remove-use-strict.js`
|
|
18
|
+
- 2x jest/no-disabled-tests in test files (expected for conditional test skipping)
|
|
19
|
+
|
|
20
|
+
**TypeScript files** (currently ignored via `package/**/*.ts`):
|
|
21
|
+
|
|
22
|
+
- **Estimated suppressed errors: ~163** (from sample analysis)
|
|
23
|
+
- TypeScript type-safety issues: ~114 (70%)
|
|
24
|
+
- Style/convention issues: ~49 (30%)
|
|
25
|
+
|
|
26
|
+
**Target**: Reduce suppressed errors by 50% within Q1 2025
|
|
27
|
+
**Last Updated**: 2025-10-15
|
|
28
|
+
|
|
29
|
+
## Priority Matrix
|
|
30
|
+
|
|
31
|
+
| Category | Impact | Effort | Priority | Count |
|
|
32
|
+
| ------------------------------------ | ------ | ------ | -------- | ----- |
|
|
33
|
+
| `@typescript-eslint/no-explicit-any` | High | High | P1 | 22 |
|
|
34
|
+
| `@typescript-eslint/no-unsafe-*` | High | High | P1 | 85 |
|
|
35
|
+
| `config.ts` type safety | High | Medium | P1 | 7 |
|
|
36
|
+
| `no-param-reassign` | Medium | Low | P2 | 0 |
|
|
37
|
+
| `class-methods-use-this` | Low | Low | P3 | 0 |
|
|
38
|
+
| `no-nested-ternary` | Low | Low | P3 | 0 |
|
|
39
|
+
| `import/prefer-default-export` | Low | Medium | P3 | 9 |
|
|
40
|
+
| `global-require` | Medium | High | P2 | 3 |
|
|
41
|
+
| Other style issues | Low | Low | P3 | 31 |
|
|
42
|
+
|
|
43
|
+
## Categories of Suppressed Errors
|
|
44
|
+
|
|
45
|
+
### 1. TypeScript Type Safety (Requires Major Refactoring)
|
|
46
|
+
|
|
47
|
+
#### `@typescript-eslint/no-explicit-any` (22 instances)
|
|
48
|
+
|
|
49
|
+
**Files affected:** `configExporter/`, `config.ts`, `utils/`
|
|
50
|
+
**Why suppressed:** These require careful type definitions and potentially breaking API changes
|
|
51
|
+
**Fix strategy:** Create proper type definitions for configuration objects and YAML parsing
|
|
52
|
+
|
|
53
|
+
#### `@typescript-eslint/no-unsafe-*` (85 instances)
|
|
54
|
+
|
|
55
|
+
- `no-unsafe-assignment`: 47 instances
|
|
56
|
+
- `no-unsafe-member-access`: 20 instances
|
|
57
|
+
- `no-unsafe-call`: 8 instances
|
|
58
|
+
- `no-unsafe-return`: 8 instances
|
|
59
|
+
- `no-unsafe-argument`: 7 instances
|
|
60
|
+
**Why suppressed:** These stem from `any` types and dynamic property access
|
|
61
|
+
**Fix strategy:** Requires comprehensive type refactoring alongside `no-explicit-any` fixes
|
|
62
|
+
|
|
63
|
+
### 2. Module System (Potential Breaking Changes)
|
|
64
|
+
|
|
65
|
+
#### `global-require` (3 instances)
|
|
66
|
+
|
|
67
|
+
**Files affected:** `configExporter/cli.ts`
|
|
68
|
+
**Why suppressed:** Dynamic require calls are needed for conditional module loading
|
|
69
|
+
**Fix strategy:** Would require converting to ES modules with dynamic imports
|
|
70
|
+
|
|
71
|
+
#### `import/prefer-default-export` (9 instances)
|
|
72
|
+
|
|
73
|
+
**Files affected:** Multiple single-export modules
|
|
74
|
+
**Why suppressed:** Adding default exports alongside named exports could break consumers
|
|
75
|
+
**Fix strategy:** Can be fixed non-breaking by adding default exports that match named exports
|
|
76
|
+
|
|
77
|
+
### 3. Code Style (Can Be Fixed)
|
|
78
|
+
|
|
79
|
+
#### `class-methods-use-this` (0 instances)
|
|
80
|
+
|
|
81
|
+
✅ **FIXED** - All FileWriter methods that didn't use instance state have been converted to static methods
|
|
82
|
+
|
|
83
|
+
#### `no-nested-ternary` (0 instances)
|
|
84
|
+
|
|
85
|
+
✅ **FIXED** - All nested ternary expressions have been refactored to if-else statements for better readability
|
|
86
|
+
|
|
87
|
+
#### `no-param-reassign` (0 instances)
|
|
88
|
+
|
|
89
|
+
✅ **FIXED** - Refactored `applyDefaults` function to return new objects instead of mutating parameters
|
|
90
|
+
|
|
91
|
+
#### `no-underscore-dangle` (2 instances)
|
|
92
|
+
|
|
93
|
+
**Fix strategy:** Rename variables or add exceptions for Node internals
|
|
94
|
+
|
|
95
|
+
### 4. Control Flow
|
|
96
|
+
|
|
97
|
+
#### `no-await-in-loop` (1 instance)
|
|
98
|
+
|
|
99
|
+
**Fix strategy:** Use `Promise.all()` for parallel execution
|
|
100
|
+
|
|
101
|
+
#### `no-continue` (1 instance)
|
|
102
|
+
|
|
103
|
+
**Fix strategy:** Refactor loop logic
|
|
104
|
+
|
|
105
|
+
## Recommended Approach
|
|
106
|
+
|
|
107
|
+
### Phase 1: Non-Breaking Fixes
|
|
108
|
+
|
|
109
|
+
✅ Completed:
|
|
110
|
+
|
|
111
|
+
- Fixed `no-use-before-define` by reordering functions
|
|
112
|
+
- Fixed redundant type constituents with `string & {}` pattern
|
|
113
|
+
- Added proper type annotations for `requireOrError` calls
|
|
114
|
+
- Configured appropriate global rule disables (`no-console`, `no-restricted-syntax`)
|
|
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
|
+
|
|
119
|
+
🔧 Could still fix (low risk):
|
|
120
|
+
|
|
121
|
+
- `no-useless-escape` - Remove unnecessary escapes
|
|
122
|
+
- Unused variables - Remove or prefix with underscore
|
|
123
|
+
|
|
124
|
+
### Phase 2: Follow-up PRs (Non-Breaking)
|
|
125
|
+
|
|
126
|
+
- Systematic type safety improvements file by file
|
|
127
|
+
- Add explicit type definitions for configuration objects
|
|
128
|
+
- Replace `any` with `unknown` where possible
|
|
129
|
+
|
|
130
|
+
### Phase 3: Future Major Version (Breaking Changes)
|
|
131
|
+
|
|
132
|
+
- Convert `export =` to `export default`
|
|
133
|
+
- Convert `require()` to ES6 imports
|
|
134
|
+
- Full TypeScript strict mode compliance
|
|
135
|
+
- Provide codemod for automatic migration
|
|
136
|
+
|
|
137
|
+
## Configuration Strategy
|
|
138
|
+
|
|
139
|
+
The current approach uses file-specific overrides to suppress errors in affected files while maintaining strict checking elsewhere. This allows:
|
|
140
|
+
|
|
141
|
+
1. New code to follow strict standards
|
|
142
|
+
2. Gradual refactoring of existing code
|
|
143
|
+
3. Clear visibility of technical debt
|
|
144
|
+
|
|
145
|
+
## Issue Tracking
|
|
146
|
+
|
|
147
|
+
GitHub issues should be created for each category:
|
|
148
|
+
|
|
149
|
+
1. [ ] Issue: Type safety refactoring for configExporter module
|
|
150
|
+
2. [ ] Issue: Type safety for dynamic config loading
|
|
151
|
+
3. [ ] Issue: Convert class methods to static where appropriate
|
|
152
|
+
4. [ ] Issue: Module system modernization (ES6 modules)
|
|
153
|
+
5. [ ] Issue: Create codemod for breaking changes migration
|
|
154
|
+
|
|
155
|
+
## Notes
|
|
156
|
+
|
|
157
|
+
- All suppressed errors are documented in `eslint.config.js` with TODO comments
|
|
158
|
+
- The suppressions are scoped to specific files to prevent spreading technical debt
|
|
159
|
+
- New code should not add to these suppressions
|
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/common-upgrades.md
CHANGED
|
@@ -6,6 +6,7 @@ This document provides step-by-step instructions for the most common upgrade sce
|
|
|
6
6
|
|
|
7
7
|
## Table of Contents
|
|
8
8
|
|
|
9
|
+
- [Upgrading Shakapacker](#upgrading-shakapacker)
|
|
9
10
|
- [Migrating Package Managers](#migrating-package-managers)
|
|
10
11
|
- [Yarn to npm](#yarn-to-npm)
|
|
11
12
|
- [npm to Yarn](#npm-to-yarn)
|
|
@@ -15,6 +16,85 @@ This document provides step-by-step instructions for the most common upgrade sce
|
|
|
15
16
|
|
|
16
17
|
---
|
|
17
18
|
|
|
19
|
+
## Upgrading Shakapacker
|
|
20
|
+
|
|
21
|
+
> **⚠️ Important:** Shakapacker is both a Ruby gem AND an npm package. **You must update BOTH** when upgrading.
|
|
22
|
+
|
|
23
|
+
Shakapacker consists of two components that must be updated together:
|
|
24
|
+
|
|
25
|
+
1. **Ruby gem** - provides Rails integration and view helpers
|
|
26
|
+
2. **npm package** - provides webpack/rspack configuration and build tools
|
|
27
|
+
|
|
28
|
+
### Upgrade Steps
|
|
29
|
+
|
|
30
|
+
#### 1. Update `Gemfile`
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
gem "shakapacker", "9.3.0" # or the version you want to upgrade to
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Pre-release versions:** Ruby gems use dot notation (e.g., `"9.3.0.beta.1"`)
|
|
37
|
+
|
|
38
|
+
#### 2. Update `package.json`
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"shakapacker": "9.3.0"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Pre-release versions:** npm uses hyphen notation (e.g., `"9.3.0-beta.1"`)
|
|
49
|
+
|
|
50
|
+
#### 3. Run bundler and package manager
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
bundle update shakapacker
|
|
54
|
+
yarn install # or npm install, pnpm install, bun install
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### 4. Test your build
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
bin/shakapacker
|
|
61
|
+
bin/shakapacker-dev-server
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Why Both Must Be Updated
|
|
65
|
+
|
|
66
|
+
- **Mismatched versions can cause build failures** - The Ruby gem expects specific configuration formats from the npm package
|
|
67
|
+
- **Feature compatibility** - New features in the gem require corresponding npm package updates
|
|
68
|
+
- **Bug fixes** - Fixes often span both Ruby and JavaScript code
|
|
69
|
+
|
|
70
|
+
### Version Format Differences
|
|
71
|
+
|
|
72
|
+
Note that pre-release versions use different formats:
|
|
73
|
+
|
|
74
|
+
| Component | Stable Version | Pre-release Version |
|
|
75
|
+
| ------------ | -------------- | ------------------- |
|
|
76
|
+
| Gemfile | `"9.3.0"` | `"9.3.0.beta.1"` |
|
|
77
|
+
| package.json | `"9.3.0"` | `"9.3.0-beta.1"` |
|
|
78
|
+
|
|
79
|
+
### Finding the Latest Version
|
|
80
|
+
|
|
81
|
+
- **Ruby gem:** Check [RubyGems.org](https://rubygems.org/gems/shakapacker)
|
|
82
|
+
- **npm package:** Check [npmjs.com](https://www.npmjs.com/package/shakapacker)
|
|
83
|
+
- **Releases:** See [GitHub Releases](https://github.com/shakacode/shakapacker/releases)
|
|
84
|
+
|
|
85
|
+
### Major Version Upgrades
|
|
86
|
+
|
|
87
|
+
For major version upgrades, always consult the version-specific upgrade guides for breaking changes and new features:
|
|
88
|
+
|
|
89
|
+
- [V9 Upgrade Guide](./v9_upgrade.md) - Upgrading from v8 to v9 (includes CSS Modules changes, SWC defaults, and more)
|
|
90
|
+
- [V8 Upgrade Guide](./v8_upgrade.md) - Upgrading from v7 to v8
|
|
91
|
+
- [V7 Upgrade Guide](./v7_upgrade.md) - Upgrading from v6 to v7
|
|
92
|
+
- [V6 Upgrade Guide](./v6_upgrade.md) - Upgrading from v5 to v6
|
|
93
|
+
|
|
94
|
+
> **💡 Note:** Major version upgrades may include breaking changes. The steps above cover the basic gem/package updates that apply to all versions, but you should always review the version-specific guide for additional migration steps.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
18
98
|
## Migrating Package Managers
|
|
19
99
|
|
|
20
100
|
### Yarn to npm
|
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
|