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

Siubaak / sval / 23090014415

14 Mar 2026 02:38PM UTC coverage: 80.177% (-0.06%) from 80.239%
23090014415

push

github

web-flow
Merge pull request #132 from Siubaak/claude/fix-issue-130-Zs1Ln

fix: propagate optional chain short-circuit through subsequent member…

905 of 1056 branches covered (85.7%)

Branch coverage included in aggregate %.

5 of 12 new or added lines in 2 files covered. (41.67%)

27 existing lines in 2 files now uncovered.

2800 of 3565 relevant lines covered (78.54%)

983.59 hits per line

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

61.6
/src/evaluate/expression.ts
1
import { define, freeze, getGetter, getSetter, createSymbol, assign, getDptor, callSuper, WINDOW } from '../share/util.ts'
1✔
2
import { SUPER, NOCTOR, AWAIT, CLSCTOR, NEWTARGET, SUPERCALL, PRIVATE, IMPORT, OPTCHAIN } from '../share/const.ts'
1✔
3
import { pattern, createFunc, createClass } from './helper.ts'
1✔
4
import { Variable, Prop } from '../scope/variable.ts'
1✔
5
import { Identifier } from './identifier.ts'
1✔
6
import { Literal } from './literal.ts'
1✔
7
import Scope from '../scope/index.ts'
1✔
8
import evaluate from './index.ts'
1✔
9
import * as acorn from 'acorn'
10

11
export function* ThisExpression(node: acorn.ThisExpression, scope: Scope) {
1✔
12
  const superCall = scope.find(SUPERCALL)
4✔
13
  if (superCall && superCall.get() !== true) {
4!
14
    throw new ReferenceError('Must call super constructor in derived class '
×
15
      + 'before accessing \'this\' or returning from derived constructor')
×
16
  } else {
4✔
17
    return scope.find('this').get()
4✔
18
  }
4✔
19
}
4✔
20

21
export function* ArrayExpression(node: acorn.ArrayExpression, scope: Scope) {
1✔
22
  let results: any[] = []
69✔
23
  for (let i = 0; i < node.elements.length; i++) {
69✔
24
    const item = node.elements[i]
155✔
25
    if (item.type === 'SpreadElement') {
155!
26
      results = results.concat(yield* SpreadElement(item, scope))
×
27
    } else {
155✔
28
      results.push(yield* evaluate(item, scope))
155✔
29
    }
155✔
30
  }
155✔
31
  return results
69✔
32
}
69✔
33

34
export function* ObjectExpression(node: acorn.ObjectExpression, scope: Scope) {
1✔
35
  const object: { [key: string]: any } = {}
17✔
36
  for (let i = 0; i < node.properties.length; i++) {
17✔
37
    const property = node.properties[i]
23✔
38
    if (property.type === 'SpreadElement') {
23!
39
      assign(object, yield* SpreadElement(property, scope, { spreadProps: true }))
×
40
    } else {
23✔
41
      let key: string
23✔
42
      const propKey = property.key
23✔
43
      if (property.computed) {
23!
44
        key = yield* evaluate(propKey, scope)
×
45
      } else {
23✔
46
        if (propKey.type === 'Identifier') {
23✔
47
          key = propKey.name
20✔
48
        } else {
23✔
49
          key = '' + (yield* Literal(propKey as acorn.Literal, scope))
3✔
50
        }
3✔
51
      }
23✔
52
  
53
      const value = yield* evaluate(property.value, scope)
23✔
54
  
55
      const propKind = property.kind
23✔
56
      if (propKind === 'init') {
23✔
57
        object[key] = value
21✔
58
      } else if (propKind === 'get') {
23✔
59
        const oriDptor = getDptor(object, key)
1✔
60
        define(object, key, {
1✔
61
          get: value,
1✔
62
          set: oriDptor && oriDptor.set,
1!
63
          enumerable: true,
1✔
64
          configurable: true
1✔
65
        })
1✔
66
      } else { // propKind === 'set'
1✔
67
        const oriDptor = getDptor(object, key)
1✔
68
        define(object, key, {
1✔
69
          get: oriDptor && oriDptor.get,
1✔
70
          set: value,
1✔
71
          enumerable: true,
1✔
72
          configurable: true
1✔
73
        })
1✔
74
      }
1✔
75
    }
23✔
76
  }
23✔
77
  return object
17✔
78
}
17✔
79

