• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

source-academy / js-slang / 24834367427

23 Apr 2026 12:09PM UTC coverage: 78.541% (+0.2%) from 78.391%
24834367427

Pull #1893

github

web-flow
Merge ab101147d into 715603479
Pull Request #1893: Error Handling and Stringify Changes

3126 of 4197 branches covered (74.48%)

Branch coverage included in aggregate %.

801 of 975 new or added lines in 76 files covered. (82.15%)

20 existing lines in 11 files now uncovered.

7056 of 8767 relevant lines covered (80.48%)

173930.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.27
/src/repl/utils.ts
1
import fs from 'fs/promises';
2
import { Option } from '@commander-js/extra-typings';
3
import { parseError } from '..';
4
import {
5
  Chapter,
6
  isSupportedLanguageCombo,
7
  Language,
8
  LanguageOptions,
9
  SourceLanguages,
10
  Variant,
11
} from '../langs';
12
import type { Context, Result } from '../types';
13
import { getChapterName, getVariantName, objectKeys } from '../utils/misc';
14
import { stringify } from '../utils/stringify';
15
import { GeneralRuntimeError } from '../errors/base';
16
import type { FileGetter } from '../modules/moduleTypes';
17

18
/**
19
 * Converts a string to the corresponding Source chapter. Throws an error if the string
20
 * doesn't represent a valid string.
21
 */
22
export function chapterParser(str: string): Chapter {
23
  let foundChapter: string | undefined;
24

25
  if (/^-?[0-9]+$/.test(str)) {
10✔
26
    // Chapter is fully numeric
27
    const value = parseInt(str);
5✔
28
    foundChapter = objectKeys(Chapter).find(chapterName => Chapter[chapterName] === value);
50✔
29

30
    if (foundChapter === undefined) {
5✔
31
      throw new GeneralRuntimeError(`Invalid chapter value: ${str}`);
1✔
32
    }
33
  } else {
34
    foundChapter = str;
5✔
35
  }
36

37
  if (foundChapter in Chapter) {
9✔
38
    return Chapter[foundChapter as keyof typeof Chapter];
8✔
39
  }
40
  throw new GeneralRuntimeError(`Invalid chapter value: ${str}`);
1✔
41
}
42

43
/**
44
 * Returns an {@link Option} for selecting Source chapters.
45
 */
46
export function getChapterOption(): Option<
47
  '--chapter <chapter>',
48
  undefined,
49
  Chapter.SOURCE_4,
50
  undefined,
51
  false,
52
  Chapter
53
>;
54
export function getChapterOption<T extends Chapter, U extends T>(
55
  defaultValue: U,
56
  argParser: (value: string) => T,
57
): Option<'--chapter <chapter>', undefined, U, undefined, false, T>;
58
export function getChapterOption(
59
  defaultValue: Chapter = Chapter.SOURCE_4,
24✔
60
  argParser: (value: string) => Chapter = chapterParser,
24✔
61
) {
62
  return new Option('--chapter <chapter>').default(defaultValue).argParser(argParser);
24✔
63
}
64

65
export function getVariantOption<T extends Variant>(defaultValue: T, choices: T[]) {
66
  return new Option('--variant <variant>').default(defaultValue).choices(choices);
17✔
67
}
68

69
export const getLanguageOption = <T extends LanguageOptions>() => {
4✔
70
  return new Option('--languageOptions <options>')
17✔
71
    .default({})
72
    .argParser((value: string): LanguageOptions => {
73
      const languageOptions = value.split(',').map(lang => {
×
74
        const [key, value] = lang.split('=');
×
75
        return { [key]: value };
×
76
      });
77
      return Object.assign({}, ...languageOptions);
×
78
    });
79
};
80

81
export function handleResult(result: Result, context: Context, verboseErrors: boolean): string {
82
  if (result.status === 'finished') {
28✔
83
    return stringify(result.value);
23✔
84
  }
85

86
  return `Error: ${parseError(context.errors, verboseErrors)}`;
5✔
87
}
88

89
export function assertLanguageCombo(combo: Language): asserts combo is SourceLanguages {
90
  if (isSupportedLanguageCombo(combo)) return;
15!
91

NEW
92
  const chapterName = getChapterName(combo.chapter);
×
NEW
93
  const variantName = getVariantName(combo.variant);
×
94

NEW
95
  throw new GeneralRuntimeError(
×
96
    `Invalid language combo: chapter ${chapterName} and variant ${variantName}`,
97
  );
98
}
99

100
export const nodeFileGetter: FileGetter = async p => {
4✔
101
  try {
28✔
102
    const text = await fs.readFile(p, 'utf-8');
28✔
103
    return text;
24✔
104
  } catch (error) {
105
    if (error.code === 'ENOENT') return undefined;
4!
NEW
106
    throw error;
×
107
  }
108
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc