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

DanielXMoore / Civet / 18380789274

09 Oct 2025 03:00PM UTC coverage: 91.614% (+0.03%) from 91.589%
18380789274

push

github

web-flow
Merge pull request #1806 from DanielXMoore/import-exp

3705 of 4034 branches covered (91.84%)

Branch coverage included in aggregate %.

99 of 100 new or added lines in 3 files covered. (99.0%)

18996 of 20745 relevant lines covered (91.57%)

16452.0 hits per line

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

97.32
/source/parser/declaration.civet
1
import type {
1✔
2
  AccessStart
1✔
3
  ASTLeaf
1✔
4
  ASTNode
1✔
5
  ASTNodeParent
1✔
6
  ASTRef
1✔
7
  AwaitExpression
1✔
8
  Binding
1✔
9
  BlockStatement
1✔
10
  Declaration
1✔
11
  ExpressionNode
1✔
12
  IfStatement
1✔
13
  Initializer
1✔
14
  IterationStatement
1✔
15
  ParenthesizedExpression
1✔
16
  StatementTuple
1✔
17
  SwitchStatement
1✔
18
  TypeSuffix
1✔
19
  WithClause
1✔
20
  Whitespace
1✔
21
  WSNode
1✔
22
} from ./types.civet
1✔
23

1✔
24
import {
1✔
25
  blockContainingStatement
1✔
26
  blockWithPrefix
1✔
27
  braceBlock
1✔
28
  makeEmptyBlock
1✔
29
  replaceBlockExpression
1✔
30
} from ./block.civet
1✔
31

1✔
32
import {
1✔
33
  findAncestor
1✔
34
  findChildIndex
1✔
35
  gatherRecursiveAll
1✔
36
} from ./traversal.civet
1✔
37

1✔
38
import {
1✔
39
  getPatternBlockPrefix
1✔
40
  getPatternConditions
1✔
41
} from ./pattern-matching.civet
1✔
42

1✔
43
import {
1✔
44
  append
1✔
45
  convertOptionalType
1✔
46
  insertTrimmingSpace
1✔
47
  isExit
1✔
48
  literalType
1✔
49
  makeLeftHandSideExpression
1✔
50
  makeNode
1✔
51
  parenthesizeExpression
1✔
52
  spliceChild
1✔
53
  trimFirstSpace
1✔
54
  updateParentPointers
1✔
55
} from ./util.civet
1✔
56

1✔
57
import {
1✔
58
  makeRef
1✔
59
  maybeRef
1✔
60
} from ./ref.civet
1✔
61

1✔
62
import {
1✔
63
  assignResults
1✔
64
  wrapIterationReturningResults
1✔
65
} from ./function.civet
1✔
66

1✔
67
import {
1✔
68
  gatherBindingCode
1✔
69
  gatherSubbindings
1✔
70
  simplifyBindingProperties
1✔
71
} from ./binding.civet
1✔
72

1✔
73
import {
1✔
74
  convertNamedImportsToObject
1✔
75
  processCallMemberExpression
1✔
76
} from ./lib.civet
1✔
77

1✔
78
function processAssignmentDeclaration(decl: ASTLeaf, pattern: Binding["pattern"], typeSuffix: TypeSuffix?, ws: WSNode, assign: ASTLeaf, e: ASTNode)
398✔
79
  // Adjust position to space before assignment to make TypeScript remapping happier
398✔
80
  decl = {
398✔
81
    ...decl,
398✔
82
    $loc:
398✔
83
      pos: assign.$loc.pos - 1
398✔
84
      length: assign.$loc.length + 1
398✔
85
  }
398✔
86

398✔
87
  [splices, assignments] .= gatherBindingCode pattern
398✔
88

398✔
89
  splices = splices.map (s) => [", ", s]
398✔
90
  thisAssignments := assignments.map (a) => ["", a, ";"] as const
398✔
91

398✔
92
  typeSuffix ??= pattern.typeSuffix if "typeSuffix" in pattern
398✔
93
  initializer := makeNode
398✔
94
    type: "Initializer"
398✔
95
    expression: e
398✔
96
    children: [ws, assign, e]
