• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
Build has been canceled!

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

59.26
/src/utils/ast/helpers.ts
1
import type es from 'estree';
2

3
import assert from '../assert';
4
import { ArrayMap } from '../dict';
5
import type { ModuleDeclarationWithSource } from '../../modules/moduleTypes';
6
import { InternalRuntimeError } from '../../errors/base';
7
import { isDeclaration, isIdentifier, isImportDeclaration } from './typeGuards';
8

9
export function getModuleDeclarationSource(node: ModuleDeclarationWithSource): string {
10
  assert(
642✔
11
    typeof node.source?.value === 'string',
12
    `Expected ${node.type} to have a source value of type string, got ${node.source?.value}`,
13
  );
14
  return node.source.value;
642✔
15
}
16

17
/**
18
 * Extracts all the identifiers being declared by a VariableDeclaration
19
 */
20
export function extractDeclarations(decl: es.VariableDeclaration) {
21
  function recurser(pattern: es.Pattern): es.Identifier[] {
22
    switch (pattern.type) {
4,374!
23
      case 'ArrayPattern':
24
        return pattern.elements.flatMap(recurser);
8✔
25
      case 'AssignmentPattern':
NEW
26
        return recurser(pattern.left);
×
27
      case 'Identifier':
28
        return [pattern];
4,356✔
29
      case 'ObjectPattern':
30
        return pattern.properties.flatMap(prop => {
10✔
31
          if (prop.type === 'Property') {
19!
32
            return recurser(prop.value);
19✔
33
          }
NEW
34
          return recurser(prop);
×
35
        });
36
      case 'RestElement':
NEW
37
        return recurser(pattern.argument);
×
38
      default:
NEW
39
        throw new InternalRuntimeError(
×
40
          `Should not encounter a ${pattern.type} in ${extractDeclarations.name}`,
41
          pattern,
42
        );
43
    }
44
  }
45

46
  return decl.declarations.flatMap(({ id }) => recurser(id));
4,340✔
47
}
48

49
/**
50
 * Gets all the identifiers introduced by a declaration node
51
 */
52
export function getIdsFromDeclaration(
53
  decl: es.Declaration | es.ModuleDeclaration,
54
): es.Identifier[] {
55
  switch (decl.type) {
40!
56
    case 'ExportAllDeclaration':
NEW
57
      return [];
×
58
    case 'ExportDefaultDeclaration': {
NEW
59
      switch (decl.declaration.type) {
×
60
        case 'ClassDeclaration':
61
        case 'FunctionDeclaration':
NEW
62
          return decl.declaration.id ? [decl.declaration.id] : [];
×
63
      }
NEW
64
      return [];
×
65
    }
66
    case 'ExportNamedDeclaration':
NEW
67
      return decl.declaration ? getIdsFromDeclaration(decl.declaration) : [];
×
68
    case 'ImportDeclaration':
NEW
69
      return decl.specifiers.flatMap(spec => spec.local);
×
70
    case 'ClassDeclaration':
71
    case 'FunctionDeclaration':
72
      return [decl.id];
15✔
73
    case 'VariableDeclaration':
74
      return extractDeclarations(decl);
25✔
75
  }
76
}
77

78
/**
79
 * Since Variable declarations in Source programs must be initialized and are guaranteed to only
80
 * have 1 declarator, this function unwraps variable declarations and its single declarator
81
 * into its id and init
82
 */
83
export function getSourceVariableDeclaration(decl: es.VariableDeclaration) {
84
  assert(
59,779✔
85
    decl.declarations.length === 1,
86
    'Variable Declarations in Source should only have 1 declarator!',
87
  );
88

89
  const [declaration] = decl.declarations;
59,779✔
90
  assert(
59,779✔
91
    isIdentifier(declaration.id),
92
    'Variable Declarations in Source should be declared using an Identifier!',
93
  );
94

95
  assert(!!declaration.init, 'Variable declarations in Source must be initialized!');
59,779✔
96

97
  return {
59,779✔
98
    id: declaration.id,
99
    init: declaration.init,
100
    loc: declaration.loc,
101
  };
102
}
103

104
export const getImportedName = (
73✔
105
  spec: es.ImportSpecifier | es.ImportDefaultSpecifier | es.ExportSpecifier,
106
) => {
107
  switch (spec.type) {
151✔
108
    case 'ImportDefaultSpecifier':
109
      return 'default';
32✔
110
    case 'ImportSpecifier':
111
      return spec.imported.name;
111✔
112
    case 'ExportSpecifier':
113
      return spec.local.name;
8✔
114
  }
115
};
116

117
export const specifierToString = (
73✔
118
  spec: es.ImportSpecifier | es.ImportDefaultSpecifier | es.ExportSpecifier,
119
) => {
120
  switch (spec.type) {
5!
121
    case 'ImportSpecifier': {
122
      if (spec.imported.name === spec.local.name) {
×
123
        return spec.imported.name;
×
124
      }
125
      return `${spec.imported.name} as ${spec.local.name}`;
×
126
    }
127
    case 'ImportDefaultSpecifier':
128
      return `default as ${spec.local.name}`;
×
129
    case 'ExportSpecifier': {
130
      if (spec.local.name === spec.exported.name) {
5✔
131
        return spec.local.name;
4✔
132
      }
133
      return `${spec.local.name} as ${spec.exported.name}`;
1✔
134
    }
135
  }
136
};
137

138
type BlockBody = (es.Program | es.BlockStatement)['body'][number];
139
type BlocKBodyWithoutDeclarations = Exclude<BlockBody, es.Declaration>;
140

141
/**
142
 * Returns true if the array of statements doesn't contain any declarations
143
 */
144
export function hasNoDeclarations(stmt: BlockBody[]): stmt is BlocKBodyWithoutDeclarations[] {
145
  return !stmt.some(isDeclaration);
267,836✔
146
}
147

148
type BlockBodyWithoutImports = Exclude<BlockBody, es.ImportDeclaration>;
149
/**
150
 * Returns true if the array of statements doesn't contain any import declarations
151
 */
152
export function hasNoImportDeclarations(stmt: BlockBody[]): stmt is BlockBodyWithoutImports[] {
153
  return !stmt.some(isImportDeclaration);
88✔
154
}
155

156
/**
157
 * Filters out all import declarations from a program, and sorts them by
158
 * the module they import from
159
 */
160
export function filterImportDeclarations({
161
  body,
162
}: es.Program): [ArrayMap<string, es.ImportDeclaration>, BlockBodyWithoutImports[]] {
163
  return body.reduce<[ArrayMap<string, es.ImportDeclaration>, BlockBodyWithoutImports[]]>(
1,580✔
164
    ([importNodes, otherNodes], node) => {
165
      if (!isImportDeclaration(node)) return [importNodes, [...otherNodes, node]];
32,923✔
166

167
      const moduleName = getModuleDeclarationSource(node);
16✔
168
      importNodes.add(moduleName, node);
16✔
169
      return [importNodes, otherNodes];
16✔
170
    },
171
    [new ArrayMap(), []],
172
  );
173
}
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