shakapacker 9.2.0 → 9.3.0.beta.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.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +6 -9
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +6 -8
  4. data/.github/workflows/claude-code-review.yml +4 -5
  5. data/.github/workflows/claude.yml +1 -2
  6. data/.github/workflows/dummy.yml +4 -4
  7. data/.github/workflows/generator.yml +9 -9
  8. data/.github/workflows/node.yml +11 -2
  9. data/.github/workflows/ruby.yml +16 -16
  10. data/.github/workflows/test-bundlers.yml +9 -9
  11. data/.gitignore +4 -0
  12. data/CHANGELOG.md +19 -4
  13. data/CLAUDE.md +6 -1
  14. data/CONTRIBUTING.md +0 -1
  15. data/Gemfile.lock +1 -1
  16. data/README.md +14 -14
  17. data/TODO.md +10 -2
  18. data/TODO_v9.md +13 -3
  19. data/bin/export-bundler-config +1 -1
  20. data/conductor-setup.sh +1 -1
  21. data/conductor.json +1 -1
  22. data/docs/cdn_setup.md +13 -8
  23. data/docs/common-upgrades.md +2 -1
  24. data/docs/configuration.md +630 -0
  25. data/docs/css-modules-export-mode.md +120 -100
  26. data/docs/customizing_babel_config.md +16 -16
  27. data/docs/deployment.md +18 -0
  28. data/docs/developing_shakapacker.md +6 -0
  29. data/docs/optional-peer-dependencies.md +9 -4
  30. data/docs/peer-dependencies.md +17 -6
  31. data/docs/precompile_hook.md +342 -0
  32. data/docs/react.md +57 -47
  33. data/docs/releasing.md +0 -2
  34. data/docs/rspack.md +25 -21
  35. data/docs/rspack_migration_guide.md +335 -8
  36. data/docs/sprockets.md +1 -0
  37. data/docs/style_loader_vs_mini_css.md +12 -12
  38. data/docs/subresource_integrity.md +13 -7
  39. data/docs/transpiler-performance.md +40 -19
  40. data/docs/troubleshooting.md +0 -2
  41. data/docs/typescript-migration.md +48 -39
  42. data/docs/typescript.md +12 -8
  43. data/docs/using_esbuild_loader.md +10 -10
  44. data/docs/v6_upgrade.md +33 -20
  45. data/docs/v7_upgrade.md +8 -6
  46. data/docs/v8_upgrade.md +13 -12
  47. data/docs/v9_upgrade.md +2 -1
  48. data/eslint.config.fast.js +134 -0
  49. data/eslint.config.js +140 -0
  50. data/knip.ts +54 -0
  51. data/lib/install/bin/export-bundler-config +1 -1
  52. data/lib/install/config/shakapacker.yml +16 -5
  53. data/lib/shakapacker/compiler.rb +80 -0
  54. data/lib/shakapacker/configuration.rb +33 -5
  55. data/lib/shakapacker/dev_server_runner.rb +140 -1
  56. data/lib/shakapacker/doctor.rb +294 -65
  57. data/lib/shakapacker/instance.rb +8 -3
  58. data/lib/shakapacker/runner.rb +244 -8
  59. data/lib/shakapacker/version.rb +1 -1
  60. data/lib/tasks/shakapacker/doctor.rake +42 -2
  61. data/package/babel/preset.ts +7 -4
  62. data/package/config.ts +42 -30
  63. data/package/configExporter/cli.ts +799 -208
  64. data/package/configExporter/configFile.ts +520 -0
  65. data/package/configExporter/fileWriter.ts +12 -8
  66. data/package/configExporter/index.ts +9 -1
  67. data/package/configExporter/types.ts +36 -2
  68. data/package/configExporter/yamlSerializer.ts +22 -8
  69. data/package/dev_server.ts +1 -1
  70. data/package/environments/__type-tests__/rspack-plugin-compatibility.ts +11 -5
  71. data/package/environments/base.ts +18 -13
  72. data/package/environments/development.ts +1 -1
  73. data/package/environments/production.ts +4 -1
  74. data/package/index.d.ts +50 -3
  75. data/package/index.d.ts.template +50 -0
  76. data/package/index.ts +7 -7
  77. data/package/loaders.d.ts +2 -2
  78. data/package/optimization/rspack.ts +1 -1
  79. data/package/plugins/rspack.ts +15 -4
  80. data/package/plugins/webpack.ts +7 -3
  81. data/package/rspack/index.ts +10 -2
  82. data/package/rules/raw.ts +3 -2
  83. data/package/rules/sass.ts +1 -1
  84. data/package/types/README.md +15 -13
  85. data/package/types/index.ts +5 -5
  86. data/package/types.ts +0 -1
  87. data/package/utils/defaultConfigPath.ts +4 -1
  88. data/package/utils/errorCodes.ts +129 -100
  89. data/package/utils/errorHelpers.ts +34 -29
  90. data/package/utils/getStyleRule.ts +5 -2
  91. data/package/utils/helpers.ts +21 -11
  92. data/package/utils/pathValidation.ts +43 -35
  93. data/package/utils/requireOrError.ts +1 -1
  94. data/package/utils/snakeToCamelCase.ts +1 -1
  95. data/package/utils/typeGuards.ts +132 -83
  96. data/package/utils/validateDependencies.ts +1 -1
  97. data/package/webpack-types.d.ts +3 -3
  98. data/package/webpackDevServerConfig.ts +22 -10
  99. data/package-lock.json +2 -2
  100. data/package.json +36 -28
  101. data/scripts/type-check-no-emit.js +1 -1
  102. data/test/configExporter/configFile.test.js +392 -0
  103. data/test/configExporter/integration.test.js +275 -0
  104. data/test/helpers.js +1 -1
  105. data/test/package/configExporter.test.js +154 -0
  106. data/test/package/helpers.test.js +2 -2
  107. data/test/package/rules/sass-version-parsing.test.js +71 -0
  108. data/test/package/rules/sass.test.js +2 -4
  109. data/test/package/rules/sass1.test.js +1 -3
  110. data/test/package/rules/sass16.test.js +23 -0
  111. data/tools/README.md +15 -5
  112. data/tsconfig.eslint.json +2 -9
  113. data/yarn.lock +1894 -1492
  114. metadata +19 -3
  115. data/.eslintignore +0 -5