80
export function* FunctionExpression(node: acorn.FunctionExpression, scope: Scope) {
1✔
81
  if (node.id && node.id.name) {
13!
82
    // it's for accessing function expression by its name inside
83
    // e.g. const a = function b() { console.log(b) }
84
    const tmpScope = new Scope(scope)
×
85
    const func = createFunc(node, tmpScope)
×
86
    tmpScope.const(node.id.name, func)
×
87
    return func
×
88
  } else {
13✔
89
    return createFunc(node, scope)
13✔
90
  }
13✔
91
}
13✔
92

93
export function* UnaryExpression(node: acorn.UnaryExpression, scope: Scope) {
1✔
94
  const arg = node.argument
8✔
95
  switch (node.operator) {
8✔
96
    case '+': return +(yield* evaluate(arg, scope))
8✔
97
    case '-': return -(yield* evaluate(arg, scope))
8✔
98
    case '!': return !(yield* evaluate(arg, scope))
8✔
99
    case '~': return ~(yield* evaluate(arg, scope))
8✔
100
    case 'void': return void (yield* evaluate(arg, scope))
8✔
101
    case 'typeof':
8✔
102
      if (arg.type === 'Identifier') {
2✔
103
        return typeof (yield* Identifier(arg, scope, { throwErr: false }))
1✔
104
      } else {
1✔
105
        return typeof (yield* evaluate(arg, scope))
1✔
106
      }
1✔
107
    case 'delete':
8✔
108
      if (arg.type === 'MemberExpression') {
1✔
109
        const variable: Prop = yield* MemberExpression(arg, scope, { getVar: true })
1✔
110
        return variable.del()
1✔
111
      } else if (arg.type === 'Identifier') {
1!
112
        throw new SyntaxError('Delete of an unqualified identifier in strict mode')
×
113
      } else {
×
114
        yield* evaluate(arg, scope)
×
115
        return true
×
116
      }
×
117
    /* istanbul ignore next */
118
    default: throw new SyntaxError(`Unexpected token ${node.operator}`)
8!
119
  }
8✔
120
}
8✔
121

122
export function* UpdateExpression(node: acorn.UpdateExpression, scope: Scope) {
1✔
123
  const arg = node.argument
144✔
124
  
125
  let variable: Variable
144✔
126
  if (arg.type === 'Identifier') {
144✔
127
    variable = yield* Identifier(arg, scope, { getVar: true })
144✔
128
  } else if (arg.type === 'MemberExpression') {
144!
129
    variable = yield* MemberExpression(arg, scope, { getVar: true })
×
130
  } else {
×
131
    /* istanbul ignore next */
132
    throw new SyntaxError('Unexpected token')
×
133
  }
×
134

135
  const value = variable.get()
144✔
136
  if (node.operator === '++') {
144✔
137
    variable.set(value + 1)
144✔
138
    return node.prefix ? variable.get() : value
144!
139
  } else if (node.operator === '--') {
144!
140
    variable.set(value - 1)
×
141
    return node.prefix ? variable.get() : value
×
142
  } else {
×
143
    /* istanbul ignore next */
144
    throw new SyntaxError(`Unexpected token ${node.operator}`)
×
145
  }
×
146
}
144✔
147

