vite-plugin-server-actions 1.0.1 → 1.2.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.
- package/README.md +162 -52
- package/index.d.ts +76 -32
- package/package.json +23 -14
- package/src/ast-parser.js +535 -0
- package/src/build-utils.js +10 -12
- package/src/dev-validator.js +272 -0
- package/src/error-enhancer.js +283 -0
- package/src/index.js +428 -80
- package/src/openapi.js +67 -29
- package/src/security.js +118 -0
- package/src/type-generator.js +378 -0
- package/src/types.ts +1 -1
- package/src/validation-runtime.js +100 -0
- package/src/validation.js +126 -21
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Development-time validation and feedback system
|
|
3
|
+
* Provides real-time feedback to developers about their server actions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createDevelopmentWarning, generateHelpfulSuggestions } from "./error-enhancer.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Validate function parameters and provide feedback
|
|
10
|
+
* @param {Object} func - Function information from AST
|
|
11
|
+
* @param {string} filePath - File path for context
|
|
12
|
+
* @returns {Array<string>} - Array of validation warnings
|
|
13
|
+
*/
|
|
14
|
+
export function validateFunctionSignature(func, filePath) {
|
|
15
|
+
const warnings = [];
|
|
16
|
+
|
|
17
|
+
// Check for proper parameter typing
|
|
18
|
+
if (func.params && func.params.length > 0) {
|
|
19
|
+
const untypedParams = func.params.filter((param) => !param.type);
|
|
20
|
+
|
|
21
|
+
if (untypedParams.length > 0) {
|
|
22
|
+
warnings.push(
|
|
23
|
+
createDevelopmentWarning(
|
|
24
|
+
"Missing Type Annotations",
|
|
25
|
+
`Parameters ${untypedParams.map((p) => p.name).join(", ")} in '${func.name}' lack type annotations`,
|
|
26
|
+
{
|
|
27
|
+
filePath,
|
|
28
|
+
suggestion: "Add TypeScript types for better development experience and type safety",
|
|
29
|
+
},
|
|
30
|
+
),
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Check for proper return type annotation
|
|
36
|
+
if (!func.returnType && func.isAsync) {
|
|
37
|
+
warnings.push(
|
|
38
|
+
createDevelopmentWarning(
|
|
39
|
+
"Missing Return Type",
|
|
40
|
+
`Async function '${func.name}' should have a return type annotation`,
|
|
41
|
+
{
|
|
42
|
+
filePath,
|
|
43
|
+
suggestion: "Add return type like: Promise<MyReturnType>",
|
|
44
|
+
},
|
|
45
|
+
),
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Check for JSDoc documentation
|
|
50
|
+
if (!func.jsdoc) {
|
|
51
|
+
warnings.push(
|
|
52
|
+
createDevelopmentWarning("Missing Documentation", `Function '${func.name}' lacks JSDoc documentation`, {
|
|
53
|
+
filePath,
|
|
54
|
+
suggestion: "Add JSDoc comments to document what this function does",
|
|
55
|
+
}),
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Check for complex parameter patterns that might be hard to serialize
|
|
60
|
+
if (func.params) {
|
|
61
|
+
const complexParams = func.params.filter((param) => param.name.includes("{") || param.name.includes("["));
|
|
62
|
+
|
|
63
|
+
if (complexParams.length > 0) {
|
|
64
|
+
warnings.push(
|
|
65
|
+
createDevelopmentWarning(
|
|
66
|
+
"Complex Parameter Destructuring",
|
|
67
|
+
`Function '${func.name}' uses complex destructuring that might be hard to serialize`,
|
|
68
|
+
{
|
|
69
|
+
filePath,
|
|
70
|
+
suggestion: "Consider using simple parameters and destructure inside the function",
|
|
71
|
+
},
|
|
72
|
+
),
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return warnings;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Validate server action file structure
|
|
82
|
+
* @param {Array} functionDetails - Array of function details
|
|
83
|
+
* @param {string} filePath - File path for context
|
|
84
|
+
* @returns {Array<string>} - Array of validation warnings
|
|
85
|
+
*/
|
|
86
|
+
export function validateFileStructure(functionDetails, filePath) {
|
|
87
|
+
const warnings = [];
|
|
88
|
+
|
|
89
|
+
// Check if file has any functions
|
|
90
|
+
if (functionDetails.length === 0) {
|
|
91
|
+
warnings.push(
|
|
92
|
+
createDevelopmentWarning("No Functions Found", "No exported functions found in server action file", {
|
|
93
|
+
filePath,
|
|
94
|
+
suggestion: "Make sure to export your functions: export async function myFunction() {}",
|
|
95
|
+
}),
|
|
96
|
+
);
|
|
97
|
+
return warnings;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Check for too many functions in one file
|
|
101
|
+
if (functionDetails.length > 10) {
|
|
102
|
+
warnings.push(
|
|
103
|
+
createDevelopmentWarning(
|
|
104
|
+
"Large File",
|
|
105
|
+
`File contains ${functionDetails.length} functions. Consider splitting into smaller modules`,
|
|
106
|
+
{
|
|
107
|
+
filePath,
|
|
108
|
+
suggestion: "Group related functions and split into multiple .server.js files",
|
|
109
|
+
},
|
|
110
|
+
),
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Check for naming consistency
|
|
115
|
+
const functionNames = functionDetails.map((fn) => fn.name);
|
|
116
|
+
const hasInconsistentNaming = checkNamingConsistency(functionNames);
|
|
117
|
+
|
|
118
|
+
if (hasInconsistentNaming) {
|
|
119
|
+
warnings.push(
|
|
120
|
+
createDevelopmentWarning("Inconsistent Naming", "Function names use inconsistent naming patterns", {
|
|
121
|
+
filePath,
|
|
122
|
+
suggestion: "Use consistent naming: camelCase (getUserById) or snake_case (get_user_by_id)",
|
|
123
|
+
}),
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return warnings;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Validate function arguments at runtime (development only)
|
|
132
|
+
* @param {string} functionName - Name of the function being called
|
|
133
|
+
* @param {Array} args - Arguments being passed
|
|
134
|
+
* @param {Object} functionInfo - Function metadata
|
|
135
|
+
* @returns {Array<string>} - Array of validation warnings
|
|
136
|
+
*/
|
|
137
|
+
export function validateRuntimeArguments(functionName, args, functionInfo) {
|
|
138
|
+
const warnings = [];
|
|
139
|
+
|
|
140
|
+
if (process.env.NODE_ENV !== "development") {
|
|
141
|
+
return warnings; // Only validate in development
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Check argument count
|
|
145
|
+
if (functionInfo && functionInfo.params) {
|
|
146
|
+
const requiredParams = functionInfo.params.filter((p) => !p.isOptional && !p.isRest);
|
|
147
|
+
const maxParams = functionInfo.params.filter((p) => !p.isRest).length;
|
|
148
|
+
|
|
149
|
+
if (args.length < requiredParams.length) {
|
|
150
|
+
warnings.push(
|
|
151
|
+
`Function '${functionName}' expects at least ${requiredParams.length} arguments, got ${args.length}`,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (args.length > maxParams && !functionInfo.params.some((p) => p.isRest)) {
|
|
156
|
+
warnings.push(`Function '${functionName}' expects at most ${maxParams} arguments, got ${args.length}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Check for non-serializable arguments
|
|
161
|
+
args.forEach((arg, index) => {
|
|
162
|
+
if (typeof arg === "function") {
|
|
163
|
+
warnings.push(`Argument ${index + 1} is a function and cannot be serialized`);
|
|
164
|
+
} else if (arg instanceof Date) {
|
|
165
|
+
warnings.push(`Argument ${index + 1} is a Date object. Consider passing as ISO string`);
|
|
166
|
+
} else if (arg instanceof RegExp) {
|
|
167
|
+
warnings.push(`Argument ${index + 1} is a RegExp and cannot be serialized`);
|
|
168
|
+
} else if (arg && typeof arg === "object" && arg.constructor !== Object && !Array.isArray(arg)) {
|
|
169
|
+
warnings.push(`Argument ${index + 1} is a custom object instance that may not serialize properly`);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
return warnings;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Generate development-time type information
|
|
178
|
+
* @param {Object} functionInfo - Function information
|
|
179
|
+
* @returns {string} - TypeScript-like type definition
|
|
180
|
+
*/
|
|
181
|
+
export function generateTypeInfo(functionInfo) {
|
|
182
|
+
const { name, params, returnType, isAsync } = functionInfo;
|
|
183
|
+
|
|
184
|
+
const paramStrings = params.map((param) => {
|
|
185
|
+
let paramStr = param.name;
|
|
186
|
+
if (param.type) {
|
|
187
|
+
paramStr += `: ${param.type}`;
|
|
188
|
+
}
|
|
189
|
+
if (param.defaultValue) {
|
|
190
|
+
paramStr += ` = ${param.defaultValue}`;
|
|
191
|
+
}
|
|
192
|
+
return paramStr;
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const returnTypeStr = returnType || "any";
|
|
196
|
+
const finalReturnType = isAsync ? `Promise<${returnTypeStr}>` : returnTypeStr;
|
|
197
|
+
|
|
198
|
+
return `function ${name}(${paramStrings.join(", ")}): ${finalReturnType}`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Check naming consistency across functions
|
|
203
|
+
* @param {Array<string>} functionNames - Array of function names
|
|
204
|
+
* @returns {boolean} - True if naming is inconsistent
|
|
205
|
+
*/
|
|
206
|
+
function checkNamingConsistency(functionNames) {
|
|
207
|
+
if (functionNames.length < 2) return false;
|
|
208
|
+
|
|
209
|
+
const camelCaseCount = functionNames.filter((name) => /^[a-z][a-zA-Z0-9]*$/.test(name)).length;
|
|
210
|
+
const snakeCaseCount = functionNames.filter((name) => /^[a-z][a-z0-9_]*$/.test(name) && name.includes("_")).length;
|
|
211
|
+
const pascalCaseCount = functionNames.filter((name) => /^[A-Z][a-zA-Z0-9]*$/.test(name)).length;
|
|
212
|
+
|
|
213
|
+
// If multiple naming styles are used significantly, it's inconsistent
|
|
214
|
+
const styles = [camelCaseCount, snakeCaseCount, pascalCaseCount].filter((count) => count > 0);
|
|
215
|
+
return styles.length > 1 && Math.max(...styles) < functionNames.length * 0.8;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Create development feedback for the console
|
|
220
|
+
* @param {Object} serverFunctions - Map of server functions
|
|
221
|
+
* @returns {string} - Formatted feedback message
|
|
222
|
+
*/
|
|
223
|
+
export function createDevelopmentFeedback(serverFunctions) {
|
|
224
|
+
let feedback = "\n[Vite Server Actions] 📋 Development Feedback:\n";
|
|
225
|
+
|
|
226
|
+
const totalFunctions = Array.from(serverFunctions.values()).reduce(
|
|
227
|
+
(sum, module) => sum + (module.functions?.length || 0),
|
|
228
|
+
0,
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
feedback += ` 📊 Found ${totalFunctions} server actions across ${serverFunctions.size} modules\n`;
|
|
232
|
+
|
|
233
|
+
// List modules and their functions
|
|
234
|
+
for (const [moduleName, moduleInfo] of serverFunctions) {
|
|
235
|
+
const { functions, filePath } = moduleInfo;
|
|
236
|
+
feedback += ` 📁 ${filePath}: ${functions.join(", ")}\n`;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
feedback += "\n 💡 Tips:\n";
|
|
240
|
+
feedback += " • Add TypeScript types for better IntelliSense\n";
|
|
241
|
+
feedback += " • Use Zod schemas for runtime validation\n";
|
|
242
|
+
feedback += " • Keep functions focused and well-documented\n";
|
|
243
|
+
|
|
244
|
+
return feedback;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Validate Zod schema attachment
|
|
249
|
+
* @param {Object} moduleExports - Exported module
|
|
250
|
+
* @param {Array} functionNames - Array of function names
|
|
251
|
+
* @param {string} filePath - File path for context
|
|
252
|
+
* @returns {Array<string>} - Array of validation suggestions
|
|
253
|
+
*/
|
|
254
|
+
export function validateSchemaAttachment(moduleExports, functionNames, filePath) {
|
|
255
|
+
const suggestions = [];
|
|
256
|
+
|
|
257
|
+
functionNames.forEach((funcName) => {
|
|
258
|
+
const func = moduleExports[funcName];
|
|
259
|
+
if (func && typeof func === "function") {
|
|
260
|
+
if (!func.schema) {
|
|
261
|
+
suggestions.push(
|
|
262
|
+
createDevelopmentWarning("Missing Validation Schema", `Function '${funcName}' has no attached Zod schema`, {
|
|
263
|
+
filePath,
|
|
264
|
+
suggestion: `Add: ${funcName}.schema = z.object({ /* your schema */ });`,
|
|
265
|
+
}),
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
return suggestions;
|
|
272
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced error handling with context and suggestions
|
|
3
|
+
* Provides developer-friendly error messages with actionable suggestions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Create an enhanced error message with context and suggestions
|
|
8
|
+
* @param {string} errorType - Type of error
|
|
9
|
+
* @param {string} originalMessage - Original error message
|
|
10
|
+
* @param {Object} context - Error context information
|
|
11
|
+
* @returns {string}
|
|
12
|
+
*/
|
|
13
|
+
export function createEnhancedError(errorType, originalMessage, context = {}) {
|
|
14
|
+
const { filePath, functionName, availableFunctions, suggestion } = context;
|
|
15
|
+
|
|
16
|
+
let enhancedMessage = `[Vite Server Actions] ${errorType}: ${originalMessage}`;
|
|
17
|
+
|
|
18
|
+
if (filePath) {
|
|
19
|
+
enhancedMessage += `\n 📁 File: ${filePath}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (functionName) {
|
|
23
|
+
enhancedMessage += `\n 🔧 Function: ${functionName}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (availableFunctions && availableFunctions.length > 0) {
|
|
27
|
+
enhancedMessage += `\n 📋 Available functions: ${availableFunctions.join(", ")}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (suggestion) {
|
|
31
|
+
enhancedMessage += `\n 💡 Suggestion: ${suggestion}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return enhancedMessage;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Enhance function not found errors
|
|
39
|
+
* @param {string} functionName - The function that wasn't found
|
|
40
|
+
* @param {string} moduleName - Module where function was expected
|
|
41
|
+
* @param {Array} availableFunctions - List of available functions
|
|
42
|
+
* @returns {Object}
|
|
43
|
+
*/
|
|
44
|
+
export function enhanceFunctionNotFoundError(functionName, moduleName, availableFunctions = []) {
|
|
45
|
+
const suggestions = [];
|
|
46
|
+
|
|
47
|
+
// Check for similar function names (typos)
|
|
48
|
+
const similarFunctions = availableFunctions.filter((fn) => levenshteinDistance(fn, functionName) <= 2);
|
|
49
|
+
|
|
50
|
+
if (similarFunctions.length > 0) {
|
|
51
|
+
suggestions.push(`Did you mean: ${similarFunctions.join(", ")}?`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Check for common naming patterns
|
|
55
|
+
const namingPatterns = [
|
|
56
|
+
{ pattern: /^get/, suggestion: "For data fetching, consider: fetch, load, or retrieve" },
|
|
57
|
+
{ pattern: /^create/, suggestion: "For creation, consider: add, insert, or save" },
|
|
58
|
+
{ pattern: /^update/, suggestion: "For updates, consider: edit, modify, or change" },
|
|
59
|
+
{ pattern: /^delete/, suggestion: "For deletion, consider: remove, destroy, or clear" },
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
const matchingPattern = namingPatterns.find((p) => p.pattern.test(functionName));
|
|
63
|
+
if (matchingPattern) {
|
|
64
|
+
suggestions.push(matchingPattern.suggestion);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (availableFunctions.length === 0) {
|
|
68
|
+
suggestions.push("No functions are exported from this module. Make sure to export your functions.");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
message: createEnhancedError(
|
|
73
|
+
"Function Not Found",
|
|
74
|
+
`Function '${functionName}' not found in module '${moduleName}'`,
|
|
75
|
+
{
|
|
76
|
+
functionName,
|
|
77
|
+
availableFunctions,
|
|
78
|
+
suggestion: suggestions.join(" "),
|
|
79
|
+
},
|
|
80
|
+
),
|
|
81
|
+
code: "FUNCTION_NOT_FOUND",
|
|
82
|
+
suggestions,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Enhance AST parsing errors
|
|
88
|
+
* @param {string} filePath - File that failed to parse
|
|
89
|
+
* @param {Error} originalError - Original parsing error
|
|
90
|
+
* @returns {Object}
|
|
91
|
+
*/
|
|
92
|
+
export function enhanceParsingError(filePath, originalError) {
|
|
93
|
+
const suggestions = [];
|
|
94
|
+
|
|
95
|
+
if (originalError.message.includes("Unexpected token")) {
|
|
96
|
+
suggestions.push("Check for syntax errors in your server action file");
|
|
97
|
+
suggestions.push("Ensure all functions are properly exported");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (originalError.message.includes("Identifier")) {
|
|
101
|
+
suggestions.push("Function names must be valid JavaScript identifiers");
|
|
102
|
+
suggestions.push("Function names cannot start with numbers or contain special characters");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (originalError.message.includes("duplicate")) {
|
|
106
|
+
suggestions.push("Each function name must be unique within the same file");
|
|
107
|
+
suggestions.push("Consider renaming duplicate functions or using different export patterns");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
message: createEnhancedError("Parsing Error", `Failed to parse server action file: ${originalError.message}`, {
|
|
112
|
+
filePath,
|
|
113
|
+
suggestion: suggestions.join(" "),
|
|
114
|
+
}),
|
|
115
|
+
code: "PARSE_ERROR",
|
|
116
|
+
suggestions,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Enhance validation errors
|
|
122
|
+
* @param {Array} validationErrors - Array of validation errors
|
|
123
|
+
* @param {string} functionName - Function that failed validation
|
|
124
|
+
* @returns {Object}
|
|
125
|
+
*/
|
|
126
|
+
export function enhanceValidationError(validationErrors, functionName) {
|
|
127
|
+
const suggestions = [];
|
|
128
|
+
|
|
129
|
+
// Analyze common validation patterns
|
|
130
|
+
const hasTypeErrors = validationErrors.some(
|
|
131
|
+
(err) => err.message.includes("Expected") || err.message.includes("Invalid"),
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
if (hasTypeErrors) {
|
|
135
|
+
suggestions.push("Check the types of arguments you're passing to the function");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const hasRequiredErrors = validationErrors.some(
|
|
139
|
+
(err) => err.message.includes("required") || err.message.includes("missing"),
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
if (hasRequiredErrors) {
|
|
143
|
+
suggestions.push("Make sure all required parameters are provided");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const hasFormatErrors = validationErrors.some(
|
|
147
|
+
(err) => err.message.includes("format") || err.message.includes("pattern"),
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
if (hasFormatErrors) {
|
|
151
|
+
suggestions.push("Check the format of string inputs (email, URL, etc.)");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
message: createEnhancedError("Validation Error", `Validation failed for function '${functionName}'`, {
|
|
156
|
+
functionName,
|
|
157
|
+
suggestion: suggestions.join(" "),
|
|
158
|
+
}),
|
|
159
|
+
code: "VALIDATION_ERROR",
|
|
160
|
+
suggestions,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Enhance module loading errors
|
|
166
|
+
* @param {string} modulePath - Path to module that failed to load
|
|
167
|
+
* @param {Error} originalError - Original loading error
|
|
168
|
+
* @returns {Object}
|
|
169
|
+
*/
|
|
170
|
+
export function enhanceModuleLoadError(modulePath, originalError) {
|
|
171
|
+
const suggestions = [];
|
|
172
|
+
|
|
173
|
+
if (originalError.code === "ENOENT") {
|
|
174
|
+
suggestions.push("Make sure the file exists and the path is correct");
|
|
175
|
+
suggestions.push("Check that your build process hasn't moved or renamed the file");
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (originalError.message.includes("import")) {
|
|
179
|
+
suggestions.push("Verify all import statements in your server action file");
|
|
180
|
+
suggestions.push("Make sure imported modules are installed and available");
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (originalError.message.includes("export")) {
|
|
184
|
+
suggestions.push("Ensure your functions are properly exported");
|
|
185
|
+
suggestions.push("Use 'export function' or 'export const' for your server actions");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
message: createEnhancedError("Module Load Error", `Failed to load server action module: ${originalError.message}`, {
|
|
190
|
+
filePath: modulePath,
|
|
191
|
+
suggestion: suggestions.join(" "),
|
|
192
|
+
}),
|
|
193
|
+
code: "MODULE_LOAD_ERROR",
|
|
194
|
+
suggestions,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Enhance development warnings with helpful context
|
|
200
|
+
* @param {string} warningType - Type of warning
|
|
201
|
+
* @param {string} message - Warning message
|
|
202
|
+
* @param {Object} context - Additional context
|
|
203
|
+
* @returns {string}
|
|
204
|
+
*/
|
|
205
|
+
export function createDevelopmentWarning(warningType, message, context = {}) {
|
|
206
|
+
let warning = `[Vite Server Actions] ⚠️ ${warningType}: ${message}`;
|
|
207
|
+
|
|
208
|
+
if (context.filePath) {
|
|
209
|
+
warning += `\n 📁 File: ${context.filePath}`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (context.suggestion) {
|
|
213
|
+
warning += `\n 💡 Tip: ${context.suggestion}`;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return warning;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Calculate Levenshtein distance between two strings
|
|
221
|
+
* @param {string} a - First string
|
|
222
|
+
* @param {string} b - Second string
|
|
223
|
+
* @returns {number}
|
|
224
|
+
*/
|
|
225
|
+
function levenshteinDistance(a, b) {
|
|
226
|
+
const matrix = [];
|
|
227
|
+
|
|
228
|
+
for (let i = 0; i <= b.length; i++) {
|
|
229
|
+
matrix[i] = [i];
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
for (let j = 0; j <= a.length; j++) {
|
|
233
|
+
matrix[0][j] = j;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
for (let i = 1; i <= b.length; i++) {
|
|
237
|
+
for (let j = 1; j <= a.length; j++) {
|
|
238
|
+
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
239
|
+
matrix[i][j] = matrix[i - 1][j - 1];
|
|
240
|
+
} else {
|
|
241
|
+
matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j] + 1);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return matrix[b.length][a.length];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Generate helpful suggestions based on common mistakes
|
|
251
|
+
* @param {string} errorContext - Context where error occurred
|
|
252
|
+
* @param {Object} additionalInfo - Additional information about the error
|
|
253
|
+
* @returns {Array<string>}
|
|
254
|
+
*/
|
|
255
|
+
export function generateHelpfulSuggestions(errorContext, additionalInfo = {}) {
|
|
256
|
+
const suggestions = [];
|
|
257
|
+
|
|
258
|
+
switch (errorContext) {
|
|
259
|
+
case "no-functions-found":
|
|
260
|
+
suggestions.push("Make sure your functions are exported: export async function myFunction() {}");
|
|
261
|
+
suggestions.push("Check that your file ends with .server.js or .server.ts");
|
|
262
|
+
suggestions.push("Verify the file is in a location matched by your include patterns");
|
|
263
|
+
break;
|
|
264
|
+
|
|
265
|
+
case "async-function-required":
|
|
266
|
+
suggestions.push("Server actions should be async functions");
|
|
267
|
+
suggestions.push("Change 'export function' to 'export async function'");
|
|
268
|
+
break;
|
|
269
|
+
|
|
270
|
+
case "invalid-arguments":
|
|
271
|
+
suggestions.push("All function arguments must be JSON-serializable");
|
|
272
|
+
suggestions.push("Functions, classes, and other complex objects cannot be passed");
|
|
273
|
+
suggestions.push("Consider passing plain objects, arrays, strings, and numbers only");
|
|
274
|
+
break;
|
|
275
|
+
|
|
276
|
+
case "type-safety":
|
|
277
|
+
suggestions.push("Add TypeScript types to your server actions for better development experience");
|
|
278
|
+
suggestions.push("Use Zod schemas for runtime validation");
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return suggestions;
|
|
283
|
+
}
|