@@ -9,68 +9,68 @@
9
9
  */
10
10
  export enum ErrorCode {
11
11
  // Configuration errors (1xxx)
12
- CONFIG_NOT_FOUND = 'SHAKAPACKER_1001',
13
- CONFIG_INVALID_YAML = 'SHAKAPACKER_1002',
14
- CONFIG_MISSING_REQUIRED = 'SHAKAPACKER_1003',
15
- CONFIG_VALIDATION_FAILED = 'SHAKAPACKER_1004',
16
- CONFIG_MERGE_FAILED = 'SHAKAPACKER_1005',
17
- CONFIG_TYPE_MISMATCH = 'SHAKAPACKER_1006',
12
+ CONFIG_NOT_FOUND = "SHAKAPACKER_1001",
13
+ CONFIG_INVALID_YAML = "SHAKAPACKER_1002",
14
+ CONFIG_MISSING_REQUIRED = "SHAKAPACKER_1003",
15
+ CONFIG_VALIDATION_FAILED = "SHAKAPACKER_1004",
16
+ CONFIG_MERGE_FAILED = "SHAKAPACKER_1005",
17
+ CONFIG_TYPE_MISMATCH = "SHAKAPACKER_1006",
18
18
 
19
19
  // File system errors (2xxx)
20
- FILE_NOT_FOUND = 'SHAKAPACKER_2001',
21
- FILE_READ_ERROR = 'SHAKAPACKER_2002',
22
- FILE_WRITE_ERROR = 'SHAKAPACKER_2003',
23
- FILE_PERMISSION_DENIED = 'SHAKAPACKER_2004',
24
- PATH_TRAVERSAL_DETECTED = 'SHAKAPACKER_2005',
25
- INVALID_PATH = 'SHAKAPACKER_2006',
20
+ FILE_NOT_FOUND = "SHAKAPACKER_2001",
21
+ FILE_READ_ERROR = "SHAKAPACKER_2002",
22
+ FILE_WRITE_ERROR = "SHAKAPACKER_2003",
23
+ FILE_PERMISSION_DENIED = "SHAKAPACKER_2004",
24
+ PATH_TRAVERSAL_DETECTED = "SHAKAPACKER_2005",
25
+ INVALID_PATH = "SHAKAPACKER_2006",
26
26
 
27
27
  // Module errors (3xxx)
28
- MODULE_NOT_FOUND = 'SHAKAPACKER_3001',
29
- MODULE_LOAD_FAILED = 'SHAKAPACKER_3002',
30
- MODULE_INVALID_EXPORT = 'SHAKAPACKER_3003',
31
- LOADER_NOT_FOUND = 'SHAKAPACKER_3004',
32
- PLUGIN_NOT_FOUND = 'SHAKAPACKER_3005',
33
- PLUGIN_INVALID = 'SHAKAPACKER_3006',
28
+ MODULE_NOT_FOUND = "SHAKAPACKER_3001",
29
+ MODULE_LOAD_FAILED = "SHAKAPACKER_3002",
30
+ MODULE_INVALID_EXPORT = "SHAKAPACKER_3003",
31
+ LOADER_NOT_FOUND = "SHAKAPACKER_3004",
32
+ PLUGIN_NOT_FOUND = "SHAKAPACKER_3005",
33
+ PLUGIN_INVALID = "SHAKAPACKER_3006",
34
34
 
35
35
  // Environment errors (4xxx)
36
- ENV_INVALID_NODE_ENV = 'SHAKAPACKER_4001',
37
- ENV_MISSING_REQUIRED = 'SHAKAPACKER_4002',
38
- ENV_INVALID_VALUE = 'SHAKAPACKER_4003',
39
- ENV_SANITIZATION_REQUIRED = 'SHAKAPACKER_4004',
36
+ ENV_INVALID_NODE_ENV = "SHAKAPACKER_4001",
37
+ ENV_MISSING_REQUIRED = "SHAKAPACKER_4002",
38
+ ENV_INVALID_VALUE = "SHAKAPACKER_4003",
39
+ ENV_SANITIZATION_REQUIRED = "SHAKAPACKER_4004",
40
40
 
41
41
  // Bundler errors (5xxx)
42
- BUNDLER_UNSUPPORTED = 'SHAKAPACKER_5001',
43
- BUNDLER_CONFIG_INVALID = 'SHAKAPACKER_5002',
44
- WEBPACK_CONFIG_INVALID = 'SHAKAPACKER_5003',
45
- RSPACK_CONFIG_INVALID = 'SHAKAPACKER_5004',
46
- TRANSPILER_NOT_FOUND = 'SHAKAPACKER_5005',
47
- TRANSPILER_CONFIG_INVALID = 'SHAKAPACKER_5006',
42
+ BUNDLER_UNSUPPORTED = "SHAKAPACKER_5001",
43
+ BUNDLER_CONFIG_INVALID = "SHAKAPACKER_5002",
44
+ WEBPACK_CONFIG_INVALID = "SHAKAPACKER_5003",
45
+ RSPACK_CONFIG_INVALID = "SHAKAPACKER_5004",
46
+ TRANSPILER_NOT_FOUND = "SHAKAPACKER_5005",
47
+ TRANSPILER_CONFIG_INVALID = "SHAKAPACKER_5006",
48
48
 
49
49
  // Dev server errors (6xxx)
50
- DEVSERVER_CONFIG_INVALID = 'SHAKAPACKER_6001',
51
- DEVSERVER_PORT_INVALID = 'SHAKAPACKER_6002',
52
- DEVSERVER_PORT_IN_USE = 'SHAKAPACKER_6003',
53
- DEVSERVER_START_FAILED = 'SHAKAPACKER_6004',
50
+ DEVSERVER_CONFIG_INVALID = "SHAKAPACKER_6001",
51
+ DEVSERVER_PORT_INVALID = "SHAKAPACKER_6002",
52
+ DEVSERVER_PORT_IN_USE = "SHAKAPACKER_6003",
53
+ DEVSERVER_START_FAILED = "SHAKAPACKER_6004",
54
54
 
55
55
  // Security errors (7xxx)
56
- SECURITY_PATH_TRAVERSAL = 'SHAKAPACKER_7001',
57
- SECURITY_INVALID_INPUT = 'SHAKAPACKER_7002',
58
- SECURITY_CONTROL_CHARS = 'SHAKAPACKER_7003',
59
- SECURITY_INJECTION_ATTEMPT = 'SHAKAPACKER_7004',
56
+ SECURITY_PATH_TRAVERSAL = "SHAKAPACKER_7001",
57
+ SECURITY_INVALID_INPUT = "SHAKAPACKER_7002",
58
+ SECURITY_CONTROL_CHARS = "SHAKAPACKER_7003",
59
+ SECURITY_INJECTION_ATTEMPT = "SHAKAPACKER_7004",
60
60
 
61
61
  // Validation errors (8xxx)
62
- VALIDATION_FAILED = 'SHAKAPACKER_8001',
63
- VALIDATION_TYPE_ERROR = 'SHAKAPACKER_8002',
64
- VALIDATION_RANGE_ERROR = 'SHAKAPACKER_8003',
65
- VALIDATION_FORMAT_ERROR = 'SHAKAPACKER_8004',
66
- VALIDATION_CONSTRAINT_ERROR = 'SHAKAPACKER_8005',
62
+ VALIDATION_FAILED = "SHAKAPACKER_8001",
63
+ VALIDATION_TYPE_ERROR = "SHAKAPACKER_8002",
64
+ VALIDATION_RANGE_ERROR = "SHAKAPACKER_8003",
65
+ VALIDATION_FORMAT_ERROR = "SHAKAPACKER_8004",
66
+ VALIDATION_CONSTRAINT_ERROR = "SHAKAPACKER_8005",
67
67
 
68
68
  // Generic errors (9xxx)
69
- UNKNOWN_ERROR = 'SHAKAPACKER_9000',
70
- INTERNAL_ERROR = 'SHAKAPACKER_9001',
71
- DEPRECATED_FEATURE = 'SHAKAPACKER_9002',
72
- NOT_IMPLEMENTED = 'SHAKAPACKER_9003',
73
- OPERATION_FAILED = 'SHAKAPACKER_9004'
69
+ UNKNOWN_ERROR = "SHAKAPACKER_9000",
70
+ INTERNAL_ERROR = "SHAKAPACKER_9001",
71
+ DEPRECATED_FEATURE = "SHAKAPACKER_9002",
72
+ NOT_IMPLEMENTED = "SHAKAPACKER_9003",
73
+ OPERATION_FAILED = "SHAKAPACKER_9004"
74
74
  }
