shakapacker 8.4.0 → 9.0.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 +4 -4
- data/.eslintignore +1 -0
- data/.eslintrc.fast.js +40 -0
- data/.eslintrc.js +48 -0
- data/.github/STATUS.md +1 -0
- data/.github/workflows/claude-code-review.yml +54 -0
- data/.github/workflows/claude.yml +50 -0
- data/.github/workflows/dummy.yml +8 -4
- data/.github/workflows/generator.yml +17 -14
- data/.github/workflows/node.yml +23 -1
- data/.github/workflows/ruby.yml +11 -0
- data/.github/workflows/test-bundlers.yml +170 -0
- data/.gitignore +17 -0
- data/.husky/pre-commit +2 -0
- data/.npmignore +56 -0
- data/.prettierignore +3 -0
- data/.rubocop.yml +1 -0
- data/.yalcignore +26 -0
- data/CHANGELOG.md +156 -18
- data/CLAUDE.md +29 -0
- data/CONTRIBUTING.md +138 -20
- data/Gemfile.lock +3 -3
- data/README.md +130 -5
- data/Rakefile +39 -4
- data/TODO.md +50 -0
- data/TODO_v9.md +87 -0
- data/conductor-setup.sh +70 -0
- data/conductor.json +7 -0
- data/docs/cdn_setup.md +379 -0
- data/docs/css-modules-export-mode.md +512 -0
- data/docs/deployment.md +10 -1
- data/docs/optional-peer-dependencies.md +198 -0
- data/docs/peer-dependencies.md +60 -0
- data/docs/rspack.md +190 -0
- data/docs/rspack_migration_guide.md +202 -0
- data/docs/transpiler-migration.md +188 -0
- data/docs/transpiler-performance.md +179 -0
- data/docs/troubleshooting.md +5 -0
- data/docs/typescript-migration.md +378 -0
- data/docs/typescript.md +99 -0
- data/docs/using_esbuild_loader.md +3 -3
- data/docs/using_swc_loader.md +5 -3
- data/docs/v6_upgrade.md +10 -0
- data/docs/v9_upgrade.md +413 -0
- data/lib/install/bin/shakapacker +3 -5
- data/lib/install/config/rspack/rspack.config.js +6 -0
- data/lib/install/config/rspack/rspack.config.ts +7 -0
- data/lib/install/config/shakapacker.yml +12 -2
- data/lib/install/config/webpack/webpack.config.ts +7 -0
- data/lib/install/package.json +38 -0
- data/lib/install/template.rb +194 -44
- data/lib/shakapacker/configuration.rb +141 -0
- data/lib/shakapacker/dev_server_runner.rb +25 -5
- data/lib/shakapacker/doctor.rb +844 -0
- data/lib/shakapacker/manifest.rb +4 -2
- data/lib/shakapacker/rspack_runner.rb +19 -0
- data/lib/shakapacker/runner.rb +144 -4
- data/lib/shakapacker/swc_migrator.rb +376 -0
- data/lib/shakapacker/utils/manager.rb +2 -0
- data/lib/shakapacker/version.rb +1 -1
- data/lib/shakapacker/version_checker.rb +1 -1
- data/lib/shakapacker/webpack_runner.rb +4 -42
- data/lib/shakapacker.rb +2 -1
- data/lib/tasks/shakapacker/doctor.rake +8 -0
- data/lib/tasks/shakapacker/install.rake +12 -2
- data/lib/tasks/shakapacker/migrate_to_swc.rake +13 -0
- data/lib/tasks/shakapacker.rake +1 -0
- data/package/.npmignore +4 -0
- data/package/babel/preset.ts +56 -0
- data/package/config.ts +175 -0
- data/package/{dev_server.js → dev_server.ts} +8 -5
- data/package/env.ts +92 -0
- data/package/environments/base.ts +138 -0
- data/package/environments/development.ts +90 -0
- data/package/environments/production.ts +80 -0
- data/package/environments/test.ts +53 -0
- data/package/environments/types.ts +90 -0
- data/package/esbuild/index.ts +42 -0
- data/package/index.d.ts +3 -97
- data/package/index.ts +52 -0
- data/package/loaders.d.ts +28 -0
- data/package/optimization/rspack.ts +36 -0
- data/package/optimization/webpack.ts +57 -0
- data/package/plugins/rspack.ts +103 -0
- data/package/plugins/webpack.ts +62 -0
- data/package/rspack/index.ts +64 -0
- data/package/rules/{babel.js → babel.ts} +2 -2
- data/package/rules/{coffee.js → coffee.ts} +1 -1
- data/package/rules/css.ts +3 -0
- data/package/rules/{erb.js → erb.ts} +1 -1
- data/package/rules/esbuild.ts +10 -0
- data/package/rules/file.ts +40 -0
- data/package/rules/{jscommon.js → jscommon.ts} +4 -4
- data/package/rules/{less.js → less.ts} +4 -4
- data/package/rules/raw.ts +25 -0
- data/package/rules/rspack.ts +176 -0
- data/package/rules/{sass.js → sass.ts} +7 -3
- data/package/rules/{stylus.js → stylus.ts} +4 -8
- data/package/rules/swc.ts +10 -0
- data/package/rules/{index.js → webpack.ts} +1 -1
- data/package/swc/index.ts +54 -0
- data/package/types/README.md +87 -0
- data/package/types/index.ts +60 -0
- data/package/types.ts +108 -0
- data/package/utils/configPath.ts +6 -0
- data/package/utils/debug.ts +49 -0
- data/package/utils/defaultConfigPath.ts +4 -0
- data/package/utils/errorCodes.ts +219 -0
- data/package/utils/errorHelpers.ts +143 -0
- data/package/utils/getStyleRule.ts +64 -0
- data/package/utils/helpers.ts +85 -0
- data/package/utils/{inliningCss.js → inliningCss.ts} +3 -3
- data/package/utils/pathValidation.ts +139 -0
- data/package/utils/requireOrError.ts +15 -0
- data/package/utils/snakeToCamelCase.ts +5 -0
- data/package/utils/typeGuards.ts +342 -0
- data/package/utils/validateDependencies.ts +61 -0
- data/package/webpack-types.d.ts +33 -0
- data/package/webpackDevServerConfig.ts +117 -0
- data/package.json +134 -9
- data/scripts/remove-use-strict.js +45 -0
- data/scripts/type-check-no-emit.js +27 -0
- data/test/package/config.test.js +3 -0
- data/test/package/env.test.js +42 -7
- data/test/package/environments/base.test.js +5 -1
- data/test/package/rules/babel.test.js +16 -0
- data/test/package/rules/esbuild.test.js +1 -1
- data/test/package/rules/raw.test.js +40 -7
- data/test/package/rules/swc.test.js +1 -1
- data/test/package/rules/webpack.test.js +35 -0
- data/test/package/staging.test.js +4 -3
- data/test/package/transpiler-defaults.test.js +127 -0
- data/test/peer-dependencies.sh +85 -0
- data/test/scripts/remove-use-strict.test.js +125 -0
- data/test/typescript/build.test.js +118 -0
- data/test/typescript/environments.test.js +107 -0
- data/test/typescript/pathValidation.test.js +142 -0
- data/test/typescript/securityValidation.test.js +182 -0
- data/tools/README.md +124 -0
- data/tools/css-modules-v9-codemod.js +179 -0
- data/tsconfig.eslint.json +16 -0
- data/tsconfig.json +38 -0
- data/yarn.lock +2704 -767
- metadata +111 -41
- data/package/babel/preset.js +0 -48
- data/package/config.js +0 -56
- data/package/env.js +0 -48
- data/package/environments/base.js +0 -171
- data/package/environments/development.js +0 -13
- data/package/environments/production.js +0 -88
- data/package/environments/test.js +0 -3
- data/package/esbuild/index.js +0 -40
- data/package/index.js +0 -40
- data/package/rules/css.js +0 -3
- data/package/rules/esbuild.js +0 -10
- data/package/rules/file.js +0 -29
- data/package/rules/raw.js +0 -5
- data/package/rules/swc.js +0 -10
- data/package/swc/index.js +0 -50
- data/package/utils/configPath.js +0 -4
- data/package/utils/defaultConfigPath.js +0 -2
- data/package/utils/getStyleRule.js +0 -40
- data/package/utils/helpers.js +0 -62
- data/package/utils/snakeToCamelCase.js +0 -5
- data/package/webpackDevServerConfig.js +0 -71
- data/test/package/rules/index.test.js +0 -16
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
# CSS Modules Export Mode
|
|
2
|
+
|
|
3
|
+
## Version 9.x (Current Default Behavior)
|
|
4
|
+
|
|
5
|
+
Starting with Shakapacker v9, CSS Modules are configured with **named exports** (`namedExport: true`) by default to align with Next.js and modern tooling standards.
|
|
6
|
+
|
|
7
|
+
### JavaScript Usage
|
|
8
|
+
|
|
9
|
+
In pure JavaScript projects, you can use true named imports:
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
// v9 - named exports in JavaScript
|
|
13
|
+
import { bright, container } from './Foo.module.css';
|
|
14
|
+
<button className={bright} />
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### TypeScript Usage
|
|
18
|
+
|
|
19
|
+
TypeScript cannot statically analyze CSS files to determine the exact export names at compile time. When css-loader generates individual named exports dynamically from your CSS classes, TypeScript doesn't know what those exports will be. Therefore, you must use namespace imports:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
// v9 - namespace import required for TypeScript
|
|
23
|
+
import * as styles from './Foo.module.css';
|
|
24
|
+
<button className={styles.bright} />
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Why namespace imports?** While webpack's css-loader generates true named exports at runtime (with `namedExport: true`), TypeScript's type system cannot determine these dynamic exports during compilation. The namespace import pattern allows TypeScript to treat the import as an object with string keys, bypassing the need for static export validation while still benefiting from the runtime optimizations of named exports.
|
|
28
|
+
|
|
29
|
+
### Benefits of v9 Configuration
|
|
30
|
+
- Eliminates certain webpack warnings
|
|
31
|
+
- Provides better tree-shaking potential
|
|
32
|
+
- Aligns with modern JavaScript module standards
|
|
33
|
+
- Automatically converts kebab-case to camelCase (`my-button` → `myButton`)
|
|
34
|
+
|
|
35
|
+
### Important: exportLocalsConvention with namedExport
|
|
36
|
+
|
|
37
|
+
When `namedExport: true` is enabled (v9 default), css-loader requires `exportLocalsConvention` to be either `'camelCaseOnly'` or `'dashesOnly'`.
|
|
38
|
+
|
|
39
|
+
**The following will cause a build error:**
|
|
40
|
+
```js
|
|
41
|
+
modules: {
|
|
42
|
+
namedExport: true,
|
|
43
|
+
exportLocalsConvention: 'camelCase' // ❌ ERROR: incompatible with namedExport: true
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Error message:**
|
|
48
|
+
```
|
|
49
|
+
"exportLocalsConvention" with "camelCase" value is incompatible with "namedExport: true" option
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Correct v9 configuration:**
|
|
53
|
+
```js
|
|
54
|
+
modules: {
|
|
55
|
+
namedExport: true,
|
|
56
|
+
exportLocalsConvention: 'camelCaseOnly' // ✅ Correct - only camelCase exported
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**exportLocalsConvention options with namedExport:**
|
|
61
|
+
|
|
62
|
+
When `namedExport: true`, you can use:
|
|
63
|
+
- `'camelCaseOnly'` (v9 default): Exports ONLY the camelCase version (e.g., only `myButton`)
|
|
64
|
+
- `'dashesOnly'`: Exports ONLY the original kebab-case version (e.g., only `my-button`)
|
|
65
|
+
|
|
66
|
+
**Not compatible with namedExport: true:**
|
|
67
|
+
- `'camelCase'`: Exports both versions (both `my-button` and `myButton`) - only works with `namedExport: false` (v8 behavior)
|
|
68
|
+
|
|
69
|
+
**Configuration Quick Reference:**
|
|
70
|
+
|
|
71
|
+
| namedExport | exportLocalsConvention | `.my-button` exports | Use Case | Compatible? |
|
|
72
|
+
|-------------|------------------------|---------------------|----------|-------------|
|
|
73
|
+
| `true` | `'camelCaseOnly'` | `myButton` | JavaScript conventions | ✅ Valid |
|
|
74
|
+
| `true` | `'dashesOnly'` | `'my-button'` | Preserve CSS naming | ✅ Valid |
|
|
75
|
+
| `false` | `'camelCase'` | Both `myButton` AND `'my-button'` | v8 compatibility | ✅ Valid |
|
|
76
|
+
| `false` | `'asIs'` | `'my-button'` | No transformation | ✅ Valid |
|
|
77
|
+
| `true` | `'camelCase'` | - | - | ❌ Build Error |
|
|
78
|
+
|
|
79
|
+
**When to use each option:**
|
|
80
|
+
- Use `'camelCaseOnly'` if you prefer standard JavaScript naming conventions
|
|
81
|
+
- Use `'dashesOnly'` if you want to preserve your CSS class names exactly as written
|
|
82
|
+
- Use `'camelCase'` (with `namedExport: false`) only if you need both versions available
|
|
83
|
+
|
|
84
|
+
## Version 8.x and Earlier Behavior
|
|
85
|
+
|
|
86
|
+
In Shakapacker v8 and earlier, the default behavior was to use a **default export object**:
|
|
87
|
+
|
|
88
|
+
```js
|
|
89
|
+
// v8 and earlier default
|
|
90
|
+
import styles from './Foo.module.css';
|
|
91
|
+
<button className={styles.bright} />
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Migrating from v8 to v9
|
|
97
|
+
|
|
98
|
+
When upgrading to Shakapacker v9, you'll need to update your CSS Module imports from default exports to named exports.
|
|
99
|
+
|
|
100
|
+
### Migration Options
|
|
101
|
+
|
|
102
|
+
#### Option 1: Update Your Code (Recommended)
|
|
103
|
+
|
|
104
|
+
**For JavaScript projects:**
|
|
105
|
+
```js
|
|
106
|
+
// Before (v8)
|
|
107
|
+
import styles from './Component.module.css';
|
|
108
|
+
<div className={styles.container}>
|
|
109
|
+
<button className={styles.button}>Click me</button>
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
// After (v9) - JavaScript
|
|
113
|
+
import { container, button } from './Component.module.css';
|
|
114
|
+
<div className={container}>
|
|
115
|
+
<button className={button}>Click me</button>
|
|
116
|
+
</div>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**For TypeScript projects:**
|
|
120
|
+
```typescript
|
|
121
|
+
// Before (v8)
|
|
122
|
+
import styles from './Component.module.css';
|
|
123
|
+
<div className={styles.container}>
|
|
124
|
+
<button className={styles.button}>Click me</button>
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
// After (v9) - TypeScript
|
|
128
|
+
import * as styles from './Component.module.css';
|
|
129
|
+
<div className={styles.container}>
|
|
130
|
+
<button className={styles.button}>Click me</button>
|
|
131
|
+
</div>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Note: TypeScript projects only need to change from default import to namespace import (`* as styles`), the property access remains the same.
|
|
135
|
+
|
|
136
|
+
#### Option 2: Keep v8 Behavior
|
|
137
|
+
|
|
138
|
+
If you prefer to keep the v8 default export behavior during migration, you can override the configuration (see below).
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Reverting to Default Exports (v8 Behavior)
|
|
143
|
+
|
|
144
|
+
To use the v8-style default exports instead of v9's named exports:
|
|
145
|
+
|
|
146
|
+
### Option 1: Update `config/webpack/commonWebpackConfig.js` (Recommended)
|
|
147
|
+
|
|
148
|
+
This approach modifies the common webpack configuration that applies to all environments:
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
// config/webpack/commonWebpackConfig.js
|
|
152
|
+
const { generateWebpackConfig, merge } = require('shakapacker');
|
|
153
|
+
|
|
154
|
+
const baseClientWebpackConfig = generateWebpackConfig();
|
|
155
|
+
|
|
156
|
+
// Override CSS Modules configuration to use v8-style default exports
|
|
157
|
+
const overrideCssModulesConfig = (config) => {
|
|
158
|
+
// Find the CSS rule in the module rules
|
|
159
|
+
const cssRule = config.module.rules.find(rule =>
|
|
160
|
+
rule.test && rule.test.toString().includes('css')
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
if (cssRule && cssRule.use) {
|
|
164
|
+
const cssLoaderUse = cssRule.use.find(use =>
|
|
165
|
+
use.loader && use.loader.includes('css-loader')
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
if (cssLoaderUse && cssLoaderUse.options && cssLoaderUse.options.modules) {
|
|
169
|
+
// Override v9 default to use v8-style default exports
|
|
170
|
+
cssLoaderUse.options.modules.namedExport = false;
|
|
171
|
+
cssLoaderUse.options.modules.exportLocalsConvention = 'asIs';
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return config;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const commonOptions = {
|
|
179
|
+
resolve: {
|
|
180
|
+
extensions: ['.css', '.ts', '.tsx'],
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const commonWebpackConfig = () => {
|
|
185
|
+
const config = merge({}, baseClientWebpackConfig, commonOptions);
|
|
186
|
+
return overrideCssModulesConfig(config);
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
module.exports = commonWebpackConfig;
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Option 2: Create `config/webpack/environment.js` (Alternative)
|
|
193
|
+
|
|
194
|
+
If you prefer using a separate environment file:
|
|
195
|
+
|
|
196
|
+
```js
|
|
197
|
+
// config/webpack/environment.js
|
|
198
|
+
const { environment } = require('@shakacode/shakapacker');
|
|
199
|
+
const getStyleRule = require('@shakacode/shakapacker/package/utils/getStyleRule');
|
|
200
|
+
|
|
201
|
+
// CSS Modules rule for *.module.css with v8-style default export
|
|
202
|
+
const cssModulesRule = getStyleRule(/\.module\.css$/i, [], {
|
|
203
|
+
sourceMap: true,
|
|
204
|
+
importLoaders: 2,
|
|
205
|
+
modules: {
|
|
206
|
+
auto: true,
|
|
207
|
+
namedExport: false, // <-- override v9 default
|
|
208
|
+
exportLocalsConvention: 'asIs' // keep class names as-is instead of camelCase
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Ensure this rule wins for *.module.css
|
|
213
|
+
if (cssModulesRule) {
|
|
214
|
+
environment.loaders.prepend('css-modules', cssModulesRule);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Plain CSS rule for non-modules
|
|
218
|
+
const plainCssRule = getStyleRule(/(?<!\.module)\.css$/i, [], {
|
|
219
|
+
sourceMap: true,
|
|
220
|
+
importLoaders: 2,
|
|
221
|
+
modules: false
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
if (plainCssRule) {
|
|
225
|
+
environment.loaders.append('css', plainCssRule);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
module.exports = environment;
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Then reference this in your environment-specific configs (development.js, production.js, etc.).
|
|
232
|
+
|
|
233
|
+
### Option 3: (Optional) Sass Modules
|
|
234
|
+
|
|
235
|
+
If you also use Sass modules, add similar configuration for SCSS files:
|
|
236
|
+
|
|
237
|
+
```js
|
|
238
|
+
// For Option 1 approach, extend the overrideCssModulesConfig function:
|
|
239
|
+
const overrideCssModulesConfig = (config) => {
|
|
240
|
+
// Handle both CSS and SCSS rules
|
|
241
|
+
const styleRules = config.module.rules.filter(rule =>
|
|
242
|
+
rule.test && (rule.test.toString().includes('css') || rule.test.toString().includes('scss'))
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
styleRules.forEach(rule => {
|
|
246
|
+
if (rule.use) {
|
|
247
|
+
const cssLoaderUse = rule.use.find(use =>
|
|
248
|
+
use.loader && use.loader.includes('css-loader')
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
if (cssLoaderUse && cssLoaderUse.options && cssLoaderUse.options.modules) {
|
|
252
|
+
cssLoaderUse.options.modules.namedExport = false;
|
|
253
|
+
cssLoaderUse.options.modules.exportLocalsConvention = 'asIs';
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
return config;
|
|
259
|
+
};
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Detailed Migration Guide
|
|
265
|
+
|
|
266
|
+
### Migrating from v8 (Default Exports) to v9 (Named Exports)
|
|
267
|
+
|
|
268
|
+
#### 1. Update Import Statements
|
|
269
|
+
|
|
270
|
+
```js
|
|
271
|
+
// Old (v8 - default export)
|
|
272
|
+
import styles from './Component.module.css';
|
|
273
|
+
|
|
274
|
+
// New (v9 - named exports)
|
|
275
|
+
import { bright, container, button } from './Component.module.css';
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
#### 2. Update Class References
|
|
279
|
+
|
|
280
|
+
```js
|
|
281
|
+
// Old (v8)
|
|
282
|
+
<div className={styles.container}>
|
|
283
|
+
<button className={styles.button}>Click me</button>
|
|
284
|
+
<span className={styles.bright}>Highlighted text</span>
|
|
285
|
+
</div>
|
|
286
|
+
|
|
287
|
+
// New (v9)
|
|
288
|
+
<div className={container}>
|
|
289
|
+
<button className={button}>Click me</button>
|
|
290
|
+
<span className={bright}>Highlighted text</span>
|
|
291
|
+
</div>
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
#### 3. Handle Kebab-Case Class Names
|
|
295
|
+
|
|
296
|
+
**Option A: Use camelCase (v9 default)**
|
|
297
|
+
|
|
298
|
+
With `exportLocalsConvention: 'camelCaseOnly'`, kebab-case class names are automatically converted:
|
|
299
|
+
|
|
300
|
+
```css
|
|
301
|
+
/* styles.module.css */
|
|
302
|
+
.my-button { ... }
|
|
303
|
+
.primary-color { ... }
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
```js
|
|
307
|
+
// v9 default - camelCase conversion
|
|
308
|
+
import { myButton, primaryColor } from './styles.module.css';
|
|
309
|
+
<button className={myButton} />
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Option B: Keep kebab-case with 'dashesOnly'**
|
|
313
|
+
|
|
314
|
+
If you prefer to preserve the original kebab-case names, configure your webpack to use `'dashesOnly'`:
|
|
315
|
+
|
|
316
|
+
```js
|
|
317
|
+
// config/webpack/commonWebpackConfig.js
|
|
318
|
+
modules: {
|
|
319
|
+
namedExport: true,
|
|
320
|
+
exportLocalsConvention: 'dashesOnly'
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
```js
|
|
325
|
+
// With dashesOnly - preserve kebab-case
|
|
326
|
+
import * as styles from './styles.module.css';
|
|
327
|
+
<button className={styles['my-button']} />
|
|
328
|
+
|
|
329
|
+
// Or with aliasing:
|
|
330
|
+
import { 'my-button': myButton } from './styles.module.css';
|
|
331
|
+
<button className={myButton} />
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**Note:** With both `'camelCaseOnly'` and `'dashesOnly'`, only one version of each class name is exported. The original kebab-case name is NOT available with `'camelCaseOnly'`, and the camelCase version is NOT available with `'dashesOnly'`.
|
|
335
|
+
|
|
336
|
+
#### 4. Using a Codemod for Large Codebases
|
|
337
|
+
|
|
338
|
+
For large codebases, you can create a codemod to automate the migration:
|
|
339
|
+
|
|
340
|
+
```js
|
|
341
|
+
// css-modules-v9-migration.js
|
|
342
|
+
module.exports = function(fileInfo, api) {
|
|
343
|
+
const j = api.jscodeshift;
|
|
344
|
+
const root = j(fileInfo.source);
|
|
345
|
+
|
|
346
|
+
// Find CSS module imports
|
|
347
|
+
root.find(j.ImportDeclaration, {
|
|
348
|
+
source: { value: value => value.endsWith('.module.css') }
|
|
349
|
+
}).forEach(path => {
|
|
350
|
+
const defaultSpecifier = path.node.specifiers.find(
|
|
351
|
+
spec => spec.type === 'ImportDefaultSpecifier'
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
if (defaultSpecifier) {
|
|
355
|
+
// Convert default import to namespace import for analysis
|
|
356
|
+
// Then extract used properties and convert to named imports
|
|
357
|
+
// ... codemod implementation
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
return root.toSource();
|
|
362
|
+
};
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Run with:
|
|
366
|
+
```bash
|
|
367
|
+
npx jscodeshift -t css-modules-v9-migration.js src/
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## Version Comparison
|
|
373
|
+
|
|
374
|
+
| Feature | v8 (and earlier) | v9 |
|
|
375
|
+
|---------|-----------------|----|
|
|
376
|
+
| Default behavior | Default export object | Named exports |
|
|
377
|
+
| Import syntax | `import styles from '...'` | `import { className } from '...'` |
|
|
378
|
+
| Class reference | `styles.className` | `className` |
|
|
379
|
+
| Export convention | `asIs` (no transformation) | `camelCaseOnly` |
|
|
380
|
+
| TypeScript warnings | May show warnings | No warnings |
|
|
381
|
+
| Tree-shaking | Limited | Optimized |
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
## Benefits of Named Exports (v9 Default)
|
|
386
|
+
|
|
387
|
+
1. **No Build Warnings**: Eliminates webpack/TypeScript warnings about missing exports
|
|
388
|
+
2. **Better Tree-Shaking**: Unused CSS class exports can be eliminated
|
|
389
|
+
3. **Explicit Dependencies**: Clear about which CSS classes are being used
|
|
390
|
+
4. **Modern Standards**: Aligns with ES modules and modern tooling
|
|
391
|
+
5. **Type Safety**: TypeScript can validate individual class imports
|
|
392
|
+
|
|
393
|
+
## Benefits of Default Exports (v8 Behavior)
|
|
394
|
+
|
|
395
|
+
1. **Familiar Pattern**: Matches most existing React tutorials
|
|
396
|
+
2. **Namespace Import**: All classes available under one import
|
|
397
|
+
3. **Less Verbose**: Single import for all classes
|
|
398
|
+
4. **Legacy Compatibility**: Works with existing codebases
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## Verifying the Configuration
|
|
403
|
+
|
|
404
|
+
### 1. Rebuild Your Packs
|
|
405
|
+
|
|
406
|
+
After making any configuration changes, rebuild your webpack bundles:
|
|
407
|
+
|
|
408
|
+
```bash
|
|
409
|
+
# For development
|
|
410
|
+
NODE_ENV=development bin/shakapacker
|
|
411
|
+
|
|
412
|
+
# Or with the dev server
|
|
413
|
+
bin/shakapacker-dev-server
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### 2. Test in Your React Component
|
|
417
|
+
|
|
418
|
+
Verify your imports work correctly:
|
|
419
|
+
|
|
420
|
+
```js
|
|
421
|
+
// v9 default (named exports)
|
|
422
|
+
import { bright } from './Foo.module.css';
|
|
423
|
+
console.log(bright); // 'Foo_bright__hash'
|
|
424
|
+
|
|
425
|
+
// Or if using v8 configuration (default export)
|
|
426
|
+
import styles from './Foo.module.css';
|
|
427
|
+
console.log(styles); // { bright: 'Foo_bright__hash' }
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### 3. Debug Webpack Configuration (Optional)
|
|
431
|
+
|
|
432
|
+
To inspect the final webpack configuration:
|
|
433
|
+
|
|
434
|
+
```bash
|
|
435
|
+
NODE_ENV=development bin/shakapacker --profile --json > /tmp/webpack-stats.json
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
Then search for `css-loader` options in the generated JSON file.
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
## Troubleshooting
|
|
443
|
+
|
|
444
|
+
### Build Error: exportLocalsConvention Incompatible with namedExport
|
|
445
|
+
|
|
446
|
+
If you see this error during build:
|
|
447
|
+
```
|
|
448
|
+
"exportLocalsConvention" with "camelCase" value is incompatible with "namedExport: true" option
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
**Cause:** Your webpack configuration has `namedExport: true` with `exportLocalsConvention: 'camelCase'`.
|
|
452
|
+
|
|
453
|
+
**Solution:** Change `exportLocalsConvention` to `'camelCaseOnly'` or `'dashesOnly'`:
|
|
454
|
+
|
|
455
|
+
```js
|
|
456
|
+
// config/webpack/commonWebpackConfig.js or similar
|
|
457
|
+
modules: {
|
|
458
|
+
namedExport: true,
|
|
459
|
+
exportLocalsConvention: 'camelCaseOnly' // or 'dashesOnly'
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
Alternatively, if you need the `'camelCase'` option (both original and camelCase exports), you must revert to v8 behavior by setting `namedExport: false` as shown in the "Reverting to Default Exports" section above.
|
|
464
|
+
|
|
465
|
+
### CSS Classes Not Applying
|
|
466
|
+
|
|
467
|
+
If your CSS classes aren't applying after the upgrade:
|
|
468
|
+
|
|
469
|
+
1. **Check import syntax**: Ensure you're using the correct import style for your configuration
|
|
470
|
+
2. **Verify class names**: Use `console.log` to see available classes
|
|
471
|
+
3. **Check camelCase conversion**: Kebab-case names are converted to camelCase in v9 with `'camelCaseOnly'`
|
|
472
|
+
4. **Rebuild webpack**: Clear cache and rebuild: `rm -rf tmp/cache && bin/shakapacker`
|
|
473
|
+
|
|
474
|
+
### TypeScript Support
|
|
475
|
+
|
|
476
|
+
#### For v9 (Named Exports)
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
// src/types/css-modules.d.ts
|
|
480
|
+
declare module '*.module.css' {
|
|
481
|
+
const classes: { [key: string]: string };
|
|
482
|
+
export = classes;
|
|
483
|
+
}
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
#### For v8 Behavior (Default Export)
|
|
487
|
+
|
|
488
|
+
```typescript
|
|
489
|
+
// src/types/css-modules.d.ts
|
|
490
|
+
declare module '*.module.css' {
|
|
491
|
+
const classes: { [key: string]: string };
|
|
492
|
+
export default classes;
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
### Build Performance
|
|
497
|
+
|
|
498
|
+
The configuration changes should not impact build performance significantly. If you experience issues:
|
|
499
|
+
|
|
500
|
+
1. Check webpack stats: `bin/shakapacker --profile`
|
|
501
|
+
2. Verify only necessary rules are being modified
|
|
502
|
+
3. Consider using webpack bundle analyzer for deeper insights
|
|
503
|
+
|
|
504
|
+
---
|
|
505
|
+
|
|
506
|
+
## Summary
|
|
507
|
+
|
|
508
|
+
- **v9 default**: Named exports with camelCase conversion
|
|
509
|
+
- **v8 default**: Default export object with no conversion
|
|
510
|
+
- **Migration path**: Update imports or override configuration
|
|
511
|
+
- **Benefits of v9**: No warnings, better tree-shaking, explicit dependencies
|
|
512
|
+
- **Keeping v8 behavior**: Override css-loader configuration as shown above
|
data/docs/deployment.md
CHANGED
|
@@ -88,7 +88,16 @@ Now, you can set `brotli_static on;` in your nginx site config, as per the confi
|
|
|
88
88
|
|
|
89
89
|
## CDN
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
Shakapacker supports serving JavaScript bundles and assets from a CDN. For a comprehensive guide on setting up CDN with Shakapacker, including CloudFlare configuration, troubleshooting, and advanced setups, see the [CDN Setup Guide](cdn_setup.md).
|
|
92
|
+
|
|
93
|
+
**Quick Setup**: Set the `SHAKAPACKER_ASSET_HOST` environment variable before compiling assets:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
export SHAKAPACKER_ASSET_HOST=https://cdn.example.com
|
|
97
|
+
RAILS_ENV=production bundle exec rails assets:precompile
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Note: Shakapacker does NOT use the `ASSET_HOST` environment variable. You must use `SHAKAPACKER_ASSET_HOST` instead (`WEBPACKER_ASSET_HOST` if using Shakapacker before v7).
|
|
92
101
|
|
|
93
102
|
## Capistrano
|
|
94
103
|
|