shakapacker 9.3.0.beta.2 → 9.3.0.beta.5
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/CHANGELOG.md +20 -0
- data/CLAUDE.md +13 -0
- data/ESLINT_TECHNICAL_DEBT.md +13 -14
- data/Gemfile.lock +1 -1
- data/README.md +28 -6
- data/docs/configuration.md +136 -0
- data/docs/early_hints.md +440 -0
- data/docs/early_hints_new_api.md +700 -0
- data/docs/feature_testing.md +492 -0
- data/docs/preventing_fouc.md +132 -0
- data/docs/troubleshooting.md +4 -0
- data/eslint.config.js +2 -6
- data/lib/install/config/shakapacker.yml +27 -0
- data/lib/shakapacker/build_config_loader.rb +147 -0
- data/lib/shakapacker/configuration.rb +10 -2
- data/lib/shakapacker/dev_server_runner.rb +73 -0
- data/lib/shakapacker/helper.rb +409 -14
- data/lib/shakapacker/railtie.rb +4 -0
- data/lib/shakapacker/runner.rb +239 -14
- data/lib/shakapacker/version.rb +1 -1
- data/package/configExporter/buildValidator.ts +3 -4
- data/package/configExporter/cli.ts +253 -161
- 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/scripts/remove-use-strict.js +0 -1
- data/test/configExporter/configFile.test.js +3 -2
- data/test/configExporter/integration.test.js +14 -27
- data/test/package/rules/babel.test.js +1 -0
- data/test/package/rules/swc.test.js +1 -0
- metadata +9 -4
- /data/bin/{export-bundler-config → shakapacker-config} +0 -0
- /data/lib/install/bin/{export-bundler-config → shakapacker-config} +0 -0
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
# Feature Testing Guide
|
|
2
|
+
|
|
3
|
+
This guide shows how to manually verify that Shakapacker features are working correctly in your application.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [HTTP 103 Early Hints](#http-103-early-hints)
|
|
8
|
+
- [Asset Compilation](#asset-compilation)
|
|
9
|
+
- [Code Splitting](#code-splitting)
|
|
10
|
+
- [Subresource Integrity (SRI)](#subresource-integrity-sri)
|
|
11
|
+
- [Source Maps](#source-maps)
|
|
12
|
+
- [Development Server](#development-server)
|
|
13
|
+
|
|
14
|
+
## HTTP 103 Early Hints
|
|
15
|
+
|
|
16
|
+
### Prerequisites
|
|
17
|
+
|
|
18
|
+
- Rails 5.2+
|
|
19
|
+
- HTTP/2-capable server (Puma 5+ recommended)
|
|
20
|
+
- Modern browser (Chrome/Edge/Firefox 103+, Safari 16.4+)
|
|
21
|
+
|
|
22
|
+
### ⚠️ Development Mode Limitations
|
|
23
|
+
|
|
24
|
+
**Early hints require HTTP/2 and will NOT work in standard Rails development mode**, which uses HTTP/1.1 by default.
|
|
25
|
+
|
|
26
|
+
**Why localhost testing doesn't work:**
|
|
27
|
+
|
|
28
|
+
- Early hints require HTTP/2
|
|
29
|
+
- HTTP/2 requires HTTPS/TLS (not `http://`)
|
|
30
|
+
- Plain `http://localhost` uses HTTP/1.1
|
|
31
|
+
- Early hints are silently ignored on HTTP/1.1
|
|
32
|
+
|
|
33
|
+
**Debug Mode:** Enable `debug: true` in config to see HTML comments showing what hints were sent (or why they weren't):
|
|
34
|
+
|
|
35
|
+
```yaml
|
|
36
|
+
# config/shakapacker.yml
|
|
37
|
+
development:
|
|
38
|
+
early_hints:
|
|
39
|
+
enabled: true
|
|
40
|
+
debug: true # Outputs debug info as HTML comments
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Testing recommendation:** Use Method 1 (Browser DevTools) or Method 2 (curl) on production/staging environments with HTTPS enabled. You should see BOTH `HTTP/2 103` (early hints) and `HTTP/2 200` (final response).
|
|
44
|
+
|
|
45
|
+
### Method 1: Browser DevTools (Recommended)
|
|
46
|
+
|
|
47
|
+
1. **Enable early hints in config:**
|
|
48
|
+
|
|
49
|
+
```yaml
|
|
50
|
+
# config/shakapacker.yml
|
|
51
|
+
production:
|
|
52
|
+
early_hints:
|
|
53
|
+
enabled: true
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
2. **Open Chrome DevTools** (F12 or Cmd+Option+I)
|
|
57
|
+
|
|
58
|
+
3. **Go to Network tab** and reload your page
|
|
59
|
+
|
|
60
|
+
4. **Look for the initial document request** (usually first row)
|
|
61
|
+
|
|
62
|
+
5. **Check the Status column** - you should see:
|
|
63
|
+
- `103 Early Hints` (shown briefly before the final response)
|
|
64
|
+
- Followed by `200 OK` for the final HTML
|
|
65
|
+
|
|
66
|
+
6. **Verify Link headers:**
|
|
67
|
+
- Click on the document request
|
|
68
|
+
- Go to the "Headers" tab
|
|
69
|
+
- Scroll to "Response Headers" section
|
|
70
|
+
- Look for `Link:` headers with `rel=preload` or `rel=prefetch`
|
|
71
|
+
|
|
72
|
+
**Expected output:**
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
Link: </packs/application-abc123.js>; rel=preload; as=script; crossorigin="anonymous"
|
|
76
|
+
Link: </packs/application-xyz789.css>; rel=preload; as=style; crossorigin="anonymous"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Method 2: curl (Command Line)
|
|
80
|
+
|
|
81
|
+
**Test early hints with curl (requires HTTPS/HTTP2):**
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Production/staging with HTTPS
|
|
85
|
+
curl -v --http2 https://your-app.com 2>&1 | grep -A5 "< HTTP"
|
|
86
|
+
|
|
87
|
+
# Look for:
|
|
88
|
+
# < HTTP/2 103
|
|
89
|
+
# < link: </packs/...>; rel=preload
|
|
90
|
+
# < HTTP/2 200
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**⚠️ Local testing limitations:**
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# This will NOT show early hints (returns HTTP/1.1):
|
|
97
|
+
RAILS_ENV=production bundle exec rails server
|
|
98
|
+
curl -v --http2 http://localhost:3000 2>&1 | grep -A5 "< HTTP"
|
|
99
|
+
# Output: < HTTP/1.1 200 OK (no 103 status)
|
|
100
|
+
|
|
101
|
+
# Why: Puma requires SSL certificates for HTTP/2
|
|
102
|
+
# Early hints need HTTP/2, which needs HTTPS
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Expected output:**
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
< HTTP/2 103
|
|
109
|
+
< link: </packs/application-abc123.js>; rel=preload; as=script; crossorigin="anonymous"
|
|
110
|
+
< link: </packs/application-xyz789.css>; rel=preload; as=style; crossorigin="anonymous"
|
|
111
|
+
<
|
|
112
|
+
< HTTP/2 200
|
|
113
|
+
< content-type: text/html; charset=utf-8
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Method 3: Check HTML Source
|
|
117
|
+
|
|
118
|
+
Early hints don't appear in HTML source (they're sent as HTTP headers before HTML). However, you can verify the assets exist:
|
|
119
|
+
|
|
120
|
+
```html
|
|
121
|
+
<!-- View page source and look for these tags in <head> or before </body> -->
|
|
122
|
+
<script src="/packs/application-abc123.js"></script>
|
|
123
|
+
<link rel="stylesheet" href="/packs/application-xyz789.css" />
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The asset filenames in early hints should match those in your HTML.
|
|
127
|
+
|
|
128
|
+
### Troubleshooting Early Hints
|
|
129
|
+
|
|
130
|
+
**Not seeing 103 status?**
|
|
131
|
+
|
|
132
|
+
1. **Enable debug mode to see what's happening:**
|
|
133
|
+
|
|
134
|
+
```yaml
|
|
135
|
+
# config/shakapacker.yml
|
|
136
|
+
production: # or development
|
|
137
|
+
early_hints:
|
|
138
|
+
enabled: true
|
|
139
|
+
debug: true # Shows debug info as HTML comments
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
View page source and look for `<!-- Shakapacker Early Hints Debug -->` comments showing what hints were sent or why they were skipped.
|
|
143
|
+
|
|
144
|
+
2. **Reverse proxies and CDNs often strip 103 responses:**
|
|
145
|
+
|
|
146
|
+
**Most common cause**: If debug mode shows hints are being sent but you don't see `HTTP/2 103` in curl or DevTools, your reverse proxy or CDN is likely stripping the 103 status code before it reaches the client.
|
|
147
|
+
|
|
148
|
+
Common culprits:
|
|
149
|
+
- Control Plane (cpln.app)
|
|
150
|
+
- Some Cloudflare configurations
|
|
151
|
+
- nginx without explicit early hints support
|
|
152
|
+
- AWS ALB/ELB
|
|
153
|
+
- Other load balancers and proxies
|
|
154
|
+
|
|
155
|
+
**How to fix proxy stripping:**
|
|
156
|
+
|
|
157
|
+
**nginx** - Enable early hints support:
|
|
158
|
+
|
|
159
|
+
```nginx
|
|
160
|
+
# nginx.conf
|
|
161
|
+
http {
|
|
162
|
+
# Enable HTTP/2
|
|
163
|
+
server {
|
|
164
|
+
listen 443 ssl http2;
|
|
165
|
+
|
|
166
|
+
# Pass through early hints (nginx 1.13+)
|
|
167
|
+
proxy_pass_header Link;
|
|
168
|
+
|
|
169
|
+
location / {
|
|
170
|
+
proxy_pass http://rails_backend;
|
|
171
|
+
proxy_http_version 1.1;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Cloudflare** - Early hints are supported but must be enabled:
|
|
178
|
+
- Go to Speed > Optimization in your Cloudflare dashboard
|
|
179
|
+
- Enable "Early Hints"
|
|
180
|
+
- Note: Only available on paid plans (Pro, Business, Enterprise)
|
|
181
|
+
|
|
182
|
+
**AWS ALB/ELB** - Does NOT support HTTP/2 103:
|
|
183
|
+
- AWS load balancers strip 103 responses
|
|
184
|
+
- **Workaround**: Deploy without ALB/ELB, or accept Link headers in 200 response
|
|
185
|
+
- Alternative: Use CloudFront with origin that supports 103
|
|
186
|
+
|
|
187
|
+
**Control Plane (cpln.app)** - Appears to strip 103:
|
|
188
|
+
- Control Plane supports HTTP/2 by default but early hints (103) don't appear to pass through
|
|
189
|
+
- No documented configuration option for early hints passthrough
|
|
190
|
+
- **Contact Control Plane support** if you need early hints support for your application
|
|
191
|
+
- **Workaround**: Early hints will work server-side but won't be visible to clients
|
|
192
|
+
- Link headers may still be included in 200 response
|
|
193
|
+
|
|
194
|
+
**General workaround when proxy strips 103:**
|
|
195
|
+
- Rails still sends Link headers in the 200 response
|
|
196
|
+
- Browsers can use these for next-page prefetch (not early hints benefit)
|
|
197
|
+
- Consider if early hints are worth the complexity for your setup
|
|
198
|
+
- Use debug mode to verify Rails is sending hints correctly
|
|
199
|
+
|
|
200
|
+
3. **Check server supports HTTP/2 and early hints:**
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
# Puma version (need 5+)
|
|
204
|
+
bundle exec puma --version
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
4. **Verify config is enabled:**
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
# In Rails console
|
|
211
|
+
Shakapacker.config.early_hints
|
|
212
|
+
# Should return: { enabled: true, css: "preload", js: "preload", debug: false }
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
5. **Check Rails log for debug messages:**
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
tail -f log/production.log | grep -i "early hints"
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
6. **Verify your server uses HTTP/2:**
|
|
222
|
+
```bash
|
|
223
|
+
curl -I --http2 https://your-app.com | grep -i "HTTP/2"
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Asset Compilation
|
|
227
|
+
|
|
228
|
+
### Verify Assets Compile Successfully
|
|
229
|
+
|
|
230
|
+
**Check manifest.json:**
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
# Development
|
|
234
|
+
cat public/packs/manifest.json | jq .
|
|
235
|
+
|
|
236
|
+
# Production (after precompile)
|
|
237
|
+
cat public/packs/manifest.json | jq '.entrypoints'
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**Expected output:**
|
|
241
|
+
|
|
242
|
+
```json
|
|
243
|
+
{
|
|
244
|
+
"entrypoints": {
|
|
245
|
+
"application": {
|
|
246
|
+
"assets": {
|
|
247
|
+
"js": [
|
|
248
|
+
"/packs/vendors~application-abc123.chunk.js",
|
|
249
|
+
"/packs/application-xyz789.js"
|
|
250
|
+
],
|
|
251
|
+
"css": ["/packs/application-abc123.chunk.css"]
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**Verify assets exist on disk:**
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
ls -lh public/packs/
|
|
262
|
+
# Should see .js, .css, .map files with hashed names
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Check HTML References
|
|
266
|
+
|
|
267
|
+
**View page source and verify pack tags:**
|
|
268
|
+
|
|
269
|
+
```html
|
|
270
|
+
<!-- Should see hashed filenames -->
|
|
271
|
+
<link rel="stylesheet" href="/packs/application-abc123.css" />
|
|
272
|
+
<script src="/packs/application-xyz789.js"></script>
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Code Splitting
|
|
276
|
+
|
|
277
|
+
### Verify Chunks Are Created
|
|
278
|
+
|
|
279
|
+
**Check manifest.json for chunks:**
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
cat public/packs/manifest.json | jq '.entrypoints.application.assets.js'
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Expected output (with code splitting):**
|
|
286
|
+
|
|
287
|
+
```json
|
|
288
|
+
[
|
|
289
|
+
"/packs/vendors~application-abc123.chunk.js", // Vendor chunk
|
|
290
|
+
"/packs/application-xyz789.js" // Main chunk
|
|
291
|
+
]
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**View Network tab in DevTools:**
|
|
295
|
+
|
|
296
|
+
- Should see multiple `.chunk.js` files loading
|
|
297
|
+
- Chunks load in order (vendors first, then application code)
|
|
298
|
+
|
|
299
|
+
## Subresource Integrity (SRI)
|
|
300
|
+
|
|
301
|
+
### Verify Integrity Attributes
|
|
302
|
+
|
|
303
|
+
**Enable SRI in config:**
|
|
304
|
+
|
|
305
|
+
```yaml
|
|
306
|
+
# config/shakapacker.yml
|
|
307
|
+
production:
|
|
308
|
+
integrity:
|
|
309
|
+
enabled: true
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Check manifest.json for integrity hashes:**
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
cat public/packs/manifest.json | jq '.application.js'
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**Expected output:**
|
|
319
|
+
|
|
320
|
+
```json
|
|
321
|
+
{
|
|
322
|
+
"src": "/packs/application-abc123.js",
|
|
323
|
+
"integrity": "sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
**Check HTML for integrity attribute:**
|
|
328
|
+
|
|
329
|
+
```html
|
|
330
|
+
<!-- View page source -->
|
|
331
|
+
<script
|
|
332
|
+
src="/packs/application-abc123.js"
|
|
333
|
+
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
|
|
334
|
+
crossorigin="anonymous"
|
|
335
|
+
></script>
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Verify SRI Works
|
|
339
|
+
|
|
340
|
+
**Break the integrity (for testing):**
|
|
341
|
+
|
|
342
|
+
1. Edit `public/packs/application-xyz789.js` (add a space)
|
|
343
|
+
2. Reload page
|
|
344
|
+
3. **Expected:** Browser console shows SRI error:
|
|
345
|
+
```
|
|
346
|
+
Failed to find a valid digest in the 'integrity' attribute
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Source Maps
|
|
350
|
+
|
|
351
|
+
### Verify Source Maps Generate
|
|
352
|
+
|
|
353
|
+
**Check for .map files:**
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
ls -lh public/packs/*.map
|
|
357
|
+
# Should see .js.map and .css.map files
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
**Check HTML references source maps:**
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
curl http://localhost:3000/packs/application-xyz789.js | tail -5
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
**Expected output:**
|
|
367
|
+
|
|
368
|
+
```javascript
|
|
369
|
+
//# sourceMappingURL=application-xyz789.js.map
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Verify Source Maps Work in DevTools
|
|
373
|
+
|
|
374
|
+
1. **Open DevTools** → Sources tab
|
|
375
|
+
2. **Find your source files** under `webpack://` or `src/`
|
|
376
|
+
3. **Set a breakpoint** in your original source code
|
|
377
|
+
4. **Trigger the code** - debugger should stop at your source, not compiled output
|
|
378
|
+
|
|
379
|
+
## Development Server
|
|
380
|
+
|
|
381
|
+
### Verify Dev Server Running
|
|
382
|
+
|
|
383
|
+
**Check server status:**
|
|
384
|
+
|
|
385
|
+
```bash
|
|
386
|
+
# Start dev server
|
|
387
|
+
./bin/shakapacker-dev-server
|
|
388
|
+
|
|
389
|
+
# In another terminal, check it's running
|
|
390
|
+
curl http://localhost:3035
|
|
391
|
+
# Should return: "Shakapacker is running"
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
**Check Rails connects to dev server:**
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
# Start Rails
|
|
398
|
+
./bin/dev # or rails server
|
|
399
|
+
|
|
400
|
+
# Check Rails log for:
|
|
401
|
+
[Shakapacker] Compiling...
|
|
402
|
+
[Shakapacker] Compiled all packs in /app/public/packs
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
**Verify hot reloading:**
|
|
406
|
+
|
|
407
|
+
1. Edit a JavaScript file in `app/javascript/`
|
|
408
|
+
2. Save the file
|
|
409
|
+
3. Browser should automatically reload (if HMR configured)
|
|
410
|
+
4. Or check terminal shows recompile message
|
|
411
|
+
|
|
412
|
+
### Troubleshooting Dev Server
|
|
413
|
+
|
|
414
|
+
**Connection refused?**
|
|
415
|
+
|
|
416
|
+
```bash
|
|
417
|
+
# Check dev_server.yml
|
|
418
|
+
cat config/shakapacker.yml | grep -A10 "dev_server"
|
|
419
|
+
|
|
420
|
+
# Verify settings:
|
|
421
|
+
# host: localhost
|
|
422
|
+
# port: 3035
|
|
423
|
+
# https: false
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
**Test connection manually:**
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
curl http://localhost:3035/packs/application.js
|
|
430
|
+
# Should return JavaScript code (not 404)
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
## Testing Checklist
|
|
434
|
+
|
|
435
|
+
Use this checklist to verify a complete Shakapacker setup:
|
|
436
|
+
|
|
437
|
+
- [ ] **Assets compile:** `bundle exec rails assets:precompile` succeeds
|
|
438
|
+
- [ ] **Manifest exists:** `public/packs/manifest.json` contains entrypoints
|
|
439
|
+
- [ ] **Assets load:** Page loads without 404s for pack files
|
|
440
|
+
- [ ] **Code splitting works:** Multiple chunks load in Network tab
|
|
441
|
+
- [ ] **Source maps work:** Can debug original source in DevTools
|
|
442
|
+
- [ ] **Dev server runs:** `./bin/shakapacker-dev-server` starts successfully
|
|
443
|
+
- [ ] **SRI enabled (if configured):** HTML contains `integrity` attributes
|
|
444
|
+
- [ ] **Early hints work (if configured):** DevTools shows 103 status
|
|
445
|
+
|
|
446
|
+
## Common Issues
|
|
447
|
+
|
|
448
|
+
### Assets Return 404
|
|
449
|
+
|
|
450
|
+
**Check manifest:**
|
|
451
|
+
|
|
452
|
+
```bash
|
|
453
|
+
cat public/packs/manifest.json | jq .
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
**Recompile:**
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
bundle exec rails assets:precompile
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Old Assets Cached
|
|
463
|
+
|
|
464
|
+
**Clear public/packs:**
|
|
465
|
+
|
|
466
|
+
```bash
|
|
467
|
+
rm -rf public/packs
|
|
468
|
+
bundle exec rails assets:precompile
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### Dev Server Won't Start
|
|
472
|
+
|
|
473
|
+
**Check port not in use:**
|
|
474
|
+
|
|
475
|
+
```bash
|
|
476
|
+
lsof -i :3035
|
|
477
|
+
# Kill process if needed
|
|
478
|
+
kill -9 <PID>
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
**Check dev_server config:**
|
|
482
|
+
|
|
483
|
+
```bash
|
|
484
|
+
cat config/shakapacker.yml | grep -A10 dev_server
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
## Additional Resources
|
|
488
|
+
|
|
489
|
+
- [Configuration Guide](configuration.md)
|
|
490
|
+
- [Early Hints Guide](early_hints.md)
|
|
491
|
+
- [Subresource Integrity Guide](subresource_integrity.md)
|
|
492
|
+
- [Troubleshooting Guide](troubleshooting.md)
|
|
@@ -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
|
{
|
|
@@ -86,11 +86,30 @@ default: &default
|
|
|
86
86
|
# https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity#cross-origin_resource_sharing_and_subresource_integrity
|
|
87
87
|
cross_origin: "anonymous"
|
|
88
88
|
|
|
89
|
+
# HTTP 103 Early Hints support for faster asset loading
|
|
90
|
+
# Sends Link headers to browsers so they can preload assets while Rails is rendering
|
|
91
|
+
# See docs/early_hints_new_api.md for setup instructions and requirements
|
|
92
|
+
# https://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-send_early_hints
|
|
93
|
+
early_hints:
|
|
94
|
+
enabled: false
|
|
95
|
+
# css: "preload" # 'preload' | 'prefetch' | 'none' - default: 'preload'
|
|
96
|
+
# js: "preload" # 'preload' | 'prefetch' | 'none' - default: 'preload'
|
|
97
|
+
# debug: false # Output early hints info as HTML comments for troubleshooting
|
|
98
|
+
|
|
89
99
|
development:
|
|
90
100
|
<<: *default
|
|
91
101
|
compile: true
|
|
92
102
|
compiler_strategy: mtime
|
|
93
103
|
|
|
104
|
+
# Early hints disabled by default in development
|
|
105
|
+
# To enable: Set enabled: true AND start Puma with: bundle exec puma --early-hints
|
|
106
|
+
# See docs/early_hints_new_api.md for setup instructions
|
|
107
|
+
# early_hints:
|
|
108
|
+
# enabled: true
|
|
109
|
+
# css: "preload"
|
|
110
|
+
# js: "preload"
|
|
111
|
+
# debug: true
|
|
112
|
+
|
|
94
113
|
# Reference: https://webpack.js.org/configuration/dev-server/
|
|
95
114
|
# Keys not described there are documented inline and in https://github.com/shakacode/shakapacker/
|
|
96
115
|
dev_server:
|
|
@@ -150,3 +169,11 @@ production:
|
|
|
150
169
|
|
|
151
170
|
# Cache manifest.json for performance
|
|
152
171
|
cache_manifest: true
|
|
172
|
+
|
|
173
|
+
# Early hints disabled by default. See docs/early_hints_new_api.md before enabling.
|
|
174
|
+
# Requires proper server configuration (Puma + HTTP/2 proxy like nginx, Thruster, or Control Plane)
|
|
175
|
+
early_hints:
|
|
176
|
+
enabled: false
|
|
177
|
+
# css: "preload"
|
|
178
|
+
# js: "preload"
|
|
179
|
+
# debug: false
|