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

Siubaak / sval / 24298560689

12 Apr 2026 04:22AM UTC coverage: 80.156% (-0.5%) from 80.612%
24298560689

push

github

web-flow
Merge pull request #147 from Siubaak/claude/fix-issue-143-Z4efM

954 of 1119 branches covered (85.25%)

Branch coverage included in aggregate %.

26 of 55 new or added lines in 2 files covered. (47.27%)

48 existing lines in 2 files now uncovered.

2948 of 3749 relevant lines covered (78.63%)

957.32 hits per line

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

58.82
/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, STRICT, STRICT_FN } 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 === null) {
155!
26
      results.length++
×
27
    } else if (item.type === 'SpreadElement') {
155!
28
      results = results.concat(yield* SpreadElement(item, scope))
×
29
    } else {
155✔
30
      results.push(yield* evaluate(item, scope))
155✔
31
    }
155✔
32
  }
155✔
33
  return results
69✔
34
}
69✔
35

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

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

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

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

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

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

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

163
  switch (node.operator) {
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 '^': return left ^ right
239✔
183
    case '&': return left & right
239✔
184
    case 'in': return left in right
239✔
185
    case 'instanceof': return left instanceof right
239✔
186
    /* istanbul ignore next */
187
    default: throw new SyntaxError(`Unexpected token ${node.operator}`)
239!
188
  }
239✔
189
}
239✔
190