148
export function* BinaryExpression(node: acorn.BinaryExpression, scope: Scope) {
1✔
149
  let left: any
239✔
150
  let right: any
239✔
151

152
  if (node.left.type === 'PrivateIdentifier') {
239!
153
    left = node.left.name
×
154
    right = yield* evaluate(node.right, scope)
×
155
    right = right[PRIVATE] || {} // compatible with checking by "#private in object"
×
156
  } else {
239✔
157
    left = yield* evaluate(node.left, scope)
239✔
158
    right = yield* evaluate(node.right, scope)
239✔
159
  }
239✔
160

161
  switch (node.operator) {
239✔
162
    case '==': return left == right
239✔
163
    case '!=': return left != right
239✔
164
    case '===': return left === right
239✔
165
    case '!==': return left !== right
239✔
166
    case '<': return left < right
239✔
167
    case '<=': return left <= right
239✔
168
    case '>': return left > right
239✔
169
    case '>=': return left >= right
239✔
170
    case '<<': return left << right
239✔
171
    case '>>': return left >> right
239✔
172
    case '>>>': return left >>> right
239✔
173
    case '+': return left + right
239✔
174
    case '-': return left - right
239✔
175
    case '*': return left * right
239✔
176
    case '**': return left ** right
239✔
177
    case '/': return left / right
239✔
178
    case '%': return left % right
239✔
179
    case '|': return left | right
239✔
180
    case '^': return left ^ right
239✔
181
    case '&': return left & right
239✔
182
    case 'in': return left in right
239✔
183
    case 'instanceof': return left instanceof right
239✔
184
    /* istanbul ignore next */
185
    default: throw new SyntaxError(`Unexpected token ${node.operator}`)
239!
186
  }
239✔
187
}
239✔
188

189
export function* AssignmentExpression(node: acorn.AssignmentExpression, scope: Scope) {
1✔
190
  const left = node.left
99✔
191
  let variable: Variable
99✔
192
  if (left.type === 'Identifier') {
99✔
193
    variable = yield* Identifier(left, scope, { getVar: true, throwErr: false })
18✔
194
    if (!variable) {
18!
195
      const win = scope.global().find('window').get()
×
196
      variable = new Prop(win, left.name)
×
197
    }
×
198
  } else if (left.type === 'MemberExpression') {
99✔
199
    variable = yield* MemberExpression(left, scope, { getVar: true })
81✔
200
  } else {
81!
201
    const value = yield* evaluate(node.right, scope)
×
202
    return yield* pattern(left, scope, { feed: value })
×
203
  }
×
204

205
  const value = yield* evaluate(node.right, scope)
99✔
206
  switch (node.operator) {
98✔
207
    case '=': variable.set(value); return variable.get()
99✔
208
    case '+=': variable.set(variable.get() + value); return variable.get()
99✔
209
    case '-=': variable.set(variable.get() - value); return variable.get()
99✔
210
    case '*=': variable.set(variable.get() * value); return variable.get()
99✔
211
    case '/=': variable.set(variable.get() / value); return variable.get()
99✔
212
    case '%=': variable.set(variable.get() % value); return variable.get()
99✔
213
    case '**=': variable.set(variable.get() ** value); return variable.get()
99✔
214
    case '<<=': variable.set(variable.get() << value); return variable.get()
99✔
215
    case '>>=': variable.set(variable.get() >> value); return variable.get()
99✔
216
    case '>>>=': variable.set(variable.get() >>> value); return variable.get()
99✔
217
    case '|=': variable.set(variable.get() | value); return variable.get()
99✔
218
    case '^=': variable.set(variable.get() ^ value); return variable.get()
99✔
219
    case '&=': variable.set(variable.get() & value); return variable.get()
99✔
220
    case '??=': variable.set(variable.get() ?? value); return variable.get()
99✔
221
    case '&&=': variable.set(variable.get() && value); return variable.get()
99✔
222
    case '||=': variable.set(variable.get() || value); return variable.get()
99✔
223
    /* istanbul ignore next */
224
    default: throw new SyntaxError(`Unexpected token ${node.operator}`)
99!
225
  }
99✔
226
}
99✔
227

228
export function* LogicalExpression(node: acorn.LogicalExpression, scope: Scope) {
1✔
229
  switch (node.operator) {
×
230
    case '||':
×
231
      return (yield* evaluate(node.left, scope)) || (yield* evaluate(node.right, scope))
×
232
    case '&&':
×
233
      return (yield* evaluate(node.left, scope)) && (yield* evaluate(node.right, scope))
×
234
    case '??':
×
235
      return (yield* evaluate(node.left, scope)) ?? (yield* evaluate(node.right, scope))
×
236
    default:
×
237
      /* istanbul ignore next */
238
      throw new SyntaxError(`Unexpected token ${node.operator}`)
×
239
  }
×
240
}
×
241

242
export interface MemberExpressionOptions {
243
  getObj?: boolean
244
  getVar?: boolean
245
}
246

247
export function* MemberExpression(
1✔
248
  node: acorn.MemberExpression,
315✔
249
  scope: Scope,
315✔
250
  options: MemberExpressionOptions = {},
315✔
251
) {
315✔
252
  const { getObj = false, getVar = false } = options
315✔
253

254
  let object: any
315✔
255
  if (node.object.type === 'Super') {
315!
256
    object = yield* Super(node.object, scope, { getProto: true })
×
257
  } else {
315✔
258
    object = yield* evaluate(node.object, scope)
315✔
259
  }
315✔
260

261
  // propagate optional chain short-circuit
262
  if (object === OPTCHAIN) return OPTCHAIN
315!
263

264
  if (getObj) return object
315✔
265

266
  let key: string
123✔
267
  let priv: boolean = false
123✔
268

269
  if (node.computed) {
294!
270
    key = yield* evaluate(node.property, scope)
×
271
  } else if (node.property.type === 'PrivateIdentifier') {
294✔
272
    key = node.property.name
×
273
    priv = true
×
274
  } else {
123✔
275
    key = (node.property as acorn.Identifier).name
123✔
276
  }
123✔
277

278
  if (priv) {
294!
279
    object = object[PRIVATE]
×
280
  }
✔
281

282
  if (getVar) {
294✔
283
    // left value
284
    const setter = getSetter(object, key)
82✔
285
    if (node.object.type === 'Super' && setter) {
82!
286
      // transfer the setter from super to this with a private key
287
      const thisObject = scope.find('this').get()
×
288
      const privateKey = createSymbol(key)
×
289
      define(thisObject, privateKey, { set: setter })
×
290
      return new Prop(thisObject, privateKey)
×
291
    } else {
82✔
292
      return new Prop(object, key)
82✔
293
    }
82✔
294
  } else {
294✔
295
    // right value
296
    const getter = getGetter(object, key)
41✔
297
    if (node.object.type === 'Super' && getter) {
41!
298
      const thisObject = scope.find('this').get()
×
299
      // if it's optional chaining, check if this ref is null or undefined, so use ==
300
      if (node.optional && thisObject == null) {
×
NEW
301
        return OPTCHAIN
×
302
      }
×
303
      return getter.call(thisObject)
×
304
    } else {
41✔
305
      // if it's optional chaining, check if object is null or undefined, so use ==
306
      if (node.optional && object == null) {
41!
NEW
307
        return OPTCHAIN
×
308
      }
×
309
      return object[key]
41✔
310
    }
41✔
311
  }
41✔
312
}
315✔
313

314
export function* ConditionalExpression(node: acorn.ConditionalExpression, scope: Scope) {
1✔
315
  return (yield* evaluate(node.test, scope))
×
316
    ? (yield* evaluate(node.consequent, scope))
×
317
    : (yield* evaluate(node.alternate, scope))
×
318
}
×
319