75
75
 
76
76
  /**
@@ -78,68 +78,86 @@ export enum ErrorCode {
78
78
  */
79
79
  export const ErrorMessages: Record<ErrorCode, string> = {
80
80
  // Configuration errors
81
- [ErrorCode.CONFIG_NOT_FOUND]: 'Configuration file not found: {path}',
82
- [ErrorCode.CONFIG_INVALID_YAML]: 'Invalid YAML in configuration file: {path}',
83
- [ErrorCode.CONFIG_MISSING_REQUIRED]: 'Missing required configuration field: {field}',
84
- [ErrorCode.CONFIG_VALIDATION_FAILED]: 'Configuration validation failed: {reason}',
85
- [ErrorCode.CONFIG_MERGE_FAILED]: 'Failed to merge configurations: {reason}',
86
- [ErrorCode.CONFIG_TYPE_MISMATCH]: 'Configuration type mismatch for {field}: expected {expected}, got {actual}',
81
+ [ErrorCode.CONFIG_NOT_FOUND]: "Configuration file not found: {path}",
82
+ [ErrorCode.CONFIG_INVALID_YAML]: "Invalid YAML in configuration file: {path}",
83
+ [ErrorCode.CONFIG_MISSING_REQUIRED]:
84
+ "Missing required configuration field: {field}",
85
+ [ErrorCode.CONFIG_VALIDATION_FAILED]:
86
+ "Configuration validation failed: {reason}",
87
+ [ErrorCode.CONFIG_MERGE_FAILED]: "Failed to merge configurations: {reason}",
88
+ [ErrorCode.CONFIG_TYPE_MISMATCH]:
89
+ "Configuration type mismatch for {field}: expected {expected}, got {actual}",
87
90
 
88
91
  // File system errors
89
- [ErrorCode.FILE_NOT_FOUND]: 'File not found: {path}',
90
- [ErrorCode.FILE_READ_ERROR]: 'Error reading file: {path}',
91
- [ErrorCode.FILE_WRITE_ERROR]: 'Error writing file: {path}',
92
- [ErrorCode.FILE_PERMISSION_DENIED]: 'Permission denied accessing: {path}',
93
- [ErrorCode.PATH_TRAVERSAL_DETECTED]: 'Path traversal attempt detected: {path}',
94
- [ErrorCode.INVALID_PATH]: 'Invalid path: {path}',
92
+ [ErrorCode.FILE_NOT_FOUND]: "File not found: {path}",
93
+ [ErrorCode.FILE_READ_ERROR]: "Error reading file: {path}",
94
+ [ErrorCode.FILE_WRITE_ERROR]: "Error writing file: {path}",
95
+ [ErrorCode.FILE_PERMISSION_DENIED]: "Permission denied accessing: {path}",
96
+ [ErrorCode.PATH_TRAVERSAL_DETECTED]:
97
+ "Path traversal attempt detected: {path}",
98
+ [ErrorCode.INVALID_PATH]: "Invalid path: {path}",
95
99
 
96
100
  // Module errors
97
- [ErrorCode.MODULE_NOT_FOUND]: 'Module not found: {module}',
98
- [ErrorCode.MODULE_LOAD_FAILED]: 'Failed to load module: {module}',
99
- [ErrorCode.MODULE_INVALID_EXPORT]: 'Invalid export from module: {module}',
100
- [ErrorCode.LOADER_NOT_FOUND]: 'Loader not found: {loader}',
101
- [ErrorCode.PLUGIN_NOT_FOUND]: 'Plugin not found: {plugin}',
102
- [ErrorCode.PLUGIN_INVALID]: 'Invalid plugin: {plugin}',
101
+ [ErrorCode.MODULE_NOT_FOUND]: "Module not found: {module}",
102
+ [ErrorCode.MODULE_LOAD_FAILED]: "Failed to load module: {module}",
103
+ [ErrorCode.MODULE_INVALID_EXPORT]: "Invalid export from module: {module}",
104
+ [ErrorCode.LOADER_NOT_FOUND]: "Loader not found: {loader}",
105
+ [ErrorCode.PLUGIN_NOT_FOUND]: "Plugin not found: {plugin}",
106
+ [ErrorCode.PLUGIN_INVALID]: "Invalid plugin: {plugin}",
103
107
 
104
108
  // Environment errors
105
- [ErrorCode.ENV_INVALID_NODE_ENV]: 'Invalid NODE_ENV value: {value}. Valid values are: {valid}',
106
- [ErrorCode.ENV_MISSING_REQUIRED]: 'Missing required environment variable: {variable}',
107
- [ErrorCode.ENV_INVALID_VALUE]: 'Invalid value for environment variable {variable}: {value}',
108
- [ErrorCode.ENV_SANITIZATION_REQUIRED]: 'Environment variable {variable} contained unsafe characters and was sanitized',
109
+ [ErrorCode.ENV_INVALID_NODE_ENV]:
110
+ "Invalid NODE_ENV value: {value}. Valid values are: {valid}",
111
+ [ErrorCode.ENV_MISSING_REQUIRED]:
112
+ "Missing required environment variable: {variable}",
113
+ [ErrorCode.ENV_INVALID_VALUE]:
114
+ "Invalid value for environment variable {variable}: {value}",
115
+ [ErrorCode.ENV_SANITIZATION_REQUIRED]:
116
+ "Environment variable {variable} contained unsafe characters and was sanitized",
109
117
 
110
118
  // Bundler errors
111
- [ErrorCode.BUNDLER_UNSUPPORTED]: 'Unsupported bundler: {bundler}',
112
- [ErrorCode.BUNDLER_CONFIG_INVALID]: 'Invalid bundler configuration: {reason}',
113
- [ErrorCode.WEBPACK_CONFIG_INVALID]: 'Invalid webpack configuration: {reason}',
114
- [ErrorCode.RSPACK_CONFIG_INVALID]: 'Invalid rspack configuration: {reason}',
115
- [ErrorCode.TRANSPILER_NOT_FOUND]: 'Transpiler not found: {transpiler}',
116
- [ErrorCode.TRANSPILER_CONFIG_INVALID]: 'Invalid transpiler configuration: {reason}',
119
+ [ErrorCode.BUNDLER_UNSUPPORTED]: "Unsupported bundler: {bundler}",
120
+ [ErrorCode.BUNDLER_CONFIG_INVALID]: "Invalid bundler configuration: {reason}",
121
+ [ErrorCode.WEBPACK_CONFIG_INVALID]: "Invalid webpack configuration: {reason}",
122
+ [ErrorCode.RSPACK_CONFIG_INVALID]: "Invalid rspack configuration: {reason}",
123
+ [ErrorCode.TRANSPILER_NOT_FOUND]: "Transpiler not found: {transpiler}",
124
+ [ErrorCode.TRANSPILER_CONFIG_INVALID]:
125
+ "Invalid transpiler configuration: {reason}",
117
126
 
118
127
  // Dev server errors
119
- [ErrorCode.DEVSERVER_CONFIG_INVALID]: 'Invalid dev server configuration: {reason}',
120
- [ErrorCode.DEVSERVER_PORT_INVALID]: 'Invalid port: {port}',
121
- [ErrorCode.DEVSERVER_PORT_IN_USE]: 'Port {port} is already in use',
122
- [ErrorCode.DEVSERVER_START_FAILED]: 'Failed to start dev server: {reason}',
128
+ [ErrorCode.DEVSERVER_CONFIG_INVALID]:
129
+ "Invalid dev server configuration: {reason}",
130
+ [ErrorCode.DEVSERVER_PORT_INVALID]: "Invalid port: {port}",
131
+ [ErrorCode.DEVSERVER_PORT_IN_USE]: "Port {port} is already in use",
132
+ [ErrorCode.DEVSERVER_START_FAILED]: "Failed to start dev server: {reason}",
123
133
 
124
134
  // Security errors
125
- [ErrorCode.SECURITY_PATH_TRAVERSAL]: 'Security: Path traversal attempt blocked: {path}',
126
- [ErrorCode.SECURITY_INVALID_INPUT]: 'Security: Invalid input detected: {input}',
127
- [ErrorCode.SECURITY_CONTROL_CHARS]: 'Security: Control characters detected and removed from: {field}',
128
- [ErrorCode.SECURITY_INJECTION_ATTEMPT]: 'Security: Potential injection attempt blocked: {details}',
135
+ [ErrorCode.SECURITY_PATH_TRAVERSAL]:
136
+ "Security: Path traversal attempt blocked: {path}",
137
+ [ErrorCode.SECURITY_INVALID_INPUT]:
138
+ "Security: Invalid input detected: {input}",
139
+ [ErrorCode.SECURITY_CONTROL_CHARS]:
140
+ "Security: Control characters detected and removed from: {field}",
141
+ [ErrorCode.SECURITY_INJECTION_ATTEMPT]:
142
+ "Security: Potential injection attempt blocked: {details}",
129
143
 
130
144
  // Validation errors
131
- [ErrorCode.VALIDATION_FAILED]: 'Validation failed: {reason}',
132
- [ErrorCode.VALIDATION_TYPE_ERROR]: 'Type validation error: {field} should be {type}',
133
- [ErrorCode.VALIDATION_RANGE_ERROR]: 'Value out of range: {field} must be between {min} and {max}',
134
- [ErrorCode.VALIDATION_FORMAT_ERROR]: 'Format error: {field} does not match expected format',
135
- [ErrorCode.VALIDATION_CONSTRAINT_ERROR]: 'Constraint violation: {constraint}',
145
+ [ErrorCode.VALIDATION_FAILED]: "Validation failed: {reason}",
146
+ [ErrorCode.VALIDATION_TYPE_ERROR]:
147
+ "Type validation error: {field} should be {type}",
148
+ [ErrorCode.VALIDATION_RANGE_ERROR]:
149
+ "Value out of range: {field} must be between {min} and {max}",
150
+ [ErrorCode.VALIDATION_FORMAT_ERROR]:
151
+ "Format error: {field} does not match expected format",
152
+ [ErrorCode.VALIDATION_CONSTRAINT_ERROR]: "Constraint violation: {constraint}",
136
153
 
137
154
  // Generic errors
138
- [ErrorCode.UNKNOWN_ERROR]: 'An unknown error occurred',
139
- [ErrorCode.INTERNAL_ERROR]: 'Internal error: {details}',
140
- [ErrorCode.DEPRECATED_FEATURE]: 'Deprecated feature: {feature}. Use {alternative} instead',
141
- [ErrorCode.NOT_IMPLEMENTED]: 'Feature not yet implemented: {feature}',
142
- [ErrorCode.OPERATION_FAILED]: 'Operation failed: {operation}'
155
+ [ErrorCode.UNKNOWN_ERROR]: "An unknown error occurred",
156
+ [ErrorCode.INTERNAL_ERROR]: "Internal error: {details}",
157
+ [ErrorCode.DEPRECATED_FEATURE]:
158
+ "Deprecated feature: {feature}. Use {alternative} instead",
159
+ [ErrorCode.NOT_IMPLEMENTED]: "Feature not yet implemented: {feature}",
160
+ [ErrorCode.OPERATION_FAILED]: "Operation failed: {operation}"
143
161
  }
