MISC: Show user-friendly error message when there is syntax error in scripts (#1963)

This commit is contained in:
catloversg
2025-02-16 16:27:26 +07:00
committed by GitHub
parent 0a4598a9a0
commit 23bc4e8804
3 changed files with 56 additions and 24 deletions
+2 -2
View File
@@ -146,7 +146,7 @@ function parseOnlyRamCalculate(
const scriptFileType = getFileType(script.filename); const scriptFileType = getFileType(script.filename);
let moduleAST; let moduleAST;
try { try {
moduleAST = parseAST(script.code, scriptFileType); moduleAST = parseAST(script.filename, script.server, script.code, scriptFileType);
} catch (error) { } catch (error) {
return { return {
errorCode: RamCalculationErrorCode.ImportError, errorCode: RamCalculationErrorCode.ImportError,
@@ -552,7 +552,7 @@ export function calculateRamUsage(
): RamCalculation { ): RamCalculation {
try { try {
const fileType = getFileType(scriptName); const fileType = getFileType(scriptName);
const ast = typeof input === "string" ? parseAST(input, fileType) : input; const ast = typeof input === "string" ? parseAST(scriptName, server, input, fileType) : input;
return parseOnlyRamCalculate(ast, scriptName, server, getFileTypeFeature(fileType), otherScripts); return parseOnlyRamCalculate(ast, scriptName, server, getFileTypeFeature(fileType), otherScripts);
} catch (error) { } catch (error) {
return { return {
+1 -1
View File
@@ -276,7 +276,7 @@ function Root(props: IProps): React.ReactElement {
} }
let ast; let ast;
try { try {
ast = parseAST(newCode, getFileType(currentScript.path)); ast = parseAST(currentScript.path, currentScript.hostname, newCode, getFileType(currentScript.path));
makeModelsForImports(ast, server); makeModelsForImports(ast, server);
} catch (error) { } catch (error) {
showRAMError({ showRAMError({
+53 -21
View File
@@ -63,32 +63,64 @@ export function getFileTypeFeature(fileType: FileType): FileTypeFeature {
return result; return result;
} }
export function parseAST(code: string, fileType: FileType): AST { export function parseAST(scriptName: string, hostname: string, code: string, fileType: FileType): AST {
const fileTypeFeature = getFileTypeFeature(fileType); const fileTypeFeature = getFileTypeFeature(fileType);
let ast: AST; let ast: AST;
/** try {
* acorn is much faster than babel-parser, especially when parsing many big JS files, so we use it to parse the AST of /**
* JS code. babel-parser is only useful when we have to parse JSX and TypeScript. * acorn is much faster than babel-parser, especially when parsing many big JS files, so we use it to parse the AST of
*/ * JS code. babel-parser is only useful when we have to parse JSX and TypeScript.
if (fileType === FileType.JS) { */
ast = acorn.parse(code, { sourceType: "module", ecmaVersion: "latest" }); if (fileType === FileType.JS) {
} else { ast = acorn.parse(code, { sourceType: "module", ecmaVersion: "latest" });
const plugins = []; } else {
if (fileTypeFeature.isReact) { const plugins = [];
plugins.push("jsx"); if (fileTypeFeature.isReact) {
plugins.push("jsx");
}
if (fileTypeFeature.isTypeScript) {
plugins.push("typescript");
}
ast = babel.packages.parser.parse(code, {
sourceType: "module",
ecmaVersion: "latest",
/**
* The usage of the "estree" plugin is mandatory. We use acorn-walk to walk the AST. acorn-walk only supports the
* ESTree AST format, but babel-parser uses the Babel AST format by default.
*/
plugins: [["estree", { classFeatures: true }], ...plugins],
}).program;
} }
if (fileTypeFeature.isTypeScript) { } catch (error) {
plugins.push("typescript"); /**
} * The message of syntax errors may be cryptic for players without programming experience. For example, some players
ast = babel.packages.parser.parse(code, { * asked us what "Unexpected token" means. Therefore, we will catch the error here and provide a user-friendly error
sourceType: "module", * message.
ecmaVersion: "latest", */
if (error instanceof SyntaxError) {
let errorLocation = "unknown";
/** /**
* The usage of the "estree" plugin is mandatory. We use acorn-walk to walk the AST. acorn-walk only supports the * Some browsers (e.g., Firefox, Chrome) add the "loc" property to the error object. This property provides the
* ESTree AST format, but babel-parser uses the Babel AST format by default. * line and column numbers of the error.
*/ */
plugins: [["estree", { classFeatures: true }], ...plugins], if (
}).program; "loc" in error &&
error.loc &&
typeof error.loc === "object" &&
"line" in error.loc &&
"column" in error.loc
) {
errorLocation = `Line ${error.loc.line}, Column: ${error.loc.column}`;
}
throw new Error(
`Syntax error in ${scriptName}, server: ${hostname}. Error location: ${errorLocation}. Error message: ${error.message}.`,
{
cause: error,
},
);
} else {
throw error;
}
} }
return ast; return ast;
} }