320
export function* CallExpression(node: acorn.CallExpression, scope: Scope) {
1✔
321
  let func: any
410✔
322
  let object: any
410✔
323

324
  if (node.callee.type === 'MemberExpression') {
410✔
325
    object = yield* MemberExpression(node.callee, scope, { getObj: true })
192✔
326

327
    // propagate optional chain short-circuit
328
    if (object === OPTCHAIN) return OPTCHAIN
192!
329

330
    // if it's optional chaining, check if object is null or undefined, so use ==
331
    if (node.callee.optional && object == null) {
192!
NEW
332
      return OPTCHAIN
×
333
    }
×
334

335
    // get key
336
    let key: string
192✔
337
    let priv: boolean = false
192✔
338

339
    if (node.callee.computed) {
192!
340
      key = yield* evaluate(node.callee.property, scope)
×
341
    } else if (node.callee.property.type === 'PrivateIdentifier') {
192!
342
      key = node.callee.property.name
×
343
      priv = true
×
344
    } else {
192✔
345
      key = (node.callee.property as acorn.Identifier).name
192✔
346
    }
192✔
347

348
    let obj = object
192✔
349

350
    if (priv) {
192!
351
      obj = obj[PRIVATE]
×
352
    }
×
353

354
    // right value
355
    if (node.callee.object.type === 'Super') {
192!
356
      const thisObject = scope.find('this').get()
×
357
      func = obj[key].bind(thisObject)
×
358
    } else {
192✔
359
      func = obj[key]
192✔
360
    }
192✔
361

362
    // if it's optional chaining, check if function is null or undefined, so use ==
363
    if (node.optional && func == null) {
192!
NEW
364
      return OPTCHAIN
×
365
    }
×
366

367
    if (typeof func !== 'function') {
192!
368
      throw new TypeError(`${key} is not a function`)
×
369
    } else if (CLSCTOR in func) {
192!
370
      throw new TypeError(`Class constructor ${key} cannot be invoked without 'new'`)
×
371
    }
×
372
  } else {
391✔
373
    object = scope.find('this').get()
218✔
374
    func = yield* evaluate(node.callee, scope)
218✔
375

376
    // propagate optional chain short-circuit
377
    if (func === OPTCHAIN) return OPTCHAIN
218!
378

379
    // if it's optional chaining, check if function is null or undefined, so use ==
380
    if (node.optional && func == null) {
218!
NEW
381
      return OPTCHAIN
×
382
    }
×
383

384
    if (typeof func !== 'function' || node.callee.type !== 'Super' && CLSCTOR in func) {
218!
385
      let name: string
×
386
      if (node.callee.type === 'Identifier') {
×
387
        name = node.callee.name
×
388
      } else {
×
389
        try {
×
390
          name = JSON.stringify(func)
×
391
        } catch (err) {
×
392
          name = '' + func
×
393
        }
×
394
      }
×
395
      if (typeof func !== 'function') {
×
396
        throw new TypeError(`${name} is not a function`)
×
397
      } else {
×
398
        throw new TypeError(`Class constructor ${name} cannot be invoked without 'new'`)
×
399
      }
×
400
    }
×
401
  }
218✔
402

403
  let args: any[] = []
410✔
404
  for (let i = 0; i < node.arguments.length; i++) {
410✔
405
    const arg = node.arguments[i]
316✔
406
    if (arg.type === 'SpreadElement') {
316!
407
      args = args.concat(yield* SpreadElement(arg, scope))
×
408
    } else {
316✔
409
      args.push(yield* evaluate(arg, scope))
316✔
410
    }
314✔
411
  }
316✔
412

413
  if (node.callee.type === 'Super') {
410!
414
    const superCall = scope.find(SUPERCALL)
×
415
    const construct = superCall.get()
×
416
    if (construct === true) {
×
417
      throw new ReferenceError('Super constructor may only be called once')
×
418
    }
×
419
    const inst = callSuper(object, func, args)
×
420
    yield* construct(inst)
×
421
    scope.find('this').set(inst)
×
422
    scope.find(SUPERCALL).set(true)
×
423
    return inst
×
424
  }
✔
425

426
  try {
408✔
427
    return func.apply(object, args)
408✔
428
  } catch (err) {
410✔
429
    if (
1✔
430
      err instanceof TypeError && err.message === 'Illegal invocation'
1!
431
      && func.toString().indexOf('[native code]') !== -1
×
432
    ) {
1!
433
      // you will get "TypeError: Illegal invocation" if not binding native function with window
434
      const win = scope.global().find('window').get()
×
435
      if (win && win[WINDOW]) {
×
436
        return func.apply(win[WINDOW], args)
×
437
      }
×
438
    }
×
439
    throw err
1✔
440
  }
1✔
441
}
410✔
442

