Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion mobile-app/lib/service/learn/learn_file_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,12 @@ class LearnFileService {
if (babelRes?.error != null) {
throw Exception('Babel transpilation failed: ${babelRes?.error}');
}
fileContents = babelRes?.value;

final result = babelRes?.value as Map<dynamic, dynamic>?;
if (result?['success'] == false) {
throw Exception('Babel transpilation failed: ${result?['error']}');
}
fileContents = result?['code'];

Document document = parse(challengeContent);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -702,13 +702,34 @@ class ChallengeViewModel extends BaseViewModel {
_userConsoleMessages = [];
setTestConsoleMessages = ['<p>// running tests</p>'];

// Get user code console messages
String userCode;
try {
userCode = await builder.buildUserCode(
challenge!,
_babelWebView.webViewController,
);
} catch (e) {
String errorMessage = e.toString();
if (errorMessage.contains('Babel transpilation failed')) {
errorMessage = errorMessage.replaceFirst('Exception: ', '');
}
String userFriendlyMessage = parseSyntaxError(errorMessage);

setPanelType = PanelType.hint;
setHint = '<p>$userFriendlyMessage</p>';
setTestConsoleMessages = [
...testConsoleMessages,
'<p>$userFriendlyMessage</p>',
'<p>// tests completed</p>',
];
setIsRunningTests = false;
_scaffoldKey.currentState?.openEndDrawer();
return;
}

if ([1, 26, 28].contains(challenge!.challengeType)) {
final evalResult = await testController!.callAsyncJavaScript(
functionBody: await builder.buildUserCode(
challenge!,
_babelWebView.webViewController,
),
functionBody: userCode,
);
if (evalResult != null && evalResult.error != null) {
setUserConsoleMessages = [
Expand All @@ -724,10 +745,7 @@ class ChallengeViewModel extends BaseViewModel {
final updateTestRunnerRes = await testController!.callAsyncJavaScript(
functionBody: ScriptBuilder.runnerScript,
arguments: {
'userCode': await builder.buildUserCode(
challenge!,
_babelWebView.webViewController,
),
'userCode': userCode,
'workerType': builder.getWorkerType(challenge!.challengeType),
'combinedCode': await builder.combinedCode(challenge!),
'editableRegionContent': editableRegionContent,
Expand Down
20 changes: 17 additions & 3 deletions mobile-app/lib/ui/views/learn/test_runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,17 @@ import 'package:freecodecamp/service/learn/learn_file_service.dart';
class ScriptBuilder {
final LearnFileService fileService = locator<LearnFileService>();

static const transpileScript =
'return Babel.transform(code, { presets: ["env"] }).code';
static const transpileScript = '''
try {
return { success: true, code: Babel.transform(code, { presets: ["env"] }).code };
} catch (e) {
let errorMsg = e.message || e.toString();
if (e.loc) {
errorMsg = "SyntaxError: " + e.message + " (" + e.loc.line + ":" + e.loc.column + ")";
}
return { success: false, error: errorMsg };
}
''';

static const hideFccHeaderStyle = '''
<style class="fcc-hide-header">
Expand Down Expand Up @@ -71,7 +80,12 @@ return testRes;
throw Exception('Babel transpilation failed: ${babelRes?.error}');
}

return babelRes?.value ?? challengeFile;
final result = babelRes?.value as Map<dynamic, dynamic>?;
if (result?['success'] == false) {
throw Exception('Babel transpilation failed: ${result?['error']}');
}

return result?['code'] ?? challengeFile;
case 20:
case 23:
case 27:
Expand Down
16 changes: 16 additions & 0 deletions mobile-app/lib/ui/views/learn/utils/challenge_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,19 @@ DateTime parseMonthYear(String monthYear) {
String formatChallengeDate(DateTime date) {
return '${date.year.toString().padLeft(4, '0')}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
}

String parseSyntaxError(String errorMessage) {
final syntaxErrorRegex = RegExp(r'SyntaxError:.*?\((\d+):(\d+)\)');
final match = syntaxErrorRegex.firstMatch(errorMessage);

if (match != null) {
final line = int.parse(match.group(1)!) - 1;
return 'There is a syntax error on line $line. Please check for missing brackets, quotes, or other syntax issues.';
}

if (errorMessage.contains('Babel transpilation failed')) {
return 'There is a syntax error in your code. Please check for missing brackets, quotes, or other syntax issues.';
}

return errorMessage;
}
36 changes: 36 additions & 0 deletions mobile-app/test/unit/challenge_utils_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,40 @@ void main() {
expect(formatChallengeDate(date), '2025-12-31');
});
});

group('parseSyntaxError', () {
test('parses syntax error with line number', () {
final errorMessage =
'Exception: Babel transpilation failed: SyntaxError: unknown: Unexpected token (4:0)';
final result = parseSyntaxError(errorMessage);
expect(
result,
'There is a syntax error on line 3. Please check for missing brackets, quotes, or other syntax issues.',
);
});

test('parses syntax error with different line number', () {
final errorMessage = 'SyntaxError: Unexpected identifier (15:22)';
final result = parseSyntaxError(errorMessage);
expect(
result,
'There is a syntax error on line 14. Please check for missing brackets, quotes, or other syntax issues.',
);
});

test('returns generic message for Babel error without line numbers', () {
final errorMessage = 'Babel transpilation failed: unknown error';
final result = parseSyntaxError(errorMessage);
expect(
result,
'There is a syntax error in your code. Please check for missing brackets, quotes, or other syntax issues.',
);
});

test('returns original message for non-syntax errors', () {
final errorMessage = 'Some other error occurred';
final result = parseSyntaxError(errorMessage);
expect(result, 'Some other error occurred');
});
});
}
Loading