shakapacker 9.3.0.beta.7 → 9.3.1

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.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/update-changelog.md +224 -0
  3. data/.github/actionlint-matcher.json +17 -0
  4. data/.github/workflows/dummy.yml +9 -0
  5. data/.github/workflows/generator.yml +13 -0
  6. data/.github/workflows/node.yml +83 -0
  7. data/.github/workflows/ruby.yml +11 -0
  8. data/.github/workflows/test-bundlers.yml +10 -0
  9. data/CHANGELOG.md +55 -111
  10. data/CLAUDE.md +6 -10
  11. data/CONTRIBUTING.md +57 -0
  12. data/Gemfile.lock +1 -1
  13. data/README.md +84 -8
  14. data/docs/api-reference.md +519 -0
  15. data/docs/configuration.md +38 -4
  16. data/docs/css-modules-export-mode.md +40 -6
  17. data/docs/rspack_migration_guide.md +238 -2
  18. data/docs/transpiler-migration.md +12 -9
  19. data/docs/troubleshooting.md +21 -21
  20. data/docs/using_swc_loader.md +13 -10
  21. data/docs/v9_upgrade.md +11 -2
  22. data/eslint.config.fast.js +128 -8
  23. data/eslint.config.js +89 -33
  24. data/knip.ts +8 -1
  25. data/lib/install/config/shakapacker.yml +20 -7
  26. data/lib/shakapacker/configuration.rb +274 -8
  27. data/lib/shakapacker/dev_server.rb +88 -1
  28. data/lib/shakapacker/dev_server_runner.rb +4 -0
  29. data/lib/shakapacker/doctor.rb +5 -5
  30. data/lib/shakapacker/instance.rb +85 -1
  31. data/lib/shakapacker/manifest.rb +85 -11
  32. data/lib/shakapacker/version.rb +1 -1
  33. data/lib/shakapacker.rb +143 -3
  34. data/lib/tasks/shakapacker/doctor.rake +1 -1
  35. data/lib/tasks/shakapacker/export_bundler_config.rake +4 -4
  36. data/package/config.ts +2 -4
  37. data/package/configExporter/buildValidator.ts +53 -29
  38. data/package/configExporter/cli.ts +106 -76
  39. data/package/configExporter/configFile.ts +33 -26
  40. data/package/configExporter/types.ts +64 -0
  41. data/package/configExporter/yamlSerializer.ts +118 -43
  42. data/package/dev_server.ts +3 -2
  43. data/package/env.ts +2 -2
  44. data/package/environments/__type-tests__/rspack-plugin-compatibility.ts +6 -6
  45. data/package/environments/base.ts +6 -6
  46. data/package/environments/development.ts +7 -9
  47. data/package/environments/production.ts +7 -8
  48. data/package/environments/test.ts +4 -2
  49. data/package/esbuild/index.ts +0 -2
  50. data/package/index.d.ts +1 -0
  51. data/package/index.d.ts.template +1 -0
  52. data/package/index.ts +28 -5
  53. data/package/loaders.d.ts +2 -2
  54. data/package/optimization/webpack.ts +29 -31
  55. data/package/plugins/rspack.ts +3 -1
  56. data/package/plugins/webpack.ts +5 -3
  57. data/package/rspack/index.ts +5 -4
  58. data/package/rules/file.ts +2 -1
  59. data/package/rules/jscommon.ts +1 -0
  60. data/package/rules/raw.ts +3 -1
  61. data/package/rules/rspack.ts +0 -2
  62. data/package/rules/sass.ts +0 -2
  63. data/package/rules/webpack.ts +0 -1
  64. data/package/swc/index.ts +0 -2
  65. data/package/types.ts +8 -11
  66. data/package/utils/debug.ts +0 -4
  67. data/package/utils/getStyleRule.ts +17 -9
  68. data/package/utils/helpers.ts +8 -4
  69. data/package/utils/pathValidation.ts +78 -18
  70. data/package/utils/requireOrError.ts +14 -5
  71. data/package/utils/typeGuards.ts +43 -46
  72. data/package/webpack-types.d.ts +2 -2
  73. data/package/webpackDevServerConfig.ts +5 -4
  74. data/package.json +2 -3
  75. data/test/package/configExporter/cli.test.js +440 -0
  76. data/test/package/configExporter/types.test.js +163 -0
  77. data/test/package/configExporter.test.js +264 -0
  78. data/test/package/transpiler-defaults.test.js +42 -0
  79. data/test/package/yamlSerializer.test.js +204 -0
  80. data/test/typescript/pathValidation.test.js +44 -0
  81. data/test/typescript/requireOrError.test.js +49 -0
  82. data/yarn.lock +0 -32
  83. metadata +14 -5
  84. data/.eslintrc.fast.js +0 -40
  85. data/.eslintrc.js +0 -84
