Skip to content

Commit

Permalink
MISC: Show user-friendly error message when there is syntax error in …
Browse files Browse the repository at this point in the history
…scripts (#1963)
  • Loading branch information
catloversg authored Feb 16, 2025
1 parent 0a4598a commit 23bc4e8
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 24 deletions.
4 changes: 2 additions & 2 deletions src/Script/RamCalculations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ function parseOnlyRamCalculate(
const scriptFileType = getFileType(script.filename);
let moduleAST;
try {
moduleAST = parseAST(script.code, scriptFileType);
moduleAST = parseAST(script.filename, script.server, script.code, scriptFileType);
} catch (error) {
return {
errorCode: RamCalculationErrorCode.ImportError,
Expand Down Expand Up @@ -552,7 +552,7 @@ export function calculateRamUsage(
): RamCalculation {
try {
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);
} catch (error) {
return {
Expand Down
2 changes: 1 addition & 1 deletion src/ScriptEditor/ui/ScriptEditorRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ function Root(props: IProps): React.ReactElement {
}
let ast;
try {
ast = parseAST(newCode, getFileType(currentScript.path));
ast = parseAST(currentScript.path, currentScript.hostname, newCode, getFileType(currentScript.path));
makeModelsForImports(ast, server);
} catch (error) {
showRAMError({
Expand Down
74 changes: 53 additions & 21 deletions src/utils/ScriptTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,32 +63,64 @@ export function getFileTypeFeature(fileType: FileType): FileTypeFeature {
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);
let ast: AST;
/**
* 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" });
} else {
const plugins = [];
if (fileTypeFeature.isReact) {
plugins.push("jsx");
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.
*/
if (fileType === FileType.JS) {
ast = acorn.parse(code, { sourceType: "module", ecmaVersion: "latest" });
} else {
const plugins = [];
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) {
plugins.push("typescript");
}
ast = babel.packages.parser.parse(code, {
sourceType: "module",
ecmaVersion: "latest",
} catch (error) {
/**
* The message of syntax errors may be cryptic for players without programming experience. For example, some players
* asked us what "Unexpected token" means. Therefore, we will catch the error here and provide a user-friendly error
* message.
*/
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
* ESTree AST format, but babel-parser uses the Babel AST format by default.
* Some browsers (e.g., Firefox, Chrome) add the "loc" property to the error object. This property provides the
* line and column numbers of the error.
*/
plugins: [["estree", { classFeatures: true }], ...plugins],
}).program;
if (
"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;
}
Expand Down

0 comments on commit 23bc4e8

Please sign in to comment.