144
162
 
145
163
  /**
@@ -149,12 +167,17 @@ export class ShakapackerError extends Error {
149
167
  public readonly code: ErrorCode
150
168
  public readonly details?: Record<string, any>
151
169
 
152
- constructor(code: ErrorCode, details?: Record<string, any>, customMessage?: string) {
153
- const template = ErrorMessages[code] || 'An error occurred'
154
- const message = customMessage || ShakapackerError.formatMessage(template, details)
170
+ constructor(
171
+ code: ErrorCode,
172
+ details?: Record<string, any>,
173
+ customMessage?: string
174
+ ) {
175
+ const template = ErrorMessages[code] || "An error occurred"
176
+ const message =
177
+ customMessage || ShakapackerError.formatMessage(template, details)
155
178
 
156
179
  super(message)
157
- this.name = 'ShakapackerError'
180
+ this.name = "ShakapackerError"
158
181
  this.code = code
159
182
  this.details = details
160
183
 
@@ -167,13 +190,16 @@ export class ShakapackerError extends Error {
167
190
  /**
168
191
  * Format error message with template values
169
192
  */
170
- private static formatMessage(template: string, details?: Record<string, any>): string {
193
+ private static formatMessage(
194
+ template: string,
195
+ details?: Record<string, any>
196
+ ): string {
171
197
  if (!details) return template
172
198
 
173
199
  return template.replace(/{(\w+)}/g, (match, key) => {
174
200
  const value = details[key]
175
201
  if (value === undefined) return match
176
- if (typeof value === 'object') {
202
+ if (typeof value === "object") {
177
203
  return JSON.stringify(value)
178
204
  }
179
205
  return String(value)
@@ -197,7 +223,10 @@ export class ShakapackerError extends Error {
197
223
  /**
198
224
  * Helper function to create a Shakapacker error
199
225
  */
200
- export function createError(code: ErrorCode, details?: Record<string, any>): ShakapackerError {
226
+ export function createError(
227
+ code: ErrorCode,
228
+ details?: Record<string, any>
229
+ ): ShakapackerError {
201
230
  return new ShakapackerError(code, details)
202
231
  }
203
232
 
@@ -216,4 +245,4 @@ export function getErrorCode(error: unknown): ErrorCode | null {
216
245
  return error.code
217
246
  }
218
247
  return null
219
- }
248
+ }
@@ -2,7 +2,7 @@
2
2
  * Error handling utilities for consistent error management
3
3
  */
4
4
 
5
- import { ErrorCode, ShakapackerError } from './errorCodes'
5
+ import { ErrorCode, ShakapackerError } from "./errorCodes"
6
6
 
7
7
  /**
8
8
  * Checks if an error is a file not found error (ENOENT)
@@ -10,9 +10,9 @@ import { ErrorCode, ShakapackerError } from './errorCodes'
10
10
  export function isFileNotFoundError(error: unknown): boolean {
11
11
  return (
12
12
  error !== null &&
13
- typeof error === 'object' &&
14
- 'code' in error &&
15
- (error as NodeJS.ErrnoException).code === 'ENOENT'
13
+ typeof error === "object" &&
14
+ "code" in error &&
15
+ (error as NodeJS.ErrnoException).code === "ENOENT"
16
16
  )
17
17
  }
18
18
 
@@ -22,9 +22,9 @@ export function isFileNotFoundError(error: unknown): boolean {
22
22
  export function isModuleNotFoundError(error: unknown): boolean {
23
23
  return (
24
24
  error !== null &&
25
- typeof error === 'object' &&
26
- 'code' in error &&
27
- (error as NodeJS.ErrnoException).code === 'MODULE_NOT_FOUND'
25
+ typeof error === "object" &&
26
+ "code" in error &&
27
+ (error as NodeJS.ErrnoException).code === "MODULE_NOT_FOUND"
28
28
  )
29
29
  }
30
30
 
@@ -32,15 +32,16 @@ export function isModuleNotFoundError(error: unknown): boolean {
32
32
  * Creates a consistent error message for file operations
33
33
  */
34
34
  export function createFileOperationError(
35
- operation: 'read' | 'write' | 'delete',
35
+ operation: "read" | "write" | "delete",
36
36
  filePath: string,
37
37
  details?: string
38
38
  ): ShakapackerError {
39
- const errorCode = operation === 'read'
40
- ? ErrorCode.FILE_READ_ERROR
41
- : operation === 'write'
42
- ? ErrorCode.FILE_WRITE_ERROR
43
- : ErrorCode.FILE_NOT_FOUND
39
+ const errorCode =
40
+ operation === "read"
41
+ ? ErrorCode.FILE_READ_ERROR
42
+ : operation === "write"
43
+ ? ErrorCode.FILE_WRITE_ERROR
44
+ : ErrorCode.FILE_NOT_FOUND
44
45
 
45
46
  return new ShakapackerError(errorCode, {
46
47
  path: filePath,
@@ -53,17 +54,18 @@ export function createFileOperationError(
53
54
  * Creates a consistent error message for file operations (backward compatibility)
54
55
  */
55
56
  export function createFileOperationErrorLegacy(
56
- operation: 'read' | 'write' | 'delete',
57
+ operation: "read" | "write" | "delete",
57
58
  filePath: string,
58
59
  details?: string
59
60
  ): Error {
60
61
  const baseMessage = `Failed to ${operation} file at path '${filePath}'`
61
- const errorDetails = details ? ` - ${details}` : ''
62
- const suggestion = operation === 'read'
63
- ? ' (check if file exists and permissions are correct)'
64
- : operation === 'write'
65
- ? ' (check write permissions and disk space)'
66
- : ' (check permissions)'
62
+ const errorDetails = details ? ` - ${details}` : ""
63
+ const suggestion =
64
+ operation === "read"
65
+ ? " (check if file exists and permissions are correct)"
66
+ : operation === "write"
67
+ ? " (check write permissions and disk space)"
68
+ : " (check permissions)"
67
69
  return new Error(`${baseMessage}${errorDetails}${suggestion}`)
68
70
  }
69
71
 
@@ -73,13 +75,15 @@ export function createFileOperationErrorLegacy(
73
75
  export function getErrorMessage(error: unknown): string {
74
76
  if (error instanceof Error) {
75
77
  // Include stack trace for better debugging in development
76
- const isDev = process.env.NODE_ENV === 'development'
77
- return isDev && error.stack ? `${error.message}\n${error.stack}` : error.message
78
+ const isDev = process.env.NODE_ENV === "development"
79
+ return isDev && error.stack
80
+ ? `${error.message}\n${error.stack}`
81
+ : error.message
78
82
  }
79
- if (typeof error === 'string') {
83
+ if (typeof error === "string") {
80
84
  return error
81
85
  }
82
- if (error && typeof error === 'object' && 'message' in error) {
86
+ if (error && typeof error === "object" && "message" in error) {
83
87
  return String((error as { message: unknown }).message)
84
88
  }
85
89
  // Provide more context for truly unknown errors
@@ -92,8 +96,8 @@ export function getErrorMessage(error: unknown): string {
92
96
  export function isNodeError(error: unknown): error is NodeJS.ErrnoException {
93
97
  return (
94
98
  error instanceof Error &&
95
- 'code' in error &&
96
- typeof (error as NodeJS.ErrnoException).code === 'string'
99
+ "code" in error &&
100
+ typeof (error as NodeJS.ErrnoException).code === "string"
97
101
  )
98
102
  }
99
103
 
@@ -115,7 +119,10 @@ export function createConfigValidationErrorWithCode(
115
119
  /**
116
120
  * Creates a module not found error
117
121
  */
118
- export function createModuleNotFoundError(moduleName: string, details?: string): ShakapackerError {
122
+ export function createModuleNotFoundError(
123
+ moduleName: string,
124
+ details?: string
125
+ ): ShakapackerError {
119
126
  return new ShakapackerError(ErrorCode.MODULE_NOT_FOUND, {
120
127
  module: moduleName,
121
128
  details
@@ -139,5 +146,3 @@ export function createPortValidationError(port: unknown): ShakapackerError {
139
146
  port: String(port)
140
147
  })
141
148
  }
142
-
143
-
@@ -10,7 +10,10 @@ interface StyleRule {
10
10
  type?: string
11
11
  }
12
12
 
13
- const getStyleRule = (test: RegExp, preprocessors: any[] = []): StyleRule | null => {
13
+ const getStyleRule = (
14
+ test: RegExp,
15
+ preprocessors: any[] = []
16
+ ): StyleRule | null => {
14
17
  if (moduleExists("css-loader")) {
15
18
  const tryPostcss = () =>
16
19
  canProcess("postcss-loader", (loaderPath: string) => ({
@@ -38,7 +41,7 @@ const getStyleRule = (test: RegExp, preprocessors: any[] = []): StyleRule | null
38
41
  // Note: css-loader requires 'camelCaseOnly' or 'dashesOnly' when namedExport is true
39
42
  // Using 'camelCase' with namedExport: true causes a build error
40
43
  namedExport: true,
41
- exportLocalsConvention: 'camelCaseOnly'
44
+ exportLocalsConvention: "camelCaseOnly"
42
45
  }
43
46
  }
44
47
  },
@@ -1,8 +1,10 @@
1
1
  const { isModuleNotFoundError, getErrorMessage } = require("./errorHelpers")
2
2
 
3
- const isBoolean = (str: string): boolean => /^true/.test(str) || /^false/.test(str)
3
+ const isBoolean = (str: string): boolean =>
4
+ /^true/.test(str) || /^false/.test(str)
4
5
 
5
- const ensureTrailingSlash = (path: string): string => (path.endsWith("/") ? path : `${path}/`)
6
+ const ensureTrailingSlash = (path: string): string =>
7
+ path.endsWith("/") ? path : `${path}/`
6
8
 
7
9
  const resolvedPath = (packageName: string): string | null => {
8
10
  try {
@@ -15,9 +17,13 @@ const resolvedPath = (packageName: string): string | null => {
15
17
  }
16
18
  }
17
19
 
18
- const moduleExists = (packageName: string): boolean => !!resolvedPath(packageName)
20
+ const moduleExists = (packageName: string): boolean =>
21
+ !!resolvedPath(packageName)
19
22
 
20
- const canProcess = <T = unknown>(rule: string, fn: (modulePath: string) => T): T | null => {
23
+ const canProcess = <T = unknown>(
24
+ rule: string,
25
+ fn: (modulePath: string) => T
26
+ ): T | null => {
21
27
  const modulePath = resolvedPath(rule)
22
28
 
23
29
  if (modulePath) {
@@ -27,7 +33,11 @@ const canProcess = <T = unknown>(rule: string, fn: (modulePath: string) => T): T
27
33
  return null
28
34
  }
29
35
 
30
- const loaderMatches = <T = unknown>(configLoader: string, loaderToCheck: string, fn: () => T): T | null => {
36
+ const loaderMatches = <T = unknown>(
37
+ configLoader: string,
38
+ loaderToCheck: string,
39
+ fn: () => T
40
+ ): T | null => {
31
41
  if (configLoader !== loaderToCheck) {
32
42
  return null
33
43
  }
@@ -37,10 +47,10 @@ const loaderMatches = <T = unknown>(configLoader: string, loaderToCheck: string,
37
47
  if (!moduleExists(loaderName)) {
38
48
  throw new Error(
39
49
  `Your Shakapacker config specified using ${configLoader}, but ${loaderName} package is not installed.\n` +
40
- `\nTo fix this issue, run one of the following commands:\n` +
41
- ` npm install --save-dev ${loaderName}\n` +
42
- ` yarn add --dev ${loaderName}\n` +
43
- `\nOr change your 'javascript_transpiler' setting in shakapacker.yml to use a different loader.`
50
+ `\nTo fix this issue, run one of the following commands:\n` +
51
+ ` npm install --save-dev ${loaderName}\n` +
52
+ ` yarn add --dev ${loaderName}\n` +
53
+ `\nOr change your 'javascript_transpiler' setting in shakapacker.yml to use a different loader.`
44
54
  )
45
55
  }
46
56
 
@@ -68,9 +78,9 @@ const packageFullVersion = (packageName: string): string => {
68
78
  }
69
79
  }
70
80
 
71
- const packageMajorVersion = (packageName: string): string => {
81
+ const packageMajorVersion = (packageName: string): number => {
72
82
  const match = packageFullVersion(packageName).match(/^\d+/)
73
- return match ? match[0] : "0"
83
+ return match ? parseInt(match[0], 10) : 0
74
84
  }
75
85
 
76
86
  export {