443
export function* NewExpression(node: acorn.NewExpression, scope: Scope) {
1✔
444
  const constructor = yield* evaluate(node.callee, scope)
2✔
445

446
  if (typeof constructor !== 'function') {
2!
447
    let name: string
×
448
    if (node.callee.type === 'Identifier') {
×
449
      name = node.callee.name
×
450
    } else {
×
451
      try {
×
452
        name = JSON.stringify(constructor)
×
453
      } catch (err) {
×
454
        name = '' + constructor
×
455
      }
×
456
    }
×
457
    throw new TypeError(`${name} is not a constructor`)
×
458
  } else if (constructor[NOCTOR]) {
2!
459
    throw new TypeError(`${constructor.name || '(intermediate value)'} is not a constructor`)
×
460
  }
×
461

462
  let args: any[] = []
2✔
463
  for (let i = 0; i < node.arguments.length; i++) {
2✔
464
    const arg = node.arguments[i]
1✔
465
    if (arg.type === 'SpreadElement') {
1!
466
      args = args.concat(yield* SpreadElement(arg, scope))
×
467
    } else {
1✔
468
      args.push(yield* evaluate(arg, scope))
1✔
469
    }
1✔
470
  }
1✔
471

472
  return new constructor(...args)
2✔
473
}
2✔
474

475
export function* MetaProperty(node: acorn.MetaProperty, scope: Scope) {
1✔
476
  if (node.meta.name === 'new' && node.property.name === 'target') {
×
477
    return scope.find(NEWTARGET).get()
×
478
  } else if (node.meta.name === 'import' && node.property.name === 'meta') {
×
479
    return { url: '' }
×
480
  }
×
481
}
×
482

483
export function* SequenceExpression(node: acorn.SequenceExpression, scope: Scope) {
1✔
484
  let result: any
×
485
  for (let i = 0; i < node.expressions.length; i++) {
×
486
    result = yield* evaluate(node.expressions[i], scope)
×
487
  }
×
488
  return result
×
489
}
×
490

491
export function* ArrowFunctionExpression(node: acorn.ArrowFunctionExpression, scope: Scope) {
1✔
492
  return createFunc(node, scope)
4✔
493
}
4✔
494

495
export function* TemplateLiteral(node: acorn.TemplateLiteral, scope: Scope) {
1✔
496
  const quasis = node.quasis.slice()
×
497
  const expressions = node.expressions.slice()
×
498

499
  let result = ''
×
500
  let temEl: acorn.TemplateElement
×
501
  let expr: acorn.Expression
×
502

503
  while (temEl = quasis.shift()) {
×
504
    result += yield* TemplateElement(temEl, scope)
×
505
    expr = expressions.shift()
×
506
    if (expr) {
×
507
      result += yield* evaluate(expr, scope)
×
508
    }
×
509
  }
×
510

511
  return result
×
512
}
×
513