191
export function* AssignmentExpression(node: acorn.AssignmentExpression, scope: Scope) {
1✔
192
  const left = node.left
99✔
193
  let variable: Variable
99✔
194
  if (left.type === 'Identifier') {
99✔
195
    variable = yield* Identifier(left, scope, { getVar: true, throwErr: false })
18✔
196
    if (!variable) {
18!
197
      const strictMode = scope.find(STRICT)
×
198
      if (strictMode && strictMode.get()) {
×
199
        throw new ReferenceError(`${left.name} is not defined`)
×
200
      }
×
201
      const win = scope.global().find('window').get()
×
202
      variable = new Prop(win, left.name)
×
203
    }
×
204
  } else if (left.type === 'MemberExpression') {
99✔
205
    variable = yield* MemberExpression(left, scope, { getVar: true })
81✔
206
  } else {
81!
207
    const value = yield* evaluate(node.right, scope)
×
208
    return yield* pattern(left, scope, { feed: value })
×
209
  }
×
210

211
  const value = yield* evaluate(node.right, scope)
99✔
212
  switch (node.operator) {
98✔
213
    case '=': variable.set(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
    case '|=': variable.set(variable.get() | value); return variable.get()
99✔
224
    case '^=': variable.set(variable.get() ^ value); return variable.get()
99✔
225
    case '&=': variable.set(variable.get() & value); return variable.get()
99✔
226
    case '??=': variable.set(variable.get() ?? value); return variable.get()
99✔
227
    case '&&=': variable.set(variable.get() && value); return variable.get()
99✔
228
    case '||=': variable.set(variable.get() || value); return variable.get()
99✔
229
    /* istanbul ignore next */
230
    default: throw new SyntaxError(`Unexpected token ${node.operator}`)
99!
231
  }
99✔
232
}
99✔
233

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

248
export interface MemberExpressionOptions {
249
  getObj?: boolean
250
  getVar?: boolean
251
}
252

253
export function* MemberExpression(
1✔
254
  node: acorn.MemberExpression,
315✔
255
  scope: Scope,
315✔
256
  options: MemberExpressionOptions = {},
315✔
257
) {
315✔
258
  const { getObj = false, getVar = false } = options
315✔
259

260
  let object: any
315✔
261
  if (node.object.type === 'Super') {
315!
262
    object = yield* Super(node.object, scope, { getProto: true })
×
263
  } else {
315✔
264
    object = yield* evaluate(node.object, scope)
315✔
265
  }
315✔
266

267
  // propagate optional chain short-circuit
268
  if (object === OPTCHAIN) return OPTCHAIN
315!
269

270
  if (getObj) return object
315✔
271

272
  let key: string
123✔
273
  let priv: boolean = false
123✔
274

275
  if (node.computed) {
294!
276
    key = yield* evaluate(node.property, scope)
×
277
  } else if (node.property.type === 'PrivateIdentifier') {
294✔
278
    key = node.property.name
×
279
    priv = true
×
280
  } else {
123✔
281
    key = (node.property as acorn.Identifier).name
123✔
282
  }
123✔
283

284
  if (priv) {
294!
285
    object = object[PRIVATE]
×
286
  }
✔
287

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

320
export function* ConditionalExpression(node: acorn.ConditionalExpression, scope: Scope) {
1✔
321
  return (yield* evaluate(node.test, scope))
×
322
    ? (yield* evaluate(node.consequent, scope))
×
323
    : (yield* evaluate(node.alternate, scope))
×
324
}
×
325

NEW
326
function getCalleeDesc(node: acorn.Expression | acorn.Super): string {
×
NEW
327
  if (node.type === 'Identifier') {
×
NEW
328
    return (node as acorn.Identifier).name
×
NEW
329
  } else if (node.type === 'MemberExpression') {
×
NEW
330
    const memberNode = node as acorn.MemberExpression
×
NEW
331
    const objDesc = getCalleeDesc(memberNode.object)
×
NEW
332
    if (!memberNode.computed) {
×
NEW
333
      if (memberNode.property.type === 'PrivateIdentifier') {
×
NEW
334
        return `${objDesc}.#${(memberNode.property as acorn.PrivateIdentifier).name}`
×
NEW
335
      }
×
NEW
336
      return `${objDesc}.${(memberNode.property as acorn.Identifier).name}`
×
NEW
337
    }
×
NEW
338
    const prop = memberNode.property as acorn.Expression
×
NEW
339
    if (prop.type === 'Literal') {
×
NEW
340
      return `${objDesc}[${(prop as acorn.Literal).raw}]`
×
NEW
341
    }
×
NEW
342
    if (prop.type === 'Identifier') {
×
NEW
343
      return `${objDesc}[${(prop as acorn.Identifier).name}]`
×
NEW
344
    }
×
NEW
345
    return objDesc
×
NEW
346
  } else if (node.type === 'Super') {
×
NEW
347
    return 'super'
×
NEW
348
  }
×
NEW
349
  return '(intermediate value)'
×
NEW
350
}
×
351

352
export function* CallExpression(node: acorn.CallExpression, scope: Scope) {
1✔
353
  let func: any
410✔
354
  let object: any
410✔
355

356
  if (node.callee.type === 'MemberExpression') {
410✔
357
    object = yield* MemberExpression(node.callee, scope, { getObj: true })
192✔
358

359
    // propagate optional chain short-circuit
360
    if (object === OPTCHAIN) return OPTCHAIN
192!
361

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

367
    // get key
368
    let key: string
192✔
369
    let priv: boolean = false
192✔
370

371
    if (node.callee.computed) {
192!
372
      key = yield* evaluate(node.callee.property, scope)
×
373
    } else if (node.callee.property.type === 'PrivateIdentifier') {
192!
374
      key = node.callee.property.name
×
375
      priv = true
×
376
    } else {
192✔
377
      key = (node.callee.property as acorn.Identifier).name
192✔
378
    }
192✔
379

380
    let obj = object
192✔
381

382
    if (priv) {
192!
383
      obj = obj[PRIVATE]
×
384
    }
×
385

386
    // right value
387
    if (node.callee.object.type === 'Super') {
192!
388
      const thisObject = scope.find('this').get()
×
389
      func = obj[key].bind(thisObject)
×
390
    } else {
192✔
391
      func = obj[key]
192✔
392
    }
192✔
393

394
    // if it's optional chaining, check if function is null or undefined, so use ==
395
    if (node.optional && func == null) {
192!
396
      return OPTCHAIN
×
397
    }
×
398

399
    if (typeof func !== 'function') {
192!
NEW
400
      const calleeDesc = getCalleeDesc(node.callee as acorn.MemberExpression)
×
NEW
401
      throw new TypeError(`${calleeDesc} is not a function`)
×
402
    } else if (CLSCTOR in func) {
192!
NEW
403
      const calleeDesc = getCalleeDesc(node.callee as acorn.MemberExpression)
×
NEW
404
      throw new TypeError(`Class constructor ${calleeDesc} cannot be invoked without 'new'`)
×
UNCOV
405
    }
×
406
  } else {
391✔
407
    func = yield* evaluate(node.callee, scope)
218✔
408

409
    // propagate optional chain short-circuit
410
    if (func === OPTCHAIN) return OPTCHAIN
218!
411

412
    // if it's optional chaining, check if function is null or undefined, so use ==
413
    if (node.optional && func == null) {
218!
414
      return OPTCHAIN
×
415
    }
×
416

417
    if (typeof func !== 'function' || node.callee.type !== 'Super' && CLSCTOR in func) {
218!
418
      let name: string
×
419
      if (node.callee.type === 'Identifier') {
×
420
        name = node.callee.name
×
421
      } else {
×
422
        try {
×
423
          name = JSON.stringify(func)
×
424
        } catch (err) {
×
425
          name = '' + func
×
426
        }
×
427
      }
×
428
      if (typeof func !== 'function') {
×
429
        throw new TypeError(`${name} is not a function`)
×
430
      } else {
×
431
        throw new TypeError(`Class constructor ${name} cannot be invoked without 'new'`)
×
432
      }
×
433
    }
×
434

435
    // In strict mode, non-method calls pass undefined as 'this' (no global coercion)
436
    if (node.callee.type === 'Super') {
218!
437
      object = scope.find('this').get()
×
438
    } else {
218✔
439
      const isStrictCall = !!(scope.find(STRICT)?.get()) || !!(func && func[STRICT_FN])
218✔
440
      object = isStrictCall ? undefined : scope.find('this').get()
218✔
441
    }
218✔
442
  }
218✔
443

444
  let args: any[] = []
410✔
445
  for (let i = 0; i < node.arguments.length; i++) {
410✔
446
    const arg = node.arguments[i]
316✔
447
    if (arg.type === 'SpreadElement') {
316!
448
      args = args.concat(yield* SpreadElement(arg, scope))
×
449
    } else {
316✔
450
      args.push(yield* evaluate(arg, scope))
316✔
451
    }
314✔
452
  }
316✔
453

454
  if (node.callee.type === 'Super') {
410!
455
    const superCall = scope.find(SUPERCALL)
×
456
    const construct = superCall.get()
×
457
    if (construct === true) {
×
458
      throw new ReferenceError('Super constructor may only be called once')
×
459
    }
×
460
    const inst = callSuper(object, func, args)
×
461
    yield* construct(inst)
×
462
    scope.find('this').set(inst)
×
463
    scope.find(SUPERCALL).set(true)
×
464
    return inst
×
465
  }
✔
466

467
  try {
408✔
468
    return func.apply(object, args)
408✔
469
  } catch (err) {
410✔
470
    if (
1✔
471
      err instanceof TypeError && err.message === 'Illegal invocation'
1!
472
      && func.toString().indexOf('[native code]') !== -1
×
473
    ) {
1!
474
      // you will get "TypeError: Illegal invocation" if not binding native function with window
475
      const win = scope.global().find('window').get()
×
476
      if (win && win[WINDOW]) {
×
477
        return func.apply(win[WINDOW], args)
×
478
      }
×
479
    }
×
480
    throw err
1✔
481
  }
1✔
482
}
410✔
483

484
export function* NewExpression(node: acorn.NewExpression, scope: Scope) {
1✔
485
  const constructor = yield* evaluate(node.callee, scope)
2✔
486

487
  if (typeof constructor !== 'function') {
2!
488
    let name: string
×
489
    if (node.callee.type === 'Identifier') {
×
490
      name = node.callee.name
×
491
    } else {
×
492
      try {
×
493
        name = JSON.stringify(constructor)
×
494
      } catch (err) {
×
495
        name = '' + constructor
×
496
      }
×
497
    }
×
498
    throw new TypeError(`${name} is not a constructor`)
×
499
  } else if (constructor[NOCTOR]) {
2!
500
    throw new TypeError(`${constructor.name || '(intermediate value)'} is not a constructor`)
×
501
  }
×
502

503
  let args: any[] = []
2✔
504
  for (let i = 0; i < node.arguments.length; i++) {
2✔
505
    const arg = node.arguments[i]
1✔
506
    if (arg.type === 'SpreadElement') {
1!
507
      args = args.concat(yield* SpreadElement(arg, scope))
×
508
    } else {
1✔
509
      args.push(yield* evaluate(arg, scope))
1✔
510
    }
1✔
511
  }
1✔
512

513
  return new constructor(...args)
2✔
514
}
2✔
515

516
export function* MetaProperty(node: acorn.MetaProperty, scope: Scope) {
1✔
517
  if (node.meta.name === 'new' && node.property.name === 'target') {
×
518
    return scope.find(NEWTARGET).get()
×
519
  } else if (node.meta.name === 'import' && node.property.name === 'meta') {
×
520
    return { url: '' }
×
521
  }
×
522
}
×
523

524
export function* SequenceExpression(node: acorn.SequenceExpression, scope: Scope) {
1✔
525
  let result: any
×
526
  for (let i = 0; i < node.expressions.length; i++) {
×
527
    result = yield* evaluate(node.expressions[i], scope)
×
528
  }
×
529
  return result
×
530
}
×
531

532
export function* ArrowFunctionExpression(node: acorn.ArrowFunctionExpression, scope: Scope) {
1✔
533
  return createFunc(node, scope)
4✔
534
}
4✔
535

536
export function* TemplateLiteral(node: acorn.TemplateLiteral, scope: Scope) {
1✔
537
  const quasis = node.quasis.slice()
×
538
  const expressions = node.expressions.slice()
×
539

540
  let result = ''
×
541
  let temEl: acorn.TemplateElement
×
542
  let expr: acorn.Expression
×
543

544
  while (temEl = quasis.shift()) {
×
545
    result += yield* TemplateElement(temEl, scope)
×
546
    expr = expressions.shift()
×
547
    if (expr) {
×
548
      result += yield* evaluate(expr, scope)
×
549
    }
×
550
  }
×
551

552
  return result
×
553
}
×
554

555
export function* TaggedTemplateExpression(node: acorn.TaggedTemplateExpression, scope: Scope) {
1✔
556
  const tagFunc = yield* evaluate(node.tag, scope)
×
557

558
  const quasis = node.quasi.quasis
×
559
  const str = quasis.map(v => v.value.cooked)
×
560
  const raw = quasis.map(v => v.value.raw)
×
561

562
  define(str, 'raw', {
×
563
    value: freeze(raw)
×
564
  })
×
565

566
  const expressions = node.quasi.expressions
×
567

568
  const args = []
×
569
  if (expressions) {
×
570
    for (let i = 0; i < expressions.length; i++) {
×
571
      args.push(yield* evaluate(expressions[i], scope))
×
572
    }
×
573
  }
×
574

575
  return tagFunc(freeze(str), ...args)
×
576
}
×
577

578
export function* TemplateElement(node: acorn.TemplateElement, scope: Scope) {
1✔
579
  return node.value.raw
×
580
}
×
581

582
export function* ClassExpression(node: acorn.ClassExpression, scope: Scope) {
1✔
583
  if (node.id && node.id.name) {
×
584
    // it's for accessing class expression by its name inside
585
    // e.g. const a = class b { log() { console.log(b) } }
586
    const tmpScope = new Scope(scope)
×
587
    const klass = yield* createClass(node, tmpScope)
×
588
    tmpScope.const(node.id.name, klass)
×
589
    return klass
×
590
  } else {
×
591
    return yield* createClass(node, scope)
×
592
  }
×
593
}
×
594

595
export interface SuperOptions {
596
  getProto?: boolean
597
}
598

599
export function* Super(node: acorn.Super, scope: Scope, options: SuperOptions = {}) {
1✔
600
  const { getProto = false } = options
×
601
  const superClass = scope.find(SUPER).get()
×
602
  return getProto ? superClass.prototype: superClass
×
603
}
×
604

605
export interface SpreadOptions {
606
  spreadProps?: boolean
607
}
608

609
export function* SpreadElement(node: acorn.SpreadElement, scope: Scope, options: SpreadOptions = {}) {
1✔
610
  const result = yield* evaluate(node.argument, scope)
×
611
  if (options.spreadProps) {
×
612
    return result
×
613
  }
×
614
  if (typeof Symbol === 'function' && typeof result[Symbol.iterator] !== 'function') {
×
615
    throw new TypeError('Spread syntax requires ...iterable[Symbol.iterator] to be a function')
×
616
  }
×
617
  return [...result]
×
618
}
×
619

620
export function* ChainExpression(node: acorn.ChainExpression, scope: Scope) {
1✔
621
  const result = yield* evaluate(node.expression, scope)
×
622
  return result === OPTCHAIN ? undefined : result
×
623
}
×
624

625
export function* ImportExpression(node: acorn.ImportExpression, scope: Scope) {
1✔
626
  const globalScope = scope.global()
2✔
627

628
  const source = yield* evaluate(node.source, scope)
2✔
629
  const module = globalScope.find(IMPORT + source)
2✔
630
  let value: any
2✔
631
  if (module) {
2✔
632
    const result = module.get()
2✔
633
    if (result) {
2✔
634
      if (typeof result === 'function') {
2✔
635
        value = result()
2✔
636
      } else if (typeof result === 'object') {
2!
637
        value = result
×
638
      }
×
639
    }
2✔
640
  }
2✔
641

642
  if (!value || typeof value !== 'object') {
2!
643
    return Promise.reject(new TypeError(`Failed to resolve module specifier "${source}"`))
×
644
  }
×
645

646
  return Promise.resolve(value)
2✔
647
}
2✔
648

649
/*<remove>*/
650
export function* YieldExpression(node: acorn.YieldExpression, scope: Scope): any {
1✔
651
  const res = yield* evaluate(node.argument, scope)
29✔
652
  return node.delegate ? yield* res : yield res
29✔
653
}
29✔
654

655
export function* AwaitExpression(node: acorn.AwaitExpression, scope: Scope): any {
1✔
656
  AWAIT.RES = yield* evaluate(node.argument, scope)
23✔
657
  return yield AWAIT
22✔
658
}
22✔
659
/*</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