• 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

81.08
/src/modules/loader/loaders.ts
1
import mapValues from 'lodash/mapValues';
2
import type { Context } from '../../types';
3
import { wrap } from '../../utils/operators';
4
import { ModuleConnectionError, ModuleInternalError } from '../errors';
5
import type {
6
  ModuleDocumentation,
7
  LoadedBundle,
8
  ModulesManifest,
9
  PartialSourceModule,
10
  Importer,
11
  ModuleDeclarationWithSource,
12
  ManifestImporter,
13
} from '../moduleTypes';
14
import {
15
  defaultSourceBundleImporter,
16
  defaultDocsImporter,
17
  setModulesStaticURL as internalUrlSetter,
18
  defaultManifestImporter,
19
  defaultSourceTabImporter,
20
} from './importers';
21
import { getRequireProvider } from './requireProvider';
22

23
export function setModulesStaticURL(value: string) {
24
  internalUrlSetter(value);
×
25

26
  // Changing the backend url should clear the caches
27
  // TODO: Do we want to memoize based on backend url?
NEW
28
  memoizedLoadModuleDocsAsync.cache.clear();
×
NEW
29
  memoizedLoadModuleManifestAsync.reset();
×
30
}
31

32
// lodash's memoize function memoizes on errors. This is undesirable,
33
// so we have our own custom memoization that won't memoize on errors
34
function getManifestLoader() {
35
  let manifest: ModulesManifest | null = null;
46✔
36
  let storedImporter: ManifestImporter | undefined = undefined;
46✔
37

38
  async function func(importer: ManifestImporter = defaultManifestImporter) {
7✔
39
    if (storedImporter !== undefined) {
7✔
40
      if (storedImporter !== importer) {
6✔
41
        storedImporter = importer;
1✔
42
        manifest = null;
1✔
43
      }
44
    } else {
45
      storedImporter = importer;
1✔
46
    }
47

48
    if (manifest !== null) {
7✔
49
      return manifest;
2✔
50
    }
51

52
    ({ default: manifest } = await importer());
5✔
53

54
    return manifest;
4✔
55
  }
56

57
  func.reset = () => {
46✔
58
    manifest = null;
3✔
59
  };
60

61
  return func;
46✔
62
}
63

64
function getMemoizedDocsLoader() {
65
  const docs = new Map<string, ModuleDocumentation>();
46✔
66
  let storedImporter: Importer<ModuleDocumentation> | undefined = undefined;
46✔
67

68
  async function func(
69
    moduleName: string,
70
    throwOnError: true,
71
    importer?: Importer<ModuleDocumentation>,
72
  ): Promise<ModuleDocumentation>;
73
  async function func(
74
    moduleName: string,
75
    throwOnError?: false,
76
    importer?: Importer<ModuleDocumentation>,
77
  ): Promise<ModuleDocumentation | null>;
78
  async function func(
79
    moduleName: string,
80
    throwOnError?: boolean,
81
    importer: Importer<ModuleDocumentation> = defaultDocsImporter,
9✔
82
  ): Promise<ModuleDocumentation | null> {
83
    if (storedImporter === undefined) {
9✔
84
      storedImporter = importer;
1✔
85
    } else if (storedImporter !== importer) {
8!
NEW
86
      storedImporter = importer;
×
87
      // Reset the cache if a different importer is used,
NEW
88
      docs.clear();
×
89
    }
90

91
    if (docs.has(moduleName)) {
9✔
92
      return docs.get(moduleName)!;
4✔
93
    }
94

95
    try {
5✔
96
      const { default: loadedDocs } = await importer(moduleName);
5✔
97
      docs.set(moduleName, loadedDocs);
4✔
98
      return loadedDocs;
4✔
99
    } catch (error) {
100
      if (throwOnError) throw error;
1!
101
      console.warn(`Failed to load documentation for ${moduleName}:`, error);
×
102
      return null;
×
103
    }
104
  }
105

106
  func.cache = docs;
46✔
107
  return func;
46✔
108
}
109

110
export const memoizedLoadModuleManifestAsync = getManifestLoader();
46✔
111
export const memoizedLoadModuleDocsAsync = getMemoizedDocsLoader();
46✔
112

113
/**
114
 * Load all the tabs of the given names
115
 */
116
export async function loadModuleTabsAsync(
117
  tabs: string[],
118
  importer: Importer<PartialSourceModule> = defaultSourceTabImporter,
4✔
119
): Promise<any[]> {
120
  return Promise.all(
4✔
121
    tabs.map(async tabName => {
122
      const { default: result } = await importer(tabName);
2✔
123
      return result;
2✔
124
    }),
125
  );
126
}
127

128
/**
129
 * Load the bundle of the module of the given name using the provided bundle loading function
130
 *
131
 * @param node         Node that triggered the loading of the given bundle
132
 * @param bundleLoader Bundle loading function
133
 */
134
export async function loadModuleBundleAsync(
135
  moduleName: string,
136
  context: Context,
137
  importer: Importer<PartialSourceModule> = defaultSourceBundleImporter,
6✔
138
  node?: ModuleDeclarationWithSource,
139
): Promise<LoadedBundle> {
140
  try {
6✔
141
    const { default: partialBundle } = await importer(moduleName, node);
6✔
142
    const loadedBundle = partialBundle(getRequireProvider(context));
5✔
143

144
    return mapValues(loadedBundle, (value, key) => {
5✔
145
      if (typeof value !== 'function') return value;
9!
146

147
      const name = value.name;
9✔
148
      return wrap(
9✔
149
        value as (...args: any[]) => any,
150
        false,
151
        `function ${name} {\n\t[Function from ${moduleName}\n\tImplementation hidden]\n}`,
152
        moduleName,
153
        name ?? key, // Ensure that names are provided if forgotten
9!
154
      );
155
    });
156
  } catch (error) {
157
    if (error instanceof ModuleConnectionError) throw error;
1!
NEW
158
    console.error(`Internal error while loading module ${moduleName}:`, error);
×
UNCOV
159
    throw new ModuleInternalError(moduleName, error, node);
×
160
  }
161
}
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