@@ -1,14 +1,23 @@
1
- /* eslint global-require: 0 */
2
1
  /* eslint import/no-dynamic-require: 0 */
3
- const config = require("../config")
2
+ import type { Config } from "../types"
4
3
 
5
- const requireOrError = (moduleName: string): any => {
4
+ const config = require("../config") as Config
5
+
6
+ interface ErrorWithCause extends Error {
7
+ cause?: unknown
8
+ }
9
+
10
+ const requireOrError = (moduleName: string): unknown => {
6
11
  try {
7
12
  return require(moduleName)
8
- } catch (error) {
9
- throw new Error(
13
+ } catch (originalError: unknown) {
14
+ const error: ErrorWithCause = new Error(
10
15
  `[SHAKAPACKER]: ${moduleName} is required for ${config.assets_bundler} but is not installed. View Shakapacker's documented dependencies at https://github.com/shakacode/shakapacker/tree/main/docs/peer-dependencies.md`
11
16
  )
17
+ // Add the original error as the cause for better debugging (ES2022+)
18
+ // Using custom interface since target is ES2020 but runtime supports it
19
+ error.cause = originalError
20
+ throw error
12
21
  }
13
22
  }
14
23
 
@@ -66,6 +66,38 @@ export function clearValidationCache(): void {
66
66
  }
67
67
  }
68
68
 
69
+ /**
70
+ * Type guard to validate DevServerConfig object at runtime
71
+ * In production, performs minimal validation for performance
72
+ */
73
+ export function isValidDevServerConfig(obj: unknown): obj is DevServerConfig {
74
+ if (typeof obj !== "object" || obj === null) {
75
+ return false
76
+ }
77
+
78
+ // In production, skip deep validation unless explicitly enabled
79
+ if (!shouldValidate()) {
80
+ return true
81
+ }
82
+
83
+ const config = obj as Record<string, unknown>
84
+
85
+ // All fields are optional, just check types if present
86
+ if (
87
+ config.hmr !== undefined &&
88
+ typeof config.hmr !== "boolean" &&
89
+ config.hmr !== "only"
90
+ ) {
91
+ return false
92
+ }
93
+
94
+ if (config.port !== undefined && !validatePort(config.port)) {
95
+ return false
96
+ }
97
+
98
+ return true
99
+ }
100
+
69
101
  /**
70
102
  * Type guard to validate Config object at runtime
71
103
  * In production, caches results for performance unless SHAKAPACKER_STRICT_VALIDATION is set
@@ -79,7 +111,7 @@ export function isValidConfig(obj: unknown): obj is Config {
79
111
  }
80
112
 
81
113
  // Check cache with TTL
82
- const cached = validatedConfigs.get(obj as object)
114
+ const cached = validatedConfigs.get(obj)
83
115
  if (cached && Date.now() - cached.timestamp < getCacheTTL()) {
84
116
  if (debugCache) {
85
117
  console.log(
@@ -104,7 +136,7 @@ export function isValidConfig(obj: unknown): obj is Config {
104
136
  for (const field of requiredStringFields) {
105
137
  if (typeof config[field] !== "string") {
106
138
  // Cache negative result
107
- validatedConfigs.set(obj as object, {
139
+ validatedConfigs.set(obj, {
108
140
  result: false,
109
141
  timestamp: Date.now()
110
142
  })
@@ -112,14 +144,11 @@ export function isValidConfig(obj: unknown): obj is Config {
112
144
  }
113
145
  // SECURITY: Path traversal validation ALWAYS runs (not subject to shouldValidate)
114
146
  // This ensures paths are safe regardless of environment or validation mode
115
- if (
116
- field.includes("path") &&
117
- !isPathTraversalSafe(config[field] as string)
118
- ) {
147
+ if (field.includes("path") && !isPathTraversalSafe(config[field])) {
119
148
  console.warn(
120
149
  `[SHAKAPACKER SECURITY] Invalid path in ${field}: ${config[field]}`
121
150
  )
122
- validatedConfigs.set(obj as object, {
151
+ validatedConfigs.set(obj, {
123
152
  result: false,
124
153
  timestamp: Date.now()
125
154
  })
@@ -142,7 +171,7 @@ export function isValidConfig(obj: unknown): obj is Config {
142
171
  for (const field of requiredBooleanFields) {
143
172
  if (typeof config[field] !== "boolean") {
144
173
  // Cache negative result
145
- validatedConfigs.set(obj as object, {
174
+ validatedConfigs.set(obj, {
146
175
  result: false,
147
176
  timestamp: Date.now()
148
177
  })
@@ -153,7 +182,7 @@ export function isValidConfig(obj: unknown): obj is Config {
153
182
  // Check arrays
154
183
  if (!Array.isArray(config.additional_paths)) {
155
184
  // Cache negative result
156
- validatedConfigs.set(obj as object, {
185
+ validatedConfigs.set(obj, {
157
186
  result: false,
158
187
  timestamp: Date.now()
159
188
  })
@@ -167,7 +196,7 @@ export function isValidConfig(obj: unknown): obj is Config {
167
196
  console.warn(
168
197
  `[SHAKAPACKER SECURITY] Invalid additional_path: ${additionalPath}`
169
198
  )
170
- validatedConfigs.set(obj as object, {
199
+ validatedConfigs.set(obj, {
171
200
  result: false,
172
201
  timestamp: Date.now()
173
202
  })
@@ -179,7 +208,7 @@ export function isValidConfig(obj: unknown): obj is Config {
179
208
  // Security checks above still run regardless of this flag
180
209
  if (!shouldValidate()) {
181
210
  // Cache positive result - basic structure and security validated
182
- validatedConfigs.set(obj as object, { result: true, timestamp: Date.now() })
211
+ validatedConfigs.set(obj, { result: true, timestamp: Date.now() })
183
212
  return true
184
213
  }
185
214
 
@@ -189,7 +218,7 @@ export function isValidConfig(obj: unknown): obj is Config {
189
218
  !isValidDevServerConfig(config.dev_server)
190
219
  ) {
191
220
  // Cache negative result
192
- validatedConfigs.set(obj as object, {
221
+ validatedConfigs.set(obj, {
193
222
  result: false,
194
223
  timestamp: Date.now()
195
224
  })
@@ -203,7 +232,7 @@ export function isValidConfig(obj: unknown): obj is Config {
203
232
  typeof integrity.cross_origin !== "string"
204
233
  ) {
205
234
  // Cache negative result
206
- validatedConfigs.set(obj as object, {
235
+ validatedConfigs.set(obj, {
207
236
  result: false,
208
237
  timestamp: Date.now()
209
238
  })
@@ -212,39 +241,7 @@ export function isValidConfig(obj: unknown): obj is Config {
212
241
  }
213
242
 
214
243
  // Cache positive result
215
- validatedConfigs.set(obj as object, { result: true, timestamp: Date.now() })
216
-
217
- return true
218
- }
219
-
220
- /**
221
- * Type guard to validate DevServerConfig object at runtime
222
- * In production, performs minimal validation for performance
223
- */
224
- export function isValidDevServerConfig(obj: unknown): obj is DevServerConfig {
225
- if (typeof obj !== "object" || obj === null) {
226
- return false
227
- }
228
-
229
- // In production, skip deep validation unless explicitly enabled
230
- if (!shouldValidate()) {
231
- return true
232
- }
233
-
234
- const config = obj as Record<string, unknown>
235
-
236
- // All fields are optional, just check types if present
237
- if (
238
- config.hmr !== undefined &&
239
- typeof config.hmr !== "boolean" &&
240
- config.hmr !== "only"
241
- ) {
242
- return false
243
- }
244
-
245
- if (config.port !== undefined && !validatePort(config.port)) {
246
- return false
247
- }
244
+ validatedConfigs.set(obj, { result: true, timestamp: Date.now() })
248
245
 
249
246
  return true
250
247
  }
@@ -1,4 +1,4 @@
1
- // @ts-ignore: webpack is an optional peer dependency (using type-only import)
1
+ // @ts-expect-error: webpack is an optional peer dependency (using type-only import)
2
2
  import type { Configuration, RuleSetRule, RuleSetUseItem } from "webpack"
3
3
 
4
4
  export interface ShakapackerWebpackConfig extends Configuration {
@@ -13,7 +13,7 @@ export interface ShakapackerRule extends RuleSetRule {
13
13
  }
14
14
 
15
15
  export interface ShakapackerLoaderOptions {
16
- [key: string]: any
16
+ [key: string]: unknown
17
17
  }
18
18
 
19
19
  export interface ShakapackerLoader {
@@ -1,4 +1,5 @@
1
1
  import { DevServerConfig } from "./types"
2
+
2
3
  const snakeToCamelCase = require("./utils/snakeToCamelCase")
3
4
 
4
5
  const shakapackerDevServerYamlConfig =
@@ -24,11 +25,11 @@ interface WebpackDevServerConfig {
24
25
  [key: string]: unknown
25
26
  }
26
27
  client?: Record<string, unknown>
27
- allowedHosts?: "all" | "auto" | string | string[]
28
+ allowedHosts?: string | string[]
28
29
  bonjour?: boolean | Record<string, unknown>
29
30
  compress?: boolean
30
31
  headers?: Record<string, unknown> | (() => Record<string, unknown>)
31
- host?: "local-ip" | "local-ipv4" | "local-ipv6" | string
32
+ host?: string
32
33
  http2?: boolean
33
34
  https?: boolean | Record<string, unknown>
34
35
  ipc?: boolean | string
@@ -41,12 +42,12 @@ interface WebpackDevServerConfig {
41
42
  | string[]
42
43
  | Record<string, unknown>
43
44
  | Record<string, unknown>[]
44
- port?: "auto" | string | number
45
+ port?: string | number
45
46
  proxy?: unknown
46
47
  server?: string | boolean | Record<string, unknown>
47
48
  setupExitSignals?: boolean
48
49
  setupMiddlewares?: (middlewares: unknown[], devServer: unknown) => unknown[]
49
- watchFiles?: string | string[] | unknown
50
+ watchFiles?: unknown
50
51
  webSocketServer?: string | boolean | Record<string, unknown>
51
52
  [key: string]: unknown
52
53
  }
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shakapacker",
3
- "version": "9.3.0-beta.7",
3
+ "version": "9.3.1",
4
4
  "description": "Use webpack to manage app-like JavaScript modules in Rails",
5
5
  "homepage": "https://github.com/shakacode/shakapacker",
6
6
  "bugs": {
@@ -74,7 +74,6 @@
74
74
  "eslint-plugin-import": "^2.32.0",
75
75
  "eslint-plugin-jest": "^29.0.1",
76
76
  "eslint-plugin-jsx-a11y": "^6.10.2",
77
- "eslint-plugin-prettier": "^5.5.4",
78
77
  "eslint-plugin-react": "^7.37.5",
79
78
  "eslint-plugin-react-hooks": "^7.0.0",
80
79
  "husky": "^9.1.7",
@@ -109,7 +108,7 @@
109
108
  "babel-loader": "^8.2.4 || ^9.0.0 || ^10.0.0",
110
109
  "compression-webpack-plugin": "^9.0.0 || ^10.0.0 || ^11.0.0",
111
110
  "css-loader": "^6.8.1 || ^7.0.0",
112
- "esbuild": "^0.14.0 || ^0.15.0 || ^0.16.0 || ^0.17.0 || ^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0",
111
+ "esbuild": "^0.14.0 || ^0.15.0 || ^0.16.0 || ^0.17.0 || ^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0",
113
112
  "esbuild-loader": "^2.0.0 || ^3.0.0 || ^4.0.0",
114
113
  "mini-css-extract-plugin": "^2.0.0",
115
114
  "rspack-manifest-plugin": "^5.0.0",