514
export function* TaggedTemplateExpression(node: acorn.TaggedTemplateExpression, scope: Scope) {
1✔
515
  const tagFunc = yield* evaluate(node.tag, scope)
×
516

517
  const quasis = node.quasi.quasis
×
518
  const str = quasis.map(v => v.value.cooked)
×
519
  const raw = quasis.map(v => v.value.raw)
×
520

521
  define(str, 'raw', {
×
522
    value: freeze(raw)
×
523
  })
×
524

525
  const expressions = node.quasi.expressions
×
526

527
  const args = []
×
528
  if (expressions) {
×
529
    for (let i = 0; i < expressions.length; i++) {
×
530
      args.push(yield* evaluate(expressions[i], scope))
×
531
    }
×
532
  }
×
533

534
  return tagFunc(freeze(str), ...args)
×
535
}
×
536

537
export function* TemplateElement(node: acorn.TemplateElement, scope: Scope) {
1✔
538
  return node.value.raw
×
539
}
×
540

541
export function* ClassExpression(node: acorn.ClassExpression, scope: Scope) {
1✔
542
  if (node.id && node.id.name) {
×
543
    // it's for accessing class expression by its name inside
544
    // e.g. const a = class b { log() { console.log(b) } }
545
    const tmpScope = new Scope(scope)
×
546
    const klass = yield* createClass(node, tmpScope)
×
547
    tmpScope.const(node.id.name, klass)
×
548
    return klass
×
549
  } else {
×
550
    return yield* createClass(node, scope)
×
551
  }
×
552
}
×
553

554
export interface SuperOptions {
555
  getProto?: boolean
556
}
557

558
export function* Super(node: acorn.Super, scope: Scope, options: SuperOptions = {}) {
1✔
559
  const { getProto = false } = options
×
560
  const superClass = scope.find(SUPER).get()
×
561
  return getProto ? superClass.prototype: superClass
×
562
}
×
563

564
export interface SpreadOptions {
565
  spreadProps?: boolean
566
}
567

568
export function* SpreadElement(node: acorn.SpreadElement, scope: Scope, options: SpreadOptions = {}) {
1✔
569
  const result = yield* evaluate(node.argument, scope)
×
570
  if (options.spreadProps) {
×
571
    return result
×
572
  }
×
573
  if (typeof Symbol === 'function' && typeof result[Symbol.iterator] !== 'function') {
×
574
    throw new TypeError('Spread syntax requires ...iterable[Symbol.iterator] to be a function')
×
575
  }
×
576
  return [...result]
×
577
}
×
578

579
export function* ChainExpression(node: acorn.ChainExpression, scope: Scope) {
1✔
NEW
580
  const result = yield* evaluate(node.expression, scope)
×
NEW
581
  return result === OPTCHAIN ? undefined : result
×
UNCOV
582
}
×
583

584
export function* ImportExpression(node: acorn.ImportExpression, scope: Scope) {
1✔
585
  const globalScope = scope.global()
2✔
586

587
  const source = yield* evaluate(node.source, scope)
2✔
588
  const module = globalScope.find(IMPORT + source)
2✔
589
  let value: any
2✔
590
  if (module) {
2✔
591
    const result = module.get()
2✔
592
    if (result) {
2✔
593
      if (typeof result === 'function') {
2✔
594
        value = result()
2✔
595
      } else if (typeof result === 'object') {
2!
596
        value = result
×
597
      }
×
598
    }
2✔
599
  }
2✔
600

601
  if (!value || typeof value !== 'object') {
2!
602
    return Promise.reject(new TypeError(`Failed to resolve module specifier "${source}"`))
×
603
  }
×
604

605
  return Promise.resolve(value)
2✔
606
}
2✔
607

608
/*<remove>*/
609
export function* YieldExpression(node: acorn.YieldExpression, scope: Scope): any {
1✔
610
  const res = yield* evaluate(node.argument, scope)
29✔
611
  return node.delegate ? yield* res : yield res
29✔
612
}
29✔
613

614
export function* AwaitExpression(node: acorn.AwaitExpression, scope: Scope): any {
1✔
615
  AWAIT.RES = yield* evaluate(node.argument, scope)
23✔
616
  return yield AWAIT
22✔
617
}
22✔
618
/*</remove>*/
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