shakapacker 9.3.0 → 9.3.2
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/.claude/commands/update-changelog.md +232 -0
- data/.github/actionlint-matcher.json +17 -0
- data/.github/workflows/dummy.yml +9 -0
- data/.github/workflows/generator.yml +13 -0
- data/.github/workflows/node.yml +83 -0
- data/.github/workflows/ruby.yml +11 -0
- data/.github/workflows/test-bundlers.yml +10 -0
- data/CHANGELOG.md +24 -9
- data/CLAUDE.md +6 -10
- data/CONTRIBUTING.md +44 -0
- data/Gemfile.lock +1 -1
- data/README.md +74 -44
- data/docs/api-reference.md +519 -0
- data/docs/cdn_setup.md +3 -3
- data/docs/common-upgrades.md +6 -7
- data/docs/configuration.md +14 -8
- data/docs/css-modules-export-mode.md +40 -6
- data/docs/deployment.md +3 -3
- data/docs/early_hints_manual_api.md +1 -1
- data/docs/feature_testing.md +3 -3
- data/docs/optional-peer-dependencies.md +2 -2
- data/docs/precompile_hook.md +15 -15
- data/docs/react.md +1 -1
- data/docs/releasing.md +7 -7
- data/docs/rspack_migration_guide.md +8 -14
- data/docs/transpiler-migration.md +12 -9
- data/docs/troubleshooting.md +3 -3
- data/docs/using_swc_loader.md +13 -10
- data/docs/v6_upgrade.md +2 -2
- data/docs/v9_upgrade.md +78 -3
- data/eslint.config.fast.js +120 -8
- data/eslint.config.js +50 -31
- data/lib/install/config/shakapacker.yml +14 -1
- data/lib/shakapacker/bundler_switcher.rb +83 -18
- data/lib/shakapacker/configuration.rb +47 -4
- data/lib/shakapacker/dev_server_runner.rb +4 -0
- data/lib/shakapacker/doctor.rb +7 -7
- data/lib/shakapacker/runner.rb +1 -1
- data/lib/shakapacker/swc_migrator.rb +2 -2
- data/lib/shakapacker/version.rb +1 -1
- data/lib/tasks/shakapacker/binstubs.rake +4 -2
- data/lib/tasks/shakapacker/check_binstubs.rake +2 -2
- data/lib/tasks/shakapacker/doctor.rake +3 -3
- data/lib/tasks/shakapacker/export_bundler_config.rake +5 -9
- data/lib/tasks/shakapacker/install.rake +4 -2
- data/lib/tasks/shakapacker/switch_bundler.rake +30 -40
- data/package/config.ts +2 -3
- data/package/configExporter/cli.ts +29 -24
- data/package/dev_server.ts +2 -2
- data/package/env.ts +1 -1
- data/package/environments/__type-tests__/rspack-plugin-compatibility.ts +6 -6
- data/package/environments/base.ts +2 -2
- data/package/environments/development.ts +3 -6
- data/package/environments/production.ts +2 -2
- data/package/environments/test.ts +2 -1
- data/package/esbuild/index.ts +0 -2
- data/package/index.d.ts +1 -0
- data/package/index.d.ts.template +1 -0
- data/package/index.ts +0 -1
- data/package/loaders.d.ts +1 -1
- data/package/plugins/rspack.ts +3 -1
- data/package/plugins/webpack.ts +5 -3
- data/package/rspack/index.ts +3 -3
- data/package/rules/file.ts +1 -1
- data/package/rules/raw.ts +3 -1
- data/package/rules/rspack.ts +0 -2
- data/package/rules/sass.ts +0 -2
- data/package/rules/webpack.ts +0 -1
- data/package/swc/index.ts +0 -2
- data/package/types.ts +8 -11
- data/package/utils/debug.ts +0 -4
- data/package/utils/getStyleRule.ts +17 -9
- data/package/utils/helpers.ts +8 -3
- data/package/utils/pathValidation.ts +10 -11
- data/package/utils/requireOrError.ts +4 -3
- data/package/webpack-types.d.ts +1 -1
- data/package/webpackDevServerConfig.ts +4 -4
- data/package.json +3 -3
- data/test/package/transpiler-defaults.test.js +42 -0
- data/yarn.lock +1 -1
- metadata +5 -2
data/eslint.config.fast.js
CHANGED
|
@@ -17,11 +17,12 @@ module.exports = [
|
|
|
17
17
|
// Global ignores (replaces .eslintignore)
|
|
18
18
|
{
|
|
19
19
|
ignores: [
|
|
20
|
-
"lib/**",
|
|
21
|
-
"**/node_modules/**",
|
|
22
|
-
"vendor/**",
|
|
23
|
-
"spec/**",
|
|
24
|
-
"package
|
|
20
|
+
"lib/**", // Ruby files, not JavaScript
|
|
21
|
+
"**/node_modules/**", // Third-party dependencies
|
|
22
|
+
"vendor/**", // Vendored dependencies
|
|
23
|
+
"spec/**", // Ruby specs, not JavaScript
|
|
24
|
+
"package/**/*.js", // Generated/compiled JavaScript from TypeScript
|
|
25
|
+
"package/**/*.d.ts" // Generated TypeScript declaration files
|
|
25
26
|
]
|
|
26
27
|
},
|
|
27
28
|
|
|
@@ -63,7 +64,11 @@ module.exports = [
|
|
|
63
64
|
"import/no-extraneous-dependencies": "off",
|
|
64
65
|
// TypeScript handles extensions, not needed for JS imports
|
|
65
66
|
"import/extensions": "off",
|
|
66
|
-
indent: ["error", 2]
|
|
67
|
+
indent: ["error", 2],
|
|
68
|
+
// Allow for...of loops - modern JS syntax
|
|
69
|
+
"no-restricted-syntax": "off",
|
|
70
|
+
// Allow console statements - used for debugging/logging throughout
|
|
71
|
+
"no-console": "off"
|
|
67
72
|
},
|
|
68
73
|
settings: {
|
|
69
74
|
react: {
|
|
@@ -130,13 +135,120 @@ module.exports = [
|
|
|
130
135
|
"@typescript-eslint/no-use-before-define": ["error"],
|
|
131
136
|
"@typescript-eslint/no-unused-vars": [
|
|
132
137
|
"error",
|
|
133
|
-
{ argsIgnorePattern: "^_" }
|
|
138
|
+
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" }
|
|
134
139
|
],
|
|
135
140
|
"@typescript-eslint/no-explicit-any": "error",
|
|
136
|
-
"@typescript-eslint/explicit-module-boundary-types": "off"
|
|
141
|
+
"@typescript-eslint/explicit-module-boundary-types": "off",
|
|
142
|
+
"no-undef": "off"
|
|
137
143
|
}
|
|
138
144
|
},
|
|
139
145
|
|
|
146
|
+
// Global rule for all TypeScript files in package/
|
|
147
|
+
// Suppress require() imports - these are intentional for CommonJS compatibility
|
|
148
|
+
// Will be addressed in Phase 3 (breaking changes) - see #708
|
|
149
|
+
{
|
|
150
|
+
files: ["package/**/*.ts"],
|
|
151
|
+
rules: {
|
|
152
|
+
"@typescript-eslint/no-require-imports": "off",
|
|
153
|
+
"global-require": "off",
|
|
154
|
+
"import/no-import-module-exports": "off"
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
// Consolidated override for package/config.ts and package/babel/preset.ts
|
|
159
|
+
{
|
|
160
|
+
files: ["package/babel/preset.ts", "package/config.ts"],
|
|
161
|
+
rules: {
|
|
162
|
+
"@typescript-eslint/no-unused-vars": "off",
|
|
163
|
+
"import/order": "off",
|
|
164
|
+
"import/newline-after-import": "off",
|
|
165
|
+
"import/first": "off",
|
|
166
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
167
|
+
"no-useless-escape": "off"
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
// configExporter module overrides
|
|
172
|
+
{
|
|
173
|
+
files: ["package/configExporter/**/*.ts"],
|
|
174
|
+
rules: {
|
|
175
|
+
"@typescript-eslint/no-use-before-define": "off",
|
|
176
|
+
"import/no-dynamic-require": "off",
|
|
177
|
+
"class-methods-use-this": "off",
|
|
178
|
+
"import/prefer-default-export": "off",
|
|
179
|
+
"no-underscore-dangle": "off",
|
|
180
|
+
"no-restricted-globals": "off",
|
|
181
|
+
"@typescript-eslint/no-unused-vars": "off",
|
|
182
|
+
"@typescript-eslint/no-explicit-any": "off"
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
// Utils module overrides
|
|
187
|
+
{
|
|
188
|
+
files: [
|
|
189
|
+
"package/utils/inliningCss.ts",
|
|
190
|
+
"package/utils/errorCodes.ts",
|
|
191
|
+
"package/utils/errorHelpers.ts",
|
|
192
|
+
"package/utils/pathValidation.ts",
|
|
193
|
+
"package/utils/getStyleRule.ts",
|
|
194
|
+
"package/utils/helpers.ts",
|
|
195
|
+
"package/utils/validateDependencies.ts",
|
|
196
|
+
"package/webpackDevServerConfig.ts"
|
|
197
|
+
],
|
|
198
|
+
rules: {
|
|
199
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
200
|
+
"no-useless-escape": "off"
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
// Plugins and optimization overrides
|
|
205
|
+
{
|
|
206
|
+
files: ["package/plugins/**/*.ts", "package/optimization/**/*.ts"],
|
|
207
|
+
rules: {
|
|
208
|
+
"import/prefer-default-export": "off"
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
// Rules, rspack, swc, esbuild, and other modules
|
|
213
|
+
{
|
|
214
|
+
files: [
|
|
215
|
+
"package/index.ts",
|
|
216
|
+
"package/rspack/index.ts",
|
|
217
|
+
"package/rules/**/*.ts",
|
|
218
|
+
"package/swc/index.ts",
|
|
219
|
+
"package/esbuild/index.ts",
|
|
220
|
+
"package/dev_server.ts",
|
|
221
|
+
"package/env.ts"
|
|
222
|
+
],
|
|
223
|
+
rules: {
|
|
224
|
+
"@typescript-eslint/no-unused-vars": "off",
|
|
225
|
+
"import/prefer-default-export": "off",
|
|
226
|
+
"no-underscore-dangle": "off"
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
// Environments module overrides
|
|
231
|
+
{
|
|
232
|
+
files: ["package/environments/**/*.ts"],
|
|
233
|
+
rules: {
|
|
234
|
+
"import/prefer-default-export": "off",
|
|
235
|
+
"no-underscore-dangle": "off"
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
// Type tests are intentionally unused - they test type compatibility
|
|
240
|
+
{
|
|
241
|
+
files: ["package/**/__type-tests__/**/*.ts"],
|
|
242
|
+
rules: {
|
|
243
|
+
"@typescript-eslint/no-unused-vars": "off"
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
// Note: Type-aware rule overrides from main config (e.g., @typescript-eslint/no-unsafe-*,
|
|
248
|
+
// @typescript-eslint/restrict-template-expressions) are intentionally omitted here since
|
|
249
|
+
// fast mode doesn't enable type-aware linting (no parserOptions.project specified).
|
|
250
|
+
// This keeps fast mode performant while maintaining consistency for non-type-aware rules.
|
|
251
|
+
|
|
140
252
|
// Prettier config must be last to override other configs
|
|
141
253
|
prettierConfig
|
|
142
254
|
]
|
data/eslint.config.js
CHANGED
|
@@ -19,13 +19,7 @@ module.exports = [
|
|
|
19
19
|
"vendor/**", // Vendored dependencies
|
|
20
20
|
"spec/**", // Ruby specs, not JavaScript
|
|
21
21
|
"package/**/*.js", // Generated/compiled JavaScript from TypeScript
|
|
22
|
-
"package/**/*.d.ts"
|
|
23
|
-
// Temporarily ignore TypeScript files until technical debt is resolved
|
|
24
|
-
// See ESLINT_TECHNICAL_DEBT.md for tracking
|
|
25
|
-
// TODO: Remove this once ESLint issues are fixed (tracked in #723)
|
|
26
|
-
// Exception: configExporter is being fixed in #707
|
|
27
|
-
"package/**/*.ts",
|
|
28
|
-
"!package/configExporter/**/*.ts" // Enable linting for configExporter (issue #707)
|
|
22
|
+
"package/**/*.d.ts" // Generated TypeScript declaration files
|
|
29
23
|
]
|
|
30
24
|
},
|
|
31
25
|
|
|
@@ -142,10 +136,10 @@ module.exports = [
|
|
|
142
136
|
// Disable base rule in favor of TypeScript version
|
|
143
137
|
"no-use-before-define": "off",
|
|
144
138
|
"@typescript-eslint/no-use-before-define": ["error"],
|
|
145
|
-
// Allow unused vars if they start with underscore (convention for ignored params)
|
|
139
|
+
// Allow unused vars if they start with underscore (convention for ignored params and type tests)
|
|
146
140
|
"@typescript-eslint/no-unused-vars": [
|
|
147
141
|
"error",
|
|
148
|
-
{ argsIgnorePattern: "^_" }
|
|
142
|
+
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" }
|
|
149
143
|
],
|
|
150
144
|
// Strict: no 'any' types allowed - use 'unknown' or specific types instead
|
|
151
145
|
"@typescript-eslint/no-explicit-any": "error",
|
|
@@ -157,37 +151,42 @@ module.exports = [
|
|
|
157
151
|
}
|
|
158
152
|
},
|
|
159
153
|
|
|
154
|
+
// Global rule for all TypeScript files in package/
|
|
155
|
+
// Suppress require() imports - these are intentional for CommonJS compatibility
|
|
156
|
+
// Will be addressed in Phase 3 (breaking changes) - see #708
|
|
157
|
+
{
|
|
158
|
+
files: ["package/**/*.ts"],
|
|
159
|
+
rules: {
|
|
160
|
+
"@typescript-eslint/no-require-imports": "off",
|
|
161
|
+
"global-require": "off",
|
|
162
|
+
"import/no-import-module-exports": "off"
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
|
|
160
166
|
// Temporary overrides for files with remaining errors
|
|
161
167
|
// See ESLINT_TECHNICAL_DEBT.md for detailed documentation
|
|
162
168
|
//
|
|
163
|
-
// These overrides suppress ~
|
|
169
|
+
// These overrides suppress ~94 type safety errors that require:
|
|
164
170
|
// 1. Major type refactoring (any/unsafe-* rules)
|
|
165
|
-
// 2.
|
|
166
|
-
// 3. Significant code restructuring
|
|
171
|
+
// 2. Proper type definitions for config objects
|
|
167
172
|
//
|
|
168
|
-
// GitHub
|
|
169
|
-
// - #
|
|
170
|
-
// - #708: Module System: Modernize to ES6 modules with codemod
|
|
171
|
-
// - #709: Code Style: Fix remaining ESLint style issues
|
|
173
|
+
// GitHub Issue tracking this technical debt:
|
|
174
|
+
// - #790: TypeScript ESLint Phase 2b: Type Safety Improvements (~94 errors)
|
|
172
175
|
{
|
|
173
176
|
// Consolidated override for package/config.ts and package/babel/preset.ts
|
|
174
177
|
// Combines rules from both previous override blocks to avoid duplication
|
|
175
178
|
files: ["package/babel/preset.ts", "package/config.ts"],
|
|
176
179
|
rules: {
|
|
177
|
-
// From first override block
|
|
178
|
-
"@typescript-eslint/no-require-imports": "off",
|
|
179
180
|
"@typescript-eslint/no-unused-vars": "off",
|
|
180
181
|
"@typescript-eslint/no-unsafe-call": "off",
|
|
181
182
|
"import/order": "off",
|
|
182
183
|
"import/newline-after-import": "off",
|
|
183
184
|
"import/first": "off",
|
|
184
|
-
// Additional rules that were in the second override for config.ts
|
|
185
185
|
"@typescript-eslint/no-unsafe-assignment": "off",
|
|
186
186
|
"@typescript-eslint/no-unsafe-member-access": "off",
|
|
187
187
|
"@typescript-eslint/no-unsafe-argument": "off",
|
|
188
188
|
"@typescript-eslint/no-explicit-any": "off",
|
|
189
|
-
"no-useless-escape": "off"
|
|
190
|
-
"no-continue": "off"
|
|
189
|
+
"no-useless-escape": "off"
|
|
191
190
|
}
|
|
192
191
|
},
|
|
193
192
|
{
|
|
@@ -205,19 +204,14 @@ module.exports = [
|
|
|
205
204
|
// Code organization (functions before use due to large file)
|
|
206
205
|
"@typescript-eslint/no-use-before-define": "off",
|
|
207
206
|
// Import style (CommonJS require for dynamic imports)
|
|
208
|
-
"@typescript-eslint/no-require-imports": "off",
|
|
209
207
|
"import/no-dynamic-require": "off",
|
|
210
|
-
"global-require": "off",
|
|
211
208
|
// Class methods that are part of public API
|
|
212
209
|
"class-methods-use-this": "off",
|
|
213
210
|
// Template expressions (valid use cases with union types)
|
|
214
211
|
"@typescript-eslint/restrict-template-expressions": "off",
|
|
215
212
|
// Style preferences
|
|
216
|
-
"no-continue": "off",
|
|
217
213
|
"import/prefer-default-export": "off",
|
|
218
|
-
"no-await-in-loop": "off",
|
|
219
214
|
"no-underscore-dangle": "off",
|
|
220
|
-
"no-shadow": "off",
|
|
221
215
|
"no-restricted-globals": "off",
|
|
222
216
|
"@typescript-eslint/no-unused-vars": "off",
|
|
223
217
|
"@typescript-eslint/require-await": "off"
|
|
@@ -236,20 +230,26 @@ module.exports = [
|
|
|
236
230
|
}
|
|
237
231
|
},
|
|
238
232
|
{
|
|
239
|
-
// Remaining utils files
|
|
233
|
+
// Remaining utils files that need type safety improvements
|
|
234
|
+
// These use dynamic requires and helper functions that return `any`
|
|
240
235
|
files: [
|
|
241
236
|
"package/utils/inliningCss.ts",
|
|
242
237
|
"package/utils/errorCodes.ts",
|
|
243
238
|
"package/utils/errorHelpers.ts",
|
|
244
|
-
"package/utils/pathValidation.ts"
|
|
239
|
+
"package/utils/pathValidation.ts",
|
|
240
|
+
"package/utils/getStyleRule.ts",
|
|
241
|
+
"package/utils/helpers.ts",
|
|
242
|
+
"package/utils/validateDependencies.ts",
|
|
243
|
+
"package/webpackDevServerConfig.ts"
|
|
245
244
|
],
|
|
246
245
|
rules: {
|
|
247
246
|
"@typescript-eslint/no-unsafe-assignment": "off",
|
|
248
247
|
"@typescript-eslint/no-unsafe-member-access": "off",
|
|
249
248
|
"@typescript-eslint/no-unsafe-argument": "off",
|
|
249
|
+
"@typescript-eslint/no-unsafe-call": "off",
|
|
250
|
+
"@typescript-eslint/no-unsafe-return": "off",
|
|
250
251
|
"@typescript-eslint/no-explicit-any": "off",
|
|
251
|
-
"no-useless-escape": "off"
|
|
252
|
-
"no-continue": "off"
|
|
252
|
+
"no-useless-escape": "off"
|
|
253
253
|
}
|
|
254
254
|
},
|
|
255
255
|
{
|
|
@@ -257,13 +257,13 @@ module.exports = [
|
|
|
257
257
|
rules: {
|
|
258
258
|
"@typescript-eslint/no-unsafe-assignment": "off",
|
|
259
259
|
"@typescript-eslint/no-unsafe-call": "off",
|
|
260
|
+
"@typescript-eslint/no-unsafe-member-access": "off",
|
|
260
261
|
"@typescript-eslint/no-redundant-type-constituents": "off",
|
|
261
262
|
"import/prefer-default-export": "off"
|
|
262
263
|
}
|
|
263
264
|
},
|
|
264
265
|
{
|
|
265
266
|
files: [
|
|
266
|
-
"package/environments/**/*.ts",
|
|
267
267
|
"package/index.ts",
|
|
268
268
|
"package/rspack/index.ts",
|
|
269
269
|
"package/rules/**/*.ts",
|
|
@@ -276,6 +276,8 @@ module.exports = [
|
|
|
276
276
|
"@typescript-eslint/no-unsafe-assignment": "off",
|
|
277
277
|
"@typescript-eslint/no-unsafe-call": "off",
|
|
278
278
|
"@typescript-eslint/no-unsafe-return": "off",
|
|
279
|
+
"@typescript-eslint/no-unsafe-member-access": "off",
|
|
280
|
+
"@typescript-eslint/no-unsafe-argument": "off",
|
|
279
281
|
"@typescript-eslint/no-redundant-type-constituents": "off",
|
|
280
282
|
"@typescript-eslint/no-unused-vars": "off",
|
|
281
283
|
"@typescript-eslint/no-unsafe-function-type": "off",
|
|
@@ -283,6 +285,23 @@ module.exports = [
|
|
|
283
285
|
"no-underscore-dangle": "off"
|
|
284
286
|
}
|
|
285
287
|
},
|
|
288
|
+
{
|
|
289
|
+
// package/environments/**/*.ts now passes no-unused-vars rule
|
|
290
|
+
// Type test functions use underscore prefix (argsIgnorePattern: "^_")
|
|
291
|
+
// All other variables are used in the code
|
|
292
|
+
files: ["package/environments/**/*.ts"],
|
|
293
|
+
rules: {
|
|
294
|
+
"@typescript-eslint/no-unsafe-assignment": "off",
|
|
295
|
+
"@typescript-eslint/no-unsafe-call": "off",
|
|
296
|
+
"@typescript-eslint/no-unsafe-return": "off",
|
|
297
|
+
"@typescript-eslint/no-unsafe-member-access": "off",
|
|
298
|
+
"@typescript-eslint/no-unsafe-argument": "off",
|
|
299
|
+
"@typescript-eslint/no-redundant-type-constituents": "off",
|
|
300
|
+
"@typescript-eslint/no-unsafe-function-type": "off",
|
|
301
|
+
"import/prefer-default-export": "off",
|
|
302
|
+
"no-underscore-dangle": "off"
|
|
303
|
+
}
|
|
304
|
+
},
|
|
286
305
|
|
|
287
306
|
// Prettier config must be last to override other configs
|
|
288
307
|
prettierConfig
|
|
@@ -19,6 +19,18 @@ default: &default
|
|
|
19
19
|
# css_extract_ignore_order_warnings to true
|
|
20
20
|
css_extract_ignore_order_warnings: false
|
|
21
21
|
|
|
22
|
+
# CSS Modules export mode
|
|
23
|
+
# Controls how CSS Module class names are exported in JavaScript
|
|
24
|
+
# Defaults to 'named' if not specified. Uncomment and change to 'default' for v8 behavior.
|
|
25
|
+
# Options:
|
|
26
|
+
# - named (default): Use named exports with camelCase conversion (v9 default)
|
|
27
|
+
# Example: import { button } from './styles.module.css'
|
|
28
|
+
# - default: Use default export with both original and camelCase names (v8 behavior)
|
|
29
|
+
# Example: import styles from './styles.module.css'
|
|
30
|
+
# For gradual migration, you can set this to default to maintain v8 behavior
|
|
31
|
+
# See https://github.com/shakacode/shakapacker/blob/main/docs/css-modules-export-mode.md
|
|
32
|
+
# css_modules_export_mode: named
|
|
33
|
+
|
|
22
34
|
public_root_path: public
|
|
23
35
|
public_output_path: packs
|
|
24
36
|
cache_path: tmp/shakapacker
|
|
@@ -41,7 +53,8 @@ default: &default
|
|
|
41
53
|
cache_manifest: false
|
|
42
54
|
|
|
43
55
|
# Select JavaScript transpiler to use
|
|
44
|
-
# Available options: 'swc' (default, 20x faster), 'babel', or '
|
|
56
|
+
# Available options: 'swc' (default, 20x faster), 'babel', 'esbuild', or 'none'
|
|
57
|
+
# Use 'none' when providing a completely custom webpack configuration
|
|
45
58
|
# Note: When using rspack, swc is used automatically regardless of this setting
|
|
46
59
|
javascript_transpiler: "swc"
|
|
47
60
|
|
|
@@ -9,6 +9,9 @@ module Shakapacker
|
|
|
9
9
|
SHAKAPACKER_CONFIG = "config/shakapacker.yml"
|
|
10
10
|
CUSTOM_DEPS_CONFIG = ".shakapacker-switch-bundler-dependencies.yml"
|
|
11
11
|
|
|
12
|
+
# Regex pattern to detect assets_bundler key in config (only matches uncommented lines)
|
|
13
|
+
ASSETS_BUNDLER_PATTERN = /^[ \t]*assets_bundler:/
|
|
14
|
+
|
|
12
15
|
# Default dependencies for each bundler (package names only, no versions)
|
|
13
16
|
DEFAULT_RSPACK_DEPS = {
|
|
14
17
|
dev: %w[@rspack/cli @rspack/plugin-react-refresh],
|
|
@@ -37,18 +40,33 @@ module Shakapacker
|
|
|
37
40
|
end
|
|
38
41
|
|
|
39
42
|
current = current_bundler
|
|
40
|
-
|
|
43
|
+
config_content = File.read(config_path)
|
|
44
|
+
has_assets_bundler = config_content =~ ASSETS_BUNDLER_PATTERN
|
|
45
|
+
|
|
46
|
+
# Early exit if already using the target bundler
|
|
47
|
+
# For webpack: if current is webpack, we're done (key optional due to default)
|
|
48
|
+
# For rspack: requires explicit key to be present
|
|
49
|
+
already_configured = if bundler == "webpack"
|
|
50
|
+
current == bundler
|
|
51
|
+
else
|
|
52
|
+
current == bundler && has_assets_bundler
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if already_configured && !install_deps
|
|
41
56
|
puts "✅ Already using #{bundler}"
|
|
42
57
|
return
|
|
43
58
|
end
|
|
44
59
|
|
|
45
|
-
if
|
|
60
|
+
if already_configured && install_deps
|
|
46
61
|
puts "✅ Already using #{bundler} - reinstalling dependencies as requested"
|
|
47
62
|
manage_dependencies(bundler, install_deps, switching: false, no_uninstall: no_uninstall)
|
|
48
63
|
return
|
|
49
64
|
end
|
|
50
65
|
|
|
51
|
-
update_config(bundler)
|
|
66
|
+
successfully_updated = update_config(bundler, config_content, has_assets_bundler)
|
|
67
|
+
|
|
68
|
+
# Verify the update was successful (only if update reported success)
|
|
69
|
+
verify_config_update(bundler) if successfully_updated
|
|
52
70
|
|
|
53
71
|
puts "✅ Switched from #{current} to #{bundler}"
|
|
54
72
|
puts ""
|
|
@@ -95,22 +113,15 @@ module Shakapacker
|
|
|
95
113
|
puts "Current bundler: #{current}"
|
|
96
114
|
puts ""
|
|
97
115
|
puts "Usage:"
|
|
98
|
-
puts " rails shakapacker:switch_bundler [webpack|rspack] [OPTIONS]"
|
|
99
116
|
puts " rake shakapacker:switch_bundler [webpack|rspack] -- [OPTIONS]"
|
|
100
117
|
puts ""
|
|
101
118
|
puts "Options:"
|
|
102
119
|
puts " --install-deps Automatically install/uninstall dependencies"
|
|
103
|
-
puts " --no-uninstall Skip uninstalling old bundler packages
|
|
120
|
+
puts " --no-uninstall Skip uninstalling old bundler packages"
|
|
104
121
|
puts " --init-config Create #{CUSTOM_DEPS_CONFIG} with default dependencies"
|
|
105
122
|
puts " --help, -h Show this help message"
|
|
106
123
|
puts ""
|
|
107
124
|
puts "Examples:"
|
|
108
|
-
puts " # Using rails command"
|
|
109
|
-
puts " rails shakapacker:switch_bundler rspack --install-deps"
|
|
110
|
-
puts " rails shakapacker:switch_bundler webpack --install-deps --no-uninstall"
|
|
111
|
-
puts " rails shakapacker:switch_bundler --init-config"
|
|
112
|
-
puts ""
|
|
113
|
-
puts " # Using rake command (note the -- separator)"
|
|
114
125
|
puts " rake shakapacker:switch_bundler rspack -- --install-deps"
|
|
115
126
|
puts " rake shakapacker:switch_bundler webpack -- --install-deps --no-uninstall"
|
|
116
127
|
puts " rake shakapacker:switch_bundler -- --init-config"
|
|
@@ -150,20 +161,74 @@ module Shakapacker
|
|
|
150
161
|
end
|
|
151
162
|
end
|
|
152
163
|
|
|
153
|
-
def update_config(bundler)
|
|
154
|
-
|
|
164
|
+
def update_config(bundler, content, has_assets_bundler)
|
|
165
|
+
# Check if assets_bundler key exists (only uncommented lines)
|
|
166
|
+
unless has_assets_bundler
|
|
167
|
+
# Track whether we successfully added the key
|
|
168
|
+
added = false
|
|
169
|
+
|
|
170
|
+
# Add assets_bundler after javascript_transpiler if it exists (excluding commented lines)
|
|
171
|
+
if (match = content.match(/^[ \t]*(?![ \t]*#)javascript_transpiler:.*$/))
|
|
172
|
+
indent = match[0][/^[ \t]*/]
|
|
173
|
+
content.sub!(/^([ \t]*(?![ \t]*#)javascript_transpiler:.*$)/, "\\1\n#{assets_bundler_entry(bundler, indent)}")
|
|
174
|
+
added = true
|
|
175
|
+
# Otherwise, add it after source_path if it exists (excluding commented lines)
|
|
176
|
+
elsif (match = content.match(/^[ \t]*(?![ \t]*#)source_path:.*$/))
|
|
177
|
+
indent = match[0][/^[ \t]*/]
|
|
178
|
+
content.sub!(/^([ \t]*(?![ \t]*#)source_path:.*$)/, "\\1\n#{assets_bundler_entry(bundler, indent)}")
|
|
179
|
+
added = true
|
|
180
|
+
# Add it after default: &default if it exists
|
|
181
|
+
elsif content.match?(/^default:[ \t]*&default[ \t]*$/)
|
|
182
|
+
# Use default 2-space indentation for this case
|
|
183
|
+
content.sub!(/^(default:[ \t]*&default[ \t]*)$/, "\\1\n#{assets_bundler_entry(bundler, ' ')}")
|
|
184
|
+
added = true
|
|
185
|
+
# Fallback: add after "default:" with proper indentation detection (handles blank lines)
|
|
186
|
+
elsif (match = content.match(/^default:\s*\n\s*([ \t]+)/m))
|
|
187
|
+
# Extract indentation from first indented line after "default:"
|
|
188
|
+
indent = match[1]
|
|
189
|
+
content.sub!(/^(default:\s*)$/, "\\1\n#{assets_bundler_entry(bundler, indent)}")
|
|
190
|
+
added = true
|
|
191
|
+
end
|
|
155
192
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
193
|
+
unless added
|
|
194
|
+
puts "⚠️ Warning: Could not find appropriate location for assets_bundler in config"
|
|
195
|
+
puts " Please add 'assets_bundler: #{bundler}' to the default section manually"
|
|
196
|
+
end
|
|
197
|
+
else
|
|
198
|
+
# Replace existing assets_bundler value (handles spaces, tabs, and various quote styles)
|
|
199
|
+
# Only matches uncommented lines
|
|
200
|
+
content.gsub!(/^([ \t]*)(?![ \t]*#)(assets_bundler:[ \t]*['"]?)(webpack|rspack)(['"]?)/, "\\1\\2#{bundler}\\4")
|
|
201
|
+
added = true
|
|
202
|
+
end
|
|
159
203
|
|
|
160
204
|
# Update javascript_transpiler recommendation for rspack
|
|
161
205
|
# Only update if not already set to swc and only on uncommented lines
|
|
162
|
-
if bundler == "rspack" && content !~ /^[ \t]*javascript_transpiler:[ \t]*['"]?swc['"]?/
|
|
163
|
-
content.gsub!(/^([ \t]*javascript_transpiler:[ \t]*['"]?)\w+(['"]?)/,
|
|
206
|
+
if bundler == "rspack" && content !~ /^[ \t]*(?![ \t]*#)javascript_transpiler:[ \t]*['"]?swc['"]?/
|
|
207
|
+
content.gsub!(/^([ \t]*(?![ \t]*#)javascript_transpiler:[ \t]*['"]?)(\w+)(['"]?)/, '\1swc\3')
|
|
164
208
|
end
|
|
165
209
|
|
|
166
210
|
File.write(config_path, content)
|
|
211
|
+
added
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Verify that the config was updated successfully
|
|
215
|
+
def verify_config_update(bundler)
|
|
216
|
+
config = load_yaml_config(config_path)
|
|
217
|
+
actual_bundler = config.dig("default", "assets_bundler")
|
|
218
|
+
|
|
219
|
+
if actual_bundler != bundler
|
|
220
|
+
raise "Config update verification failed: expected assets_bundler to be '#{bundler}', but got '#{actual_bundler}'"
|
|
221
|
+
end
|
|
222
|
+
rescue Psych::SyntaxError => e
|
|
223
|
+
raise "Config update generated invalid YAML: #{e.message}"
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Generate the assets_bundler YAML entry with proper indentation
|
|
227
|
+
# @param bundler [String] The bundler name ('webpack' or 'rspack')
|
|
228
|
+
# @param indent [String] The indentation string to use (e.g., ' ' or '\t')
|
|
229
|
+
# @return [String] The formatted YAML entry
|
|
230
|
+
def assets_bundler_entry(bundler, indent)
|
|
231
|
+
"\n#{indent}# Select assets bundler to use\n#{indent}# Available options: 'webpack' (default) or 'rspack'\n#{indent}assets_bundler: \"#{bundler}\""
|
|
167
232
|
end
|
|
168
233
|
|
|
169
234
|
def manage_dependencies(bundler, install_deps, switching: true, no_uninstall: false)
|
|
@@ -339,6 +339,31 @@ class Shakapacker::Configuration
|
|
|
339
339
|
javascript_transpiler
|
|
340
340
|
end
|
|
341
341
|
|
|
342
|
+
# Returns the CSS Modules export mode configuration
|
|
343
|
+
#
|
|
344
|
+
# Controls how CSS Module class names are exported in JavaScript:
|
|
345
|
+
# - "named" (default): Use named exports with camelCase conversion (v9 behavior)
|
|
346
|
+
# - "default": Use default export with both original and camelCase names (v8 behavior)
|
|
347
|
+
#
|
|
348
|
+
# @return [String] "named" or "default"
|
|
349
|
+
# @raise [ArgumentError] if an invalid value is configured
|
|
350
|
+
def css_modules_export_mode
|
|
351
|
+
@css_modules_export_mode ||= begin
|
|
352
|
+
mode = fetch(:css_modules_export_mode) || "named"
|
|
353
|
+
|
|
354
|
+
# Validate the configuration value
|
|
355
|
+
valid_modes = ["named", "default"]
|
|
356
|
+
unless valid_modes.include?(mode)
|
|
357
|
+
raise ArgumentError,
|
|
358
|
+
"Invalid css_modules_export_mode: '#{mode}'. " \
|
|
359
|
+
"Valid values are: #{valid_modes.map { |m| "'#{m}'" }.join(', ')}. " \
|
|
360
|
+
"See https://github.com/shakacode/shakapacker/blob/main/docs/css-modules-export-mode.md"
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
mode
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
|
|
342
367
|
# Returns the path to the bundler configuration directory
|
|
343
368
|
#
|
|
344
369
|
# This is where webpack.config.js or rspack.config.js should be located.
|
|
@@ -353,6 +378,25 @@ class Shakapacker::Configuration
|
|
|
353
378
|
rspack? ? "config/rspack" : "config/webpack"
|
|
354
379
|
end
|
|
355
380
|
|
|
381
|
+
# Returns the raw configuration data hash
|
|
382
|
+
#
|
|
383
|
+
# Returns the merged configuration from the shakapacker.yml file for the current environment.
|
|
384
|
+
# The hash has symbolized keys loaded from the config file. Individual config values can be
|
|
385
|
+
# accessed through specific accessor methods like {#source_path}, which apply defaults via {#fetch}.
|
|
386
|
+
#
|
|
387
|
+
# The returned hash is frozen to prevent accidental mutations. To access config values,
|
|
388
|
+
# use the provided accessor methods instead of modifying this hash directly.
|
|
389
|
+
#
|
|
390
|
+
# @return [Hash<Symbol, Object>] the raw configuration data with symbolized keys (frozen)
|
|
391
|
+
# @example
|
|
392
|
+
# config.data[:source_path] #=> "app/javascript"
|
|
393
|
+
# config.data[:compile] #=> true
|
|
394
|
+
# @note The hash is frozen to prevent mutations. Use accessor methods for safe config access.
|
|
395
|
+
# @api public
|
|
396
|
+
def data
|
|
397
|
+
@data ||= load.freeze
|
|
398
|
+
end
|
|
399
|
+
|
|
356
400
|
private
|
|
357
401
|
|
|
358
402
|
def default_javascript_transpiler
|
|
@@ -363,6 +407,9 @@ class Shakapacker::Configuration
|
|
|
363
407
|
def validate_transpiler_configuration(transpiler)
|
|
364
408
|
return unless ENV["NODE_ENV"] != "test" # Skip validation in test environment
|
|
365
409
|
|
|
410
|
+
# Skip validation if transpiler is set to 'none' (custom webpack config)
|
|
411
|
+
return if transpiler == "none"
|
|
412
|
+
|
|
366
413
|
# Check if package.json exists
|
|
367
414
|
package_json_path = root_path.join("package.json")
|
|
368
415
|
return unless package_json_path.exist?
|
|
@@ -495,10 +542,6 @@ class Shakapacker::Configuration
|
|
|
495
542
|
[private_full_path.cleanpath.to_s, public_full_path.cleanpath.to_s]
|
|
496
543
|
end
|
|
497
544
|
|
|
498
|
-
def data
|
|
499
|
-
@data ||= load
|
|
500
|
-
end
|
|
501
|
-
|
|
502
545
|
def load
|
|
503
546
|
config = begin
|
|
504
547
|
YAML.load_file(config_path.to_s, aliases: true)
|
|
@@ -18,6 +18,8 @@ module Shakapacker
|
|
|
18
18
|
exit(0)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
Shakapacker.ensure_node_env!
|
|
22
|
+
|
|
21
23
|
# Check for --build flag
|
|
22
24
|
build_index = argv.index("--build")
|
|
23
25
|
if build_index
|
|
@@ -65,6 +67,8 @@ module Shakapacker
|
|
|
65
67
|
end
|
|
66
68
|
|
|
67
69
|
def self.run_with_build_config(argv, build_config)
|
|
70
|
+
Shakapacker.ensure_node_env!
|
|
71
|
+
|
|
68
72
|
# Apply build config environment variables
|
|
69
73
|
build_config[:environment].each do |key, value|
|
|
70
74
|
ENV[key] = value.to_s
|
data/lib/shakapacker/doctor.rb
CHANGED
|
@@ -54,7 +54,7 @@ module Shakapacker
|
|
|
54
54
|
Shakapacker Doctor - Diagnostic tool for Shakapacker configuration
|
|
55
55
|
|
|
56
56
|
Usage:
|
|
57
|
-
|
|
57
|
+
bundle exec rake shakapacker:doctor [options]
|
|
58
58
|
|
|
59
59
|
Options:
|
|
60
60
|
--help Show this help message
|
|
@@ -163,7 +163,7 @@ module Shakapacker
|
|
|
163
163
|
begin
|
|
164
164
|
manifest_content = JSON.parse(File.read(manifest_path))
|
|
165
165
|
if manifest_content.empty?
|
|
166
|
-
add_warning("Manifest file is empty - you may need to run '
|
|
166
|
+
add_warning("Manifest file is empty - you may need to run 'bundle exec rake assets:precompile'")
|
|
167
167
|
end
|
|
168
168
|
rescue JSON::ParserError
|
|
169
169
|
@issues << "Manifest file #{manifest_path} contains invalid JSON"
|
|
@@ -325,16 +325,16 @@ module Shakapacker
|
|
|
325
325
|
if source_files.any?
|
|
326
326
|
newest_source = source_files.map { |f| File.mtime(f) }.max
|
|
327
327
|
if newest_source > File.mtime(manifest_path)
|
|
328
|
-
add_warning("Source files have been modified after last asset compilation. Run '
|
|
328
|
+
add_warning("Source files have been modified after last asset compilation. Run 'bundle exec rake assets:precompile'")
|
|
329
329
|
end
|
|
330
330
|
end
|
|
331
331
|
else
|
|
332
332
|
rails_env = defined?(Rails) ? Rails.env : ENV["RAILS_ENV"]
|
|
333
333
|
if rails_env == "production"
|
|
334
|
-
@issues << "No compiled assets found (manifest.json missing). Run '
|
|
334
|
+
@issues << "No compiled assets found (manifest.json missing). Run 'bundle exec rake assets:precompile'"
|
|
335
335
|
elsif options[:verbose]
|
|
336
336
|
# Only show in verbose mode for non-production environments
|
|
337
|
-
@info << "Assets not yet compiled. Run '
|
|
337
|
+
@info << "Assets not yet compiled. Run 'bundle exec rake assets:precompile' or start the dev server"
|
|
338
338
|
end
|
|
339
339
|
end
|
|
340
340
|
end
|
|
@@ -402,7 +402,7 @@ module Shakapacker
|
|
|
402
402
|
|
|
403
403
|
unless missing_binstubs.empty?
|
|
404
404
|
add_action_required("Missing binstubs: #{missing_binstubs.join(', ')}.")
|
|
405
|
-
add_action_required(" Fix: Run '
|
|
405
|
+
add_action_required(" Fix: Run 'bundle exec rake shakapacker:binstubs' to create them.")
|
|
406
406
|
end
|
|
407
407
|
end
|
|
408
408
|
|
|
@@ -914,7 +914,7 @@ module Shakapacker
|
|
|
914
914
|
return unless doctor.config.config_path.exist?
|
|
915
915
|
|
|
916
916
|
puts "\nConfiguration values for '#{doctor.config.env}' environment:"
|
|
917
|
-
config_data = doctor.config.
|
|
917
|
+
config_data = doctor.config.data
|
|
918
918
|
|
|
919
919
|
if config_data.any?
|
|
920
920
|
print_config_data(config_data)
|