398✔
97
  binding := makeNode {
398✔
98
    type: "Binding"
398✔
99
    pattern
398✔
100
    initializer
398✔
101
    splices
398✔
102
    typeSuffix
398✔
103
    thisAssignments
398✔
104
    children: [pattern, typeSuffix, initializer]
398✔
105
  }
398✔
106

398✔
107
  children := [decl, binding]
398✔
108

398✔
109
  makeNode {
398✔
110
    type: "Declaration"
398✔
111
    pattern.names
398✔
112
    decl
398✔
113
    bindings: [binding]
398✔
114
    splices
398✔
115
    thisAssignments
398✔
116
    children
398✔
117
  }
398✔
118

1✔
119
function processDeclarations(statements: StatementTuple[]): void
3,080✔
120
  for each declaration of gatherRecursiveAll statements, .type is "Declaration"
112,894✔
121
    { bindings } := declaration
831✔
122
    continue unless bindings?
831✔
123

590✔
124
    for i of [bindings#>..0]
831✔
125
      binding := bindings[i]
557✔
126
      subbindings := gatherSubbindings binding
557✔
127
      if subbindings#
557✔
128
        simplifyBindingProperties binding
11✔
129
        simplifyBindingProperties subbindings
11✔
130
        // Add subbindings after this binding
11✔
131
        spliceChild declaration, binding, 1, binding, subbindings
11✔
132

590✔
133
    for each binding of bindings
590✔
134
      { typeSuffix, initializer } .= binding
557✔
135

557✔
136
      if typeSuffix and typeSuffix.optional
557✔
137
        // Convert `let x? = y` to `let x: undefined | typeof y = y`
14✔
138
        if initializer and not typeSuffix.t
14✔
139
          expression := trimFirstSpace initializer.expression!
10✔
140
          if expression.type is like "Identifier", "MemberExpression"
9✔
141
            typeSuffix.children.push ": ", typeSuffix.t = {}
2✔
142
              type: "TypeTypeof"
2✔
143
              children: ["typeof ", expression]
2✔
144
              expression
2✔
145
          else if expression.type is "Literal" or
8✔
146
                  expression.type is "RegularExpressionLiteral" or
8✔
147
                  expression.type is "TemplateLiteral"
2✔
148
            typeSuffix.children.push ": ", typeSuffix.t = literalType expression
7✔
149
          else
1✔
150
            spliceChild binding, typeSuffix, 1,
1✔
151
              type: "Error"
1✔
152
              message: `Optional type can only be inferred from literals or member expressions, not ${expression.type}`
1✔
153
            continue
1✔
154
        if typeSuffix.t
13✔
155
          // Convert `let x?: T` to `let x: undefined | T`
12✔
156
          convertOptionalType typeSuffix
12✔
157
        else
1✔
158
          // Convert `let x?` to `let x = undefined`
1✔
159
          spliceChild binding, typeSuffix, 1
1✔
160
          binding.children.push initializer = binding.initializer =
1✔
161
            type: "Initializer"
1✔
162
            expression: "undefined"
1✔
163
            children: [" = ", "undefined"]
1✔
164

556✔
165
      // If this declaration is at the top level (not e.g. inside an
556✔
166
      // ExportDeclaration), try to pull out any statement expressions
556✔
167
      if initializer and blockContainingStatement declaration
557✔
168
        prependStatementExpressionBlock initializer, declaration
490✔
169

831✔
170
  for each statement of gatherRecursiveAll statements, .type is "ForStatement"
113,216✔
171
    { declaration } := statement
326✔
172
    continue unless declaration?.type is "ForDeclaration"
326✔
173
    { binding } := declaration
241✔
174
    blockPrefix := getPatternBlockPrefix
241✔
175
      binding.pattern
241✔
176
      undefined
241✔
177
      append declaration.decl, " "
241✔
178
      binding.typeSuffix
241✔
179
    simplifyBindingProperties binding
241✔
180
    if blockPrefix?
241✔
181
      statement.block.expressions.unshift ...blockPrefix
6✔
182
      braceBlock statement.block
6✔
183

1✔
184
function prependStatementExpressionBlock(initializer: Initializer, statement: ASTNodeParent): ASTRef?
1,212✔
185
  {expression: exp} .= initializer
1,212✔
186

1,212✔
187
  // Handle nested statement expression
1,212✔
188
  let ws
1,212✔
189
  if Array.isArray(exp)
1,212✔
190
    ws = exp[0]
83✔
191
    exp = exp[1]!
83✔
192

1,212✔
193
  return unless exp is like {type: "StatementExpression"}, {type: "SpreadElement", expression: {type: "StatementExpression"}}
1,090!
194

122✔
195
  pre: ASTNode[] := []
122✔
196
  statementExp := exp.statement
122✔
197
  blockStatement: StatementTuple := ["", statementExp]
122✔
198
  let ref: ASTRef
122✔
199

122✔
200
  if statementExp.type is "IterationExpression"
122✔
201
    // Async and generator iterations remain inline wrapped with IIFE
91✔
202
    return if statementExp.async or statementExp.generator
91✔
203

87✔
204
    statement := statementExp.statement
87✔
205
    blockStatement[1] = statement
87✔
206

87✔
207
    // Leave comptime statements wrapped in IIFE
87✔
208
    return if statement.type is "ComptimeStatement"
91✔
209

70✔
210
    if statement.type is "DoStatement"
70✔
211
      ref = initializer.expression = initializer.children[2] = makeRef()
3✔
212
      assignResults blockStatement, (resultNode) =>
3✔
213
        //@ts-ignore
3✔
214
        makeNode
3✔
215
          type: "AssignmentExpression",
3✔
216
          children: [ref, " = ", resultNode]
3✔
217
          parent: statement
3✔
218

3✔
219
      refDec :=
3✔
220
        type: "Declaration",
3✔
221
        children: ["let ", ref, ";"],
3✔
222

3✔
223
      // @ts-ignore
3✔
224
      pre.unshift refDec
3✔
225
    else
67✔
226
      wrapIterationReturningResults statement, => undefined
67✔
227
      ref = initializer.expression = initializer.children[2] = statement.resultsRef
67✔
228
  else
31✔
229
    ref = initializer.expression = initializer.children[2] = makeRef()
31✔
230

31✔
231
    assignResults blockStatement, (resultNode) =>
31✔
232
      //@ts-ignore
65✔
233
      makeNode
65✔
234
        type: "AssignmentExpression",
65✔
235
        children: [ref, " = ", resultNode]
65✔
236
        parent: statement
65✔
237

31✔
238
    refDec :=
31✔
239
      type: "Declaration",
31✔
240
      children: ["let ", ref, ";"],
31✔
241

31✔
242
    pre.unshift refDec
31✔
243
    //@ts-ignore
31✔
244
    pre.push ws if ws
31✔
245

101✔
246
  // insert statement before the declaration
101✔
247
  //@ts-ignore
101✔
248
  statement.children.unshift(pre, blockStatement, ";")
101✔
249
  updateParentPointers blockStatement, statement
101✔
250

101✔
251
  return ref
101✔
252

1✔
253
function processDeclarationCondition(condition, rootCondition, parent: ASTNodeParent): void
550✔
254
  return unless condition.type is "DeclarationCondition"
550✔
255

45✔
256
  { decl, bindings } := condition.declaration as Declaration
45✔
257
  // TODO: Add support for `let` and `const` declarations with multiple bindings in conditions
45✔
258
  binding := bindings[0]
45✔
259
  { pattern, typeSuffix, initializer } .= binding
45✔
260
  nullCheck := typeSuffix?.optional and not typeSuffix.t and not typeSuffix.nonnull
550✔
261

550✔
262
  unless initializer?
550✔
263
    condition.children = [
×
264
      type: "Error"
×
265
      message: "Missing initializer in declaration condition"
×
266
    ]
×
267
    return
×
268

45✔
269
  ref: ASTNode .= prependStatementExpressionBlock(initializer!, parent)
45✔
270

45✔
271
  if ref
45✔
272
    Object.assign condition, {
2✔
273
      type: "AssignmentExpression"
2✔
274
      children: [ref]
2✔
275
      pattern
2✔
276
      ref
2✔
277
      statementDeclaration: true
2✔
278
    }
2✔
279
  else
43✔
280
    { expression } := initializer
43✔
281
    ref = maybeRef expression
43✔
282
    simple := ref is expression
43✔
283
    let children
43✔
284
    if simple
43✔
285
      ref = trimFirstSpace ref
8✔
286
      children = [ref]
8✔
287
    else
35✔
288
      children = [ref, initializer]
35✔
289
      // Wrap declaration that is a plain assignment (no pattern-matching) and the immediate grandchild of an `if` or `while`
35✔
290
      // to satisfy eslint's no-cond-assign rule
35✔
291
      // More complex conditions (triggered by pattern matching or `until`/`unless`) don't need double parens
35✔
292
      grandparent := condition.parent?.parent
35✔
293
      if pattern.type is "Identifier" and (grandparent?.type is "IfStatement" or grandparent?.type is "IterationStatement") and not nullCheck
35✔
294
        children.unshift "("
7✔
295
        children.push ")"
7✔
296

43✔
297
    // `x? := ...` as a condition means `x := ..., x?`
43✔
298
    if nullCheck
43✔
299
      children.unshift "("
4✔
300
      children.push ") != null"
4✔
301
      typeSuffix = undefined
4✔
302

43✔
303
    Object.assign condition, {
43✔
304
      type: "AssignmentExpression"
43✔
305
      children
43✔
306
      hoistDec: unless simple
35✔
307
        type: "Declaration"
35✔
308
        children: ["let ", ref, typeSuffix]
35✔
309
        names: []
43✔
310
      pattern
43✔
311
      ref
43✔
312
    }
43✔
313

45✔
314
  // condition wasn't previously a child, so now needs parent pointers
45✔
315
  updateParentPointers condition, parent
45✔
316

45✔
317
  rootCondition.blockPrefix = getPatternBlockPrefix(pattern, ref, decl, typeSuffix)
45✔
318

1✔
319
function processDeclarationConditions(node: ASTNode): void
3,080✔
320
  gatherRecursiveAll node,
3,080✔
321
    (n): n is IfStatement | IterationStatement | SwitchStatement =>
3,080✔
322
      n.type is "IfStatement" or n.type is "IterationStatement" or n.type is "SwitchStatement"
113,171✔
323
  .forEach (s) =>
3,080✔
324
    processDeclarationConditionStatement s
567✔
325

1✔
326
/**
1✔
327
 * Processes adding additional conditions when declarations are used as a condition in IfStatements, WhileStatements, and SwitchStatements.
1✔
328
 * Also does additional processing for IfStatements that used to be in the parser (inserting semi-colon on bare-block consequent with else).
1✔
329
 */
1✔
330
function processDeclarationConditionStatement(s: IfStatement | IterationStatement | SwitchStatement): void
567✔
331
  { condition } := s
567✔
332
  return unless condition?.expression
567✔
333
  { expression } .= condition
550✔
334

550✔
335
  // Support for negated conditions built by unless/until
550✔
336
  if {type: 'UnaryExpression', children: [['!'], {type: 'ParenthesizedExpression', expression: expression2}]} := expression
45✔
337
    expression = expression2
23✔
338

550✔
339
  processDeclarationCondition expression, condition.expression, s
550✔
340

550✔
341
  { ref, pattern } := expression
550✔
342

550✔
343
  if pattern
550✔
344
    conditions := getPatternConditions(pattern, ref)
45✔
345
    .filter (c) =>
45✔
346
      switch c
12✔
347
        // We already check whether the ref is truthy, so it's non-null
12✔
348
        [^ref, " != null"]
12✔
349
          false
67✔
350
        // Keep top-level object checks to be consistent with pattern matching
67✔
351
        // (e.g. exclude {length} := s from matching a string)
67✔
352
        //["typeof ", ^ref, " === 'object'"]
67✔
353
        else
67✔
354
          true
67✔
355

45✔
356
    if conditions#
45✔
357
      children .= condition.children
22✔
358
      // For negated conditions, put conditions inside the negation
22✔
359
      if s.negated
22✔
360
        unless condition.expression is like {type: 'UnaryExpression', children: [['!'], {type: 'ParenthesizedExpression'}]}
2✔
361
          throw new Error "Unsupported negated condition"
×
362
        { children } = condition.expression.children[1] as ParenthesizedExpression
2✔
363
      children.unshift "("
22✔
364
      for each c of conditions
22✔
365
        children.push " && ", c
67✔
366
      children.push ")"
22✔
367

550✔
368
  // Move declaration to after the statement for until loops,
550✔
369
  // and for unless conditions with a guaranteed exit in the then clause.
550✔
370
  { blockPrefix } := condition.expression
550✔
371
  if s.negated and blockPrefix and
567✔
372
     (s.type is "IfStatement" and isExit(s.then) or
8✔
373
      s.type is "IterationStatement")
8✔
374
    { ancestor, child } := findAncestor s,
6✔
375
      (a): a is BlockStatement => a.type is "BlockStatement"
6✔
376
    unless ancestor?
6✔
377
      throw new Error "Couldn't find block for postfix declaration"
×
378
    index := findChildIndex ancestor.expressions, child
6✔
379
    if index < 0
6✔
380
      throw new Error "Couldn't find where in block to put postfix declaration"
×
381
    ancestor.expressions.splice index + 1, 0, ...blockPrefix
6✔
382
    updateParentPointers ancestor
6✔
383
    braceBlock ancestor
6✔
384
    switch s.type
6✔
385
      when "IfStatement"
6✔
386
        // If there's an else clause, and then clause has an exit,
5✔
387
        // move else clause to after the declaration (so it also has access).
5✔
388
        if elseBlock := s.else?.block
5✔
389
          if elseBlock.bare and not elseBlock.semicolon
1!
390
            elseBlock.children.push elseBlock.semicolon = ";"
×
391
          ancestor.expressions.splice index + 1 + blockPrefix#, 0, ['', elseBlock]
1✔
392
          s.children = s.children.filter (is not s.else)
1✔
393
          s.else = undefined
1✔
394
        // Ensure semicolon after if before added declaration
5✔
395
        block := s.then
5✔
396
        if block.bare and not block.semicolon
5✔
397
          block.children.push block.semicolon = ";"
1✔
398
    return
6✔
399

544✔
400
  switch s.type
544✔
401
    when "IfStatement"
567✔
402
      { else: e } := s
304✔
403

304✔
404
      // Prefix then or else block depending on negation
304✔
405
      if s.negated
304✔
406
        if e?
23✔
407
          block := blockWithPrefix blockPrefix, e.block
5✔
408
          e.children = e.children.map & is e.block ? block : &
5✔
409
          e.block = block
5✔
410
          updateParentPointers e
5✔
411
      else
281✔
412
        block := blockWithPrefix blockPrefix, s.then
281✔
413
        s.children = s.children.map & is s.then ? block : &
281✔
414
        s.then = block
281✔
415
        updateParentPointers s
281✔
416

567✔
417
    when "IterationStatement"
567✔
418
      return unless blockPrefix
104✔
419
      { children, block } := s
7✔
420
      newBlock := blockWithPrefix blockPrefix, block
7✔
421
      s.children = children.map & is block ? newBlock : &
7✔
422
      updateParentPointers s
7✔
423

567✔
424
    when "SwitchStatement"
567✔
425
      { ref, statementDeclaration } := condition.expression as! { ref: ASTRef, statementDeclaration: boolean}
136✔
426
      return unless blockPrefix
136✔
427

5✔
428
      newCondition :=
5✔
429
        type: "ParenthesizedExpression"
5✔
430
        children: ["(", ref, ")"]
5✔
431
        expression: ref
5✔
432
        parent: s
5✔
433

5✔
434
      // @ts-ignore
5✔
435
      s.children = s.children.map (c) ->
5✔
436
        if c is s.condition
18✔
437
          newCondition
5✔
438
        else
13✔
439
          c
13✔
440

5✔
441
      // @ts-ignore
5✔
442
      s.condition = newCondition
5✔
443
      updateParentPointers s
5✔
444

5✔
445
      if statementDeclaration
5✔
446
        // Hacky way to make sure the declaration is after the hoisted statement declaration
1✔
447
        // TODO better unify this with hoistDec mechanics
1✔
448
        block := makeEmptyBlock()
1✔
449
        replaceBlockExpression(s.parent as BlockStatement, s, block)
1✔
450
        block.expressions.push ["", s]
1✔
451
        s.children.splice s.children.findIndex(.token is "switch"), 0, blockPrefix
1✔
452
        s.parent = block
1✔
453
      else
4✔
454
        // wraps the entire switch statement
4✔
455
        block := blockWithPrefix [["", [{
4✔
456
          type: "Declaration",
4✔
457
          children: ["let ", ...condition.expression.children],
4✔
458
        }], ";"], ...blockPrefix], makeEmptyBlock()
4✔
459
        updateParentPointers block, s.parent
4✔
460

4✔
461
        replaceBlockExpression(s.parent as BlockStatement, s, block)
4✔
462
        block.expressions.push ["", s]
4✔
463
        s.parent = block
4✔
464

1✔
465
// Convert FromClause into arguments for dynamic import
1✔
466
function dynamizeFromClause(from)
23✔
467
  from = from[1..]  // remove 'from'
23✔
468
  from = trimFirstSpace from
23✔
469
  if assert := from.-1?.assertion
23✔
470
    from.-1.children |>= .filter (is not assert)
3✔
471
    from.push ", {", assert.keyword, ":", assert.object, "}"
3✔
472
  ["(", ...from, ")"]
23✔
473

1✔
474
function dynamizeImportDeclaration(decl)
14✔
475
  { imports } := decl
14✔
476
  { star, binding, specifiers } .= imports
14✔
477
  justDefault := binding and not specifiers and not star
14✔
478
  pattern := do
14✔
479
    if binding
14✔
480
      if specifiers
8✔
481
        makeRef()
1✔
482
      else
7✔
483
        binding
7✔
484
    else
6✔
485
      convertNamedImportsToObject(imports, true)
6✔
486
  c := "const"
14✔
487
  expression := [
14✔
488
    if justDefault then "("
4✔
489
    {type: "Await", children: ["await"]}
14✔
490
    " "
14✔
491
    decl.children[0] // import
14✔
492
    dynamizeFromClause decl.from
14✔
493
    if justDefault then ").default"
4✔
494
  ]
14✔
495
  initializer: Initializer := {
14✔
496
    type: "Initializer"
14✔
497
    expression
14✔
498
    children: [" ", "= ", expression]
14✔
499
  }
14✔
500
  bindings := [{
14✔
501
    type: "Binding"
14✔
502
    names: pattern.names
14✔
503
    pattern
14✔
504
    initializer
14✔
505
    children: [pattern, initializer]
14✔
506
  }]
14✔
507
  if binding and specifiers
14✔
508
    // import x, {y} --> const ref = await import(...), x = ref.default, {y} = ref
1✔
509
    pattern2 := binding
1✔
510
    exp2 := [
1✔
511
      pattern
1✔
512
      ".default"
1✔
513
    ]
1✔
514
    initializer2: Initializer := {
1✔
515
      type: "Initializer"
1✔
516
      expression
1✔
517
      children: [" ", "= ", exp2]
1✔
518
    }
1✔
519
    bindings.push
1✔
520
      type: "Binding"
1✔
521
      names: binding.names
1✔
522
      pattern: pattern2
1✔
523
      initializer: initializer2
1✔
524
      children: [pattern2, initializer2]
1✔
525
    pattern3 := convertNamedImportsToObject(imports.children.at(-1), true)
1✔
526
    initializer3: Initializer := {
1✔
527
      type: "Initializer"
1✔
528
      expression: pattern
1✔
529
      children: [" ", "= ", pattern]
1✔
530
    }
1✔
531
    bindings.push
1✔
532
      type: "Binding"
1✔
533
      names: specifiers.names
1✔
534
      pattern: pattern3
1✔
535
      initializer: initializer3
1✔
536
      children: [pattern3, initializer3]
1✔
537
  {
14✔
538
    type: "Declaration"
14✔
539
    names: imports.names
14✔
540
    bindings
14✔
541
    decl: c
14✔
542
    children: [
14✔
543
      c, " "
14✔
544
      bindings.flatMap (binding, i) => i > 0 ? [", ", binding] : [binding]
14✔
545
    ]
14✔
546
  }
14✔
547

1✔
548
function dynamizeImportDeclarationExpression($0: [ASTNode, Whitespace, ASTNode, Whitespace, ASTNode])
9✔
549
  [imp, ws1, imports, ws2, from] := $0
9✔
550
  awaitExpression: AwaitExpression :=
9✔
551
    type: "AwaitExpression"
9✔
552
    children: []
9✔
553
      { type: "Await", children: ["await"] }
9✔
554
      " "
9✔
555
      imp
9✔
556
      trimFirstSpace ws2
9✔
557
      dynamizeFromClause from
9✔
558
  dot: AccessStart :=
9✔
559
    type: "AccessStart"
9✔
560
    children: ["."]
9✔
561
    optional: false
9✔
562
  switch imports?.type
9✔
563
    when "Identifier" // default import
9✔
564
      processCallMemberExpression
2✔
565
        type: "CallExpression"
2✔
566
        children: []
2✔
567
          parenthesizeExpression awaitExpression
2✔
568
          {}
2✔
569
            type: "PropertyAccess"
2✔
570
            dot
2✔
571
            name: "default"
2✔
572
            children: [ws1, dot, "default"]
2✔
573
    when "Star"
9✔
574
      parenthesizeExpression awaitExpression
1✔
575
    when "Declaration" // named imports
9✔
576
      object := convertNamedImportsToObject imports
6✔
577
      processCallMemberExpression
6✔
578
        type: "CallExpression"
6✔
579
        children: []
6✔
580
          parenthesizeExpression awaitExpression
6✔
581
          {}
6✔
582
            type: "PropertyGlob"
6✔
583
            dot
6✔
584
            object
6✔
585
            children: [ws1, dot, object]
6✔
586
            reversed: true
6✔
587
    else
9!
NEW
588
      throw new Error "Unsupported dynamic import"
×
589

1✔
590
function convertWithClause(withClause: WithClause, extendsClause?: [ASTLeaf, WSNode, ExpressionNode])
3✔
591
  let extendsToken, extendsTarget, ws: WSNode
3✔
592
  if extendsClause
3✔
593
    [ extendsToken, ws, extendsTarget ] = extendsClause
1✔
594
  else
2✔
595
    extendsToken =
2✔
596
      type: "Extends"
2✔
597
      children: [" extends"]
2✔
598
    ws = ""
2✔
599
    extendsTarget = "Object"
2✔
600

3✔
601
  wrapped := withClause.targets.reduce (extendsTarget, [wsNext, withTarget]) =>
3✔
602
    args := [ extendsTarget ]
10✔
603
    exp := {
10✔
604
      type: "CallExpression",
10✔
605
      children: [
10✔
606
        makeLeftHandSideExpression(withTarget),
10✔
607
        {
10✔
608
          type: "Call",
10✔
609
          args,
10✔
610
          children: ["(", trimFirstSpace(ws), args, ")"],
10✔
611
        },
10✔
612
      ],
10✔
613
    } as ExpressionNode
10✔
614

10✔
615
    ws = wsNext
10✔
616

10✔
617
    return exp
10✔
618
  , extendsTarget
3✔
619

3✔
620
  // Ensure at least one space after extends token
3✔
621
  return [ extendsToken, insertTrimmingSpace(ws, " "), wrapped ]
3✔
622

1✔
623
export {
1✔
624
  convertWithClause
1✔
625
  dynamizeImportDeclaration
1✔
626
  dynamizeImportDeclarationExpression
1✔
627
  prependStatementExpressionBlock
1✔
628
  processAssignmentDeclaration
1✔
629
  processDeclarationConditions
1✔
630
  processDeclarations
1✔
631
}
1✔
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