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.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.md +6 -9
- data/.github/ISSUE_TEMPLATE/feature_request.md +6 -8
- data/.github/workflows/claude-code-review.yml +4 -5
- data/.github/workflows/claude.yml +1 -2
- data/.github/workflows/dummy.yml +4 -4
- data/.github/workflows/generator.yml +9 -9
- data/.github/workflows/node.yml +11 -2
- data/.github/workflows/ruby.yml +16 -16
- data/.github/workflows/test-bundlers.yml +9 -9
- data/.gitignore +4 -0
- data/CHANGELOG.md +19 -4
- data/CLAUDE.md +6 -1
- data/CONTRIBUTING.md +0 -1
- data/Gemfile.lock +1 -1
- data/README.md +14 -14
- data/TODO.md +10 -2
- data/TODO_v9.md +13 -3
- data/bin/export-bundler-config +1 -1
- data/conductor-setup.sh +1 -1
- data/conductor.json +1 -1
- data/docs/cdn_setup.md +13 -8
- data/docs/common-upgrades.md +2 -1
- data/docs/configuration.md +630 -0
- data/docs/css-modules-export-mode.md +120 -100
- data/docs/customizing_babel_config.md +16 -16
- data/docs/deployment.md +18 -0
- data/docs/developing_shakapacker.md +6 -0
- data/docs/optional-peer-dependencies.md +9 -4
- data/docs/peer-dependencies.md +17 -6
- data/docs/precompile_hook.md +342 -0
- data/docs/react.md +57 -47
- data/docs/releasing.md +0 -2
- data/docs/rspack.md +25 -21
- data/docs/rspack_migration_guide.md +335 -8
- data/docs/sprockets.md +1 -0
- data/docs/style_loader_vs_mini_css.md +12 -12
- data/docs/subresource_integrity.md +13 -7
- data/docs/transpiler-performance.md +40 -19
- data/docs/troubleshooting.md +0 -2
- data/docs/typescript-migration.md +48 -39
- data/docs/typescript.md +12 -8
- data/docs/using_esbuild_loader.md +10 -10
- data/docs/v6_upgrade.md +33 -20
- data/docs/v7_upgrade.md +8 -6
- data/docs/v8_upgrade.md +13 -12
- data/docs/v9_upgrade.md +2 -1
- data/eslint.config.fast.js +134 -0
- data/eslint.config.js +140 -0
- data/knip.ts +54 -0
- data/lib/install/bin/export-bundler-config +1 -1
- data/lib/install/config/shakapacker.yml +16 -5
- data/lib/shakapacker/compiler.rb +80 -0
- data/lib/shakapacker/configuration.rb +33 -5
- data/lib/shakapacker/dev_server_runner.rb +140 -1
- data/lib/shakapacker/doctor.rb +294 -65
- data/lib/shakapacker/instance.rb +8 -3
- data/lib/shakapacker/runner.rb +244 -8
- data/lib/shakapacker/version.rb +1 -1
- data/lib/tasks/shakapacker/doctor.rake +42 -2
- data/package/babel/preset.ts +7 -4
- data/package/config.ts +42 -30
- data/package/configExporter/cli.ts +799 -208
- data/package/configExporter/configFile.ts +520 -0
- data/package/configExporter/fileWriter.ts +12 -8
- data/package/configExporter/index.ts +9 -1
- data/package/configExporter/types.ts +36 -2
- data/package/configExporter/yamlSerializer.ts +22 -8
- data/package/dev_server.ts +1 -1
- data/package/environments/__type-tests__/rspack-plugin-compatibility.ts +11 -5
- data/package/environments/base.ts +18 -13
- data/package/environments/development.ts +1 -1
- data/package/environments/production.ts +4 -1
- data/package/index.d.ts +50 -3
- data/package/index.d.ts.template +50 -0
- data/package/index.ts +7 -7
- data/package/loaders.d.ts +2 -2
- data/package/optimization/rspack.ts +1 -1
- data/package/plugins/rspack.ts +15 -4
- data/package/plugins/webpack.ts +7 -3
- data/package/rspack/index.ts +10 -2
- data/package/rules/raw.ts +3 -2
- data/package/rules/sass.ts +1 -1
- data/package/types/README.md +15 -13
- data/package/types/index.ts +5 -5
- data/package/types.ts +0 -1
- data/package/utils/defaultConfigPath.ts +4 -1
- data/package/utils/errorCodes.ts +129 -100
- data/package/utils/errorHelpers.ts +34 -29
- data/package/utils/getStyleRule.ts +5 -2
- data/package/utils/helpers.ts +21 -11
- data/package/utils/pathValidation.ts +43 -35
- data/package/utils/requireOrError.ts +1 -1
- data/package/utils/snakeToCamelCase.ts +1 -1
- data/package/utils/typeGuards.ts +132 -83
- data/package/utils/validateDependencies.ts +1 -1
- data/package/webpack-types.d.ts +3 -3
- data/package/webpackDevServerConfig.ts +22 -10
- data/package-lock.json +2 -2
- data/package.json +36 -28
- data/scripts/type-check-no-emit.js +1 -1
- data/test/configExporter/configFile.test.js +392 -0
- data/test/configExporter/integration.test.js +275 -0
- data/test/helpers.js +1 -1
- data/test/package/configExporter.test.js +154 -0
- data/test/package/helpers.test.js +2 -2
- data/test/package/rules/sass-version-parsing.test.js +71 -0
- data/test/package/rules/sass.test.js +2 -4
- data/test/package/rules/sass1.test.js +1 -3
- data/test/package/rules/sass16.test.js +23 -0
- data/tools/README.md +15 -5
- data/tsconfig.eslint.json +2 -9
- data/yarn.lock +1894 -1492
- metadata +19 -3
- data/.eslintignore +0 -5
data/package/utils/errorCodes.ts
CHANGED
@@ -9,68 +9,68 @@
|
|
9
9
|
*/
|
10
10
|
export enum ErrorCode {
|
11
11
|
// Configuration errors (1xxx)
|
12
|
-
CONFIG_NOT_FOUND =
|
13
|
-
CONFIG_INVALID_YAML =
|
14
|
-
CONFIG_MISSING_REQUIRED =
|
15
|
-
CONFIG_VALIDATION_FAILED =
|
16
|
-
CONFIG_MERGE_FAILED =
|
17
|
-
CONFIG_TYPE_MISMATCH =
|
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 =
|
21
|
-
FILE_READ_ERROR =
|
22
|
-
FILE_WRITE_ERROR =
|
23
|
-
FILE_PERMISSION_DENIED =
|
24
|
-
PATH_TRAVERSAL_DETECTED =
|
25
|
-
INVALID_PATH =
|
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 =
|
29
|
-
MODULE_LOAD_FAILED =
|
30
|
-
MODULE_INVALID_EXPORT =
|
31
|
-
LOADER_NOT_FOUND =
|
32
|
-
PLUGIN_NOT_FOUND =
|
33
|
-
PLUGIN_INVALID =
|
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 =
|
37
|
-
ENV_MISSING_REQUIRED =
|
38
|
-
ENV_INVALID_VALUE =
|
39
|
-
ENV_SANITIZATION_REQUIRED =
|
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 =
|
43
|
-
BUNDLER_CONFIG_INVALID =
|
44
|
-
WEBPACK_CONFIG_INVALID =
|
45
|
-
RSPACK_CONFIG_INVALID =
|
46
|
-
TRANSPILER_NOT_FOUND =
|
47
|
-
TRANSPILER_CONFIG_INVALID =
|
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 =
|
51
|
-
DEVSERVER_PORT_INVALID =
|
52
|
-
DEVSERVER_PORT_IN_USE =
|
53
|
-
DEVSERVER_START_FAILED =
|
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 =
|
57
|
-
SECURITY_INVALID_INPUT =
|
58
|
-
SECURITY_CONTROL_CHARS =
|
59
|
-
SECURITY_INJECTION_ATTEMPT =
|
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 =
|
63
|
-
VALIDATION_TYPE_ERROR =
|
64
|
-
VALIDATION_RANGE_ERROR =
|
65
|
-
VALIDATION_FORMAT_ERROR =
|
66
|
-
VALIDATION_CONSTRAINT_ERROR =
|
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 =
|
70
|
-
INTERNAL_ERROR =
|
71
|
-
DEPRECATED_FEATURE =
|
72
|
-
NOT_IMPLEMENTED =
|
73
|
-
OPERATION_FAILED =
|
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]:
|
82
|
-
[ErrorCode.CONFIG_INVALID_YAML]:
|
83
|
-
[ErrorCode.CONFIG_MISSING_REQUIRED]:
|
84
|
-
|
85
|
-
[ErrorCode.
|
86
|
-
|
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]:
|
90
|
-
[ErrorCode.FILE_READ_ERROR]:
|
91
|
-
[ErrorCode.FILE_WRITE_ERROR]:
|
92
|
-
[ErrorCode.FILE_PERMISSION_DENIED]:
|
93
|
-
[ErrorCode.PATH_TRAVERSAL_DETECTED]:
|
94
|
-
|
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]:
|
98
|
-
[ErrorCode.MODULE_LOAD_FAILED]:
|
99
|
-
[ErrorCode.MODULE_INVALID_EXPORT]:
|
100
|
-
[ErrorCode.LOADER_NOT_FOUND]:
|
101
|
-
[ErrorCode.PLUGIN_NOT_FOUND]:
|
102
|
-
[ErrorCode.PLUGIN_INVALID]:
|
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]:
|
106
|
-
|
107
|
-
[ErrorCode.
|
108
|
-
|
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]:
|
112
|
-
[ErrorCode.BUNDLER_CONFIG_INVALID]:
|
113
|
-
[ErrorCode.WEBPACK_CONFIG_INVALID]:
|
114
|
-
[ErrorCode.RSPACK_CONFIG_INVALID]:
|
115
|
-
[ErrorCode.TRANSPILER_NOT_FOUND]:
|
116
|
-
[ErrorCode.TRANSPILER_CONFIG_INVALID]:
|
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]:
|
120
|
-
|
121
|
-
[ErrorCode.
|
122
|
-
[ErrorCode.
|
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]:
|
126
|
-
|
127
|
-
[ErrorCode.
|
128
|
-
|
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]:
|
132
|
-
[ErrorCode.VALIDATION_TYPE_ERROR]:
|
133
|
-
|
134
|
-
[ErrorCode.
|
135
|
-
|
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]:
|
139
|
-
[ErrorCode.INTERNAL_ERROR]:
|
140
|
-
[ErrorCode.DEPRECATED_FEATURE]:
|
141
|
-
|
142
|
-
[ErrorCode.
|
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(
|
153
|
-
|
154
|
-
|
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 =
|
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(
|
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 ===
|
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(
|
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
|
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 ===
|
14
|
-
|
15
|
-
(error as NodeJS.ErrnoException).code ===
|
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 ===
|
26
|
-
|
27
|
-
(error as NodeJS.ErrnoException).code ===
|
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:
|
35
|
+
operation: "read" | "write" | "delete",
|
36
36
|
filePath: string,
|
37
37
|
details?: string
|
38
38
|
): ShakapackerError {
|
39
|
-
const errorCode =
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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:
|
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 =
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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 ===
|
77
|
-
return isDev && error.stack
|
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 ===
|
83
|
+
if (typeof error === "string") {
|
80
84
|
return error
|
81
85
|
}
|
82
|
-
if (error && typeof 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
|
-
|
96
|
-
typeof (error as NodeJS.ErrnoException).code ===
|
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(
|
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 = (
|
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:
|
44
|
+
exportLocalsConvention: "camelCaseOnly"
|
42
45
|
}
|
43
46
|
}
|
44
47
|
},
|
data/package/utils/helpers.ts
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
const { isModuleNotFoundError, getErrorMessage } = require("./errorHelpers")
|
2
2
|
|
3
|
-
const isBoolean = (str: string): boolean =>
|
3
|
+
const isBoolean = (str: string): boolean =>
|
4
|
+
/^true/.test(str) || /^false/.test(str)
|
4
5
|
|
5
|
-
const ensureTrailingSlash = (path: string): string =>
|
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 =>
|
20
|
+
const moduleExists = (packageName: string): boolean =>
|
21
|
+
!!resolvedPath(packageName)
|
19
22
|
|
20
|
-
const canProcess = <T = unknown>(
|
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>(
|
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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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):
|
81
|
+
const packageMajorVersion = (packageName: string): number => {
|
72
82
|
const match = packageFullVersion(packageName).match(/^\d+/)
|
73
|
-
return match ? match[0] :
|
83
|
+
return match ? parseInt(match[0], 10) : 0
|
74
84
|
}
|
75
85
|
|
76
86
|
export {
|