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

DanielXMoore / Civet / 21772072879

07 Feb 2026 01:50AM UTC coverage: 91.42% (-0.2%) from 91.596%
21772072879

Pull #1779

github

web-flow
Merge 817af4c6a into 3f8c07ee4
Pull Request #1779: LSP: fix(diagnostics), ensure immediate updates for opened dependent files on change

3777 of 4118 branches covered (91.72%)

Branch coverage included in aggregate %.

19185 of 20999 relevant lines covered (91.36%)

17409.21 hits per line

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

97.15
/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
  insertBlockStatements
1✔
29
  makeEmptyBlock
1✔
30
  replaceBlockExpression
1✔
31
} from ./block.civet
1✔
32

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

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

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

1✔
61
import {
1✔
62
  makeRef
1✔
63
  maybeRef
1✔
64
} from ./ref.civet
1✔
65

1✔
66
import {
1✔
67
  assignResults
1✔
68
  wrapIterationReturningResults
1✔
69
} from ./function.civet
1✔
70

1✔
71
import {
1✔
72
  gatherBindingCode
1✔
73
  gatherSubbindings
1✔
74
  simplifyBindingProperties
1✔
75
} from ./binding.civet
1✔
76

1✔
77
import {
1✔
78
  convertNamedImportsToObject
1✔
79
  processCallMemberExpression
1✔
80
} from ./lib.civet
1✔
81

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

413✔
91
  [splices, assignments] .= gatherBindingCode pattern
413✔
92

413✔
93
  splices = splices.map (s) => [", ", s]
413✔
94
  thisAssignments := assignments.map (a) => ["", a, ";"] as const
413✔
95

413✔
96
  typeSuffix ??= pattern.typeSuffix if "typeSuffix" in pattern
413✔
97
  initializer := makeNode
413✔
98
    type: "Initializer"
413✔
99
    expression: e
413✔
100
    children: [ws, assign, e]
413✔
101
  binding := makeNode {
413✔
102
    type: "Binding"
413✔
103
    pattern
413✔
104
    initializer
413✔
105
    splices
413✔
106
    typeSuffix
413✔
107
    thisAssignments
413✔
108
    children: [pattern, typeSuffix, initializer]
413✔
109
  }
413✔
110

413✔
111
  children := [decl, binding]
413✔
112

413✔
113
  makeNode {
413✔
114
    type: "Declaration"
413✔
115
    pattern.names
413✔
116
    decl
413✔
117
    bindings: [binding]
413✔
118
    splices
413✔
119
    thisAssignments
413✔
120
    children
413✔
121
  }
413✔
122

1✔
123
function processDeclarations(statements: StatementTuple[]): void
3,119✔
124
  for each declaration of gatherRecursiveAll statements, .type is "Declaration"
114,440✔
125
    { bindings } := declaration
840✔
126
    continue unless bindings?
840✔
127

599✔
128
    for i of [bindings#>..0]
840✔
129
      binding := bindings[i]
565✔
130
      subbindings := gatherSubbindings binding
565✔
131
      if subbindings#
565✔
132
        simplifyBindingProperties binding
11✔
133
        simplifyBindingProperties subbindings
11✔
134
        // Add subbindings after this binding
11✔
135
        spliceChild declaration, binding, 1, binding, subbindings
11✔
136

599✔
137
    for each binding of bindings
599✔
138
      { typeSuffix, initializer } .= binding
565✔
139

565✔
140
      if typeSuffix and typeSuffix.optional
565✔
141
        // Convert `let x? = y` to `let x: undefined | typeof y = y`
15✔
142
        if initializer and not typeSuffix.t
15✔
143
          expression := trimFirstSpace initializer.expression!
11✔
144
          typeSuffix.t = typeOfExpression expression
11✔
145
          unless typeSuffix.t?
11✔
146
            spliceChild binding, typeSuffix, 1,
3✔
147
              type: "Error"
3✔
148
              message:
3✔
149
                if expression is like {type: "Literal", subtype: "NullLiteral"}
3✔
150
                  "Optional type can't be inferred from null"
1✔
151
                else if expression is like {type: "Identifier", name: "undefined"}
2✔
152
                  "Optional type can't be inferred from undefined"
1✔
153
                else
1✔
154
                  `Optional type can only be inferred from literals or member expressions, not ${expression.type}`
1✔
155
            continue
3✔
156
          typeSuffix.children.push ": ", typeSuffix.t
8✔
157
        if typeSuffix.t
12✔
158
          // Convert `let x?: T` to `let x: undefined | T`
11✔
159
          convertOptionalType typeSuffix
11✔
160
        else
1✔
161
          // Convert `let x?` to `let x = undefined`
1✔
162
          spliceChild binding, typeSuffix, 1
1✔
163
          binding.children.push initializer = binding.initializer =
1✔
164
            type: "Initializer"
1✔
165
            expression: "undefined"
1✔
166
            children: [" = ", "undefined"]
1✔
167

562✔
168
      // If this declaration is at the top level (not e.g. inside an
562✔
169
      // ExportDeclaration), try to pull out any statement expressions
562✔
170
      if initializer and blockContainingStatement declaration
565✔
171
        prependStatementExpressionBlock initializer, declaration
496✔
172

840✔
173
  for each statement of gatherRecursiveAll statements, .type is "ForStatement"
114,768✔
174
    { declaration } := statement
331✔
175
    continue unless declaration?.type is "ForDeclaration"
331✔
176
    { binding } := declaration
246✔
177
    blockPrefix := getPatternBlockPrefix
246✔
178
      binding.pattern
246✔
179
      undefined
246✔
180
      append declaration.decl, " "
246✔
181
      binding.typeSuffix
246✔
182
    simplifyBindingProperties binding
246✔
183
    if blockPrefix?
246✔
184
      statement.block.expressions.unshift ...blockPrefix
6✔
185
      braceBlock statement.block
6✔
186

1✔
187
function prependStatementExpressionBlock(initializer: Initializer, statement: ASTNodeParent): ASTRef?
1,222✔
188
  {expression: exp} .= initializer
1,222✔
189

1,222✔
190
  // Handle nested statement expression
1,222✔
191
  let ws: Whitespace
1,222✔
192
  if Array.isArray(exp) and exp# is 2 and isWhitespaceOrEmpty exp[0]
1,222✔
193
    ws = exp[0]
29✔
194
    exp = exp[1]!
29✔
195

1,222✔
196
  return unless exp is like {type: "StatementExpression"}, {type: "SpreadElement", expression: {type: "StatementExpression"}}
1,092!
197

130✔
198
  statementExp := exp.statement
130✔
199
  blockStatement: StatementTuple := [ws || "", statementExp, ";"]
1,222✔
200
  pre: StatementTuple[] := [blockStatement]
1,222✔
201
  let ref: ASTRef
1,222✔
202

1,222✔
203
  if statementExp.type is "IterationExpression"
1,222✔
204
    // Async and generator iterations remain inline wrapped with IIFE
97✔
205
    return if statementExp.async or statementExp.generator
97✔
206

93✔
207
    statement := statementExp.statement
93✔
208
    blockStatement[1] = statement
93✔
209

93✔
210
    // Leave comptime statements wrapped in IIFE
93✔
211
    return if statement.type is "ComptimeStatement"
97✔
212

76✔
213
    if statement.type is "DoStatement"
76✔
214
      ref = initializer.expression = initializer.children[2] = makeRef()
8✔
215
      assignResults blockStatement, (resultNode) =>
8✔
216
        //@ts-ignore
8✔
217
        makeNode
8✔
218
          type: "AssignmentExpression"
8✔
219
          children: [ref, " = ", resultNode]
8✔
220

8✔
221
      refDec :=
8✔
222
        type: "Declaration"
8✔
223
        children: ["let ", ref]
8✔
224

8✔
225
      // @ts-ignore
8✔
226
      pre.unshift ["", refDec, ";"]
8✔
227
    else
68✔
228
      wrapIterationReturningResults statement, => undefined
68✔
229
      ref = initializer.expression = initializer.children[2] = statement.resultsRef
68✔
230
  else
33✔
231
    ref = initializer.expression = initializer.children[2] = makeRef()
33✔
232

33✔
233
    assignResults blockStatement, (resultNode) =>
33✔
234
      //@ts-ignore
69✔
235
      makeNode
69✔
236
        type: "AssignmentExpression"
69✔
237
        children: [ref, " = ", resultNode]
69✔
238

33✔
239
    refDec :=
33✔
240
      type: "Declaration"
33✔
241
      children: ["let ", ref]
33✔
242

33✔
243
    pre.unshift ["", refDec, ";"]
33✔
244

109✔
245
  // Insert the statement from the statement expression
109✔
246
  // (and any helper declarations) into the containing block
109✔
247
  // just before declaration/assignment.
109✔
248
  unless { block, index } := blockContainingStatement statement
1,222✔
249
    throw new Error "Couldn't find block in prependStatementExpressionBlock"
109!
250
  insertBlockStatements block, index, ...pre
109✔
251

109✔
252
  return ref
109✔
253

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

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

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

46✔
270
  ref: ASTNode .= prependStatementExpressionBlock(initializer!, parent)
46✔
271

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

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

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

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

46✔
318
  rootCondition.blockPrefix = getPatternBlockPrefix(pattern, ref, decl, typeSuffix)
46✔
319

1✔
320
function processDeclarationConditions(node: ASTNode): void
3,119✔
321
  gatherRecursiveAll node,
3,119✔
322
    (n): n is IfStatement | IterationStatement | SwitchStatement =>
3,119✔
323
      n.type is "IfStatement" or n.type is "IterationStatement" or n.type is "SwitchStatement"
114,750✔
324
  .forEach (s) =>
3,119✔
325
    processDeclarationConditionStatement s
576✔
326

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

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

559✔
340
  processDeclarationCondition expression, condition.expression, s
559✔
341

559✔
342
  { ref, pattern } := expression
559✔
343

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

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

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

553✔
401
  switch s.type
553✔
402
    when "IfStatement"
576✔
403
      { else: e } := s
313✔
404

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

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

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

5✔
429
      replaceNode s.condition, parenthesizeExpression(ref), s
5✔
430

5✔
431
      unless statementDeclaration
5✔
432
        declStatement: StatementTuple := ["", [{
4✔
433
          type: "Declaration",
4✔
434
          children: ["let ", ...condition.expression.children],
4✔
435
        }], ";"]
4✔
436
        blockPrefix.unshift declStatement
4✔
437

5✔
438
      // Wrap switch in a block to properly scope declarations
5✔
439
      block := blockWithPrefix blockPrefix, makeEmptyBlock()
5✔
440
      updateParentPointers block, s.parent
5✔
441
      replaceBlockExpression s.parent as BlockStatement, s, block
5✔
442
      block.expressions.push ["", s]
5✔
443
      s.parent = block
5✔
444

1✔
445
// Convert FromClause into arguments for dynamic import
1✔
446
function dynamizeFromClause(from)
23✔
447
  from = from[1..]  // remove 'from'
23✔
448
  from = trimFirstSpace from
23✔
449
  if assert := from.-1?.assertion
23✔
450
    from.-1.children |>= .filter (is not assert)
3✔
451
    from.push ", {", assert.keyword, ":", assert.object, "}"
3✔
452
  ["(", ...from, ")"]
23✔
453

1✔
454
function dynamizeImportDeclaration(decl)
14✔
455
  { imports } := decl
14✔
456
  { star, binding, specifiers } .= imports
14✔
457
  justDefault := binding and not specifiers and not star
14✔
458
  pattern := do
14✔
459
    if binding
14✔
460
      if specifiers
8✔
461
        makeRef()
1✔
462
      else
7✔
463
        binding
7✔
464
    else
6✔
465
      convertNamedImportsToObject(imports, true)
6✔
466
  c := "const"
14✔
467
  expression := [
14✔
468
    if justDefault then "("
4✔
469
    {type: "Await", children: ["await"]}
14✔
470
    " "
14✔
471
    decl.children[0] // import
14✔
472
    dynamizeFromClause decl.from
14✔
473
    if justDefault then ").default"
4✔
474
  ]
14✔
475
  initializer: Initializer := {
14✔
476
    type: "Initializer"
14✔
477
    expression
14✔
478
    children: [" ", "= ", expression]
14✔
479
  }
14✔
480
  bindings := [{
14✔
481
    type: "Binding"
14✔
482
    names: pattern.names
14✔
483
    pattern
14✔
484
    initializer
14✔
485
    children: [pattern, initializer]
14✔
486
  }]
14✔
487
  if binding and specifiers
14✔
488
    // import x, {y} --> const ref = await import(...), x = ref.default, {y} = ref
1✔
489
    pattern2 := binding
1✔
490
    exp2 := [
1✔
491
      pattern
1✔
492
      ".default"
1✔
493
    ]
1✔
494
    initializer2: Initializer := {
1✔
495
      type: "Initializer"
1✔
496
      expression
1✔
497
      children: [" ", "= ", exp2]
1✔
498
    }
1✔
499
    bindings.push
1✔
500
      type: "Binding"
1✔
501
      names: binding.names
1✔
502
      pattern: pattern2
1✔
503
      initializer: initializer2
1✔
504
      children: [pattern2, initializer2]
1✔
505
    pattern3 := convertNamedImportsToObject(imports.children.at(-1), true)
1✔
506
    initializer3: Initializer := {
1✔
507
      type: "Initializer"
1✔
508
      expression: pattern
1✔
509
      children: [" ", "= ", pattern]
1✔
510
    }
1✔
511
    bindings.push
1✔
512
      type: "Binding"
1✔
513
      names: specifiers.names
1✔
514
      pattern: pattern3
1✔
515
      initializer: initializer3
1✔
516
      children: [pattern3, initializer3]
1✔
517
  {
14✔
518
    type: "Declaration"
14✔
519
    names: imports.names
14✔
520
    bindings
14✔
521
    decl: c
14✔
522
    children: [
14✔
523
      c, " "
14✔
524
      bindings.flatMap (binding, i) => i > 0 ? [", ", binding] : [binding]
14✔
525
    ]
14✔
526
  }
14✔
527

1✔
528
function dynamizeImportDeclarationExpression($0: [ASTNode, Whitespace, ASTNode, Whitespace, ASTNode])
9✔
529
  [imp, ws1, imports, ws2, from] := $0
9✔
530
  awaitExpression: AwaitExpression :=
9✔
531
    type: "AwaitExpression"
9✔
532
    children: []
9✔
533
      { type: "Await", children: ["await"] }
9✔
534
      " "
9✔
535
      imp
9✔
536
      trimFirstSpace ws2
9✔
537
      dynamizeFromClause from
9✔
538
  dot: AccessStart :=
9✔
539
    type: "AccessStart"
9✔
540
    children: ["."]
9✔
541
    optional: false
9✔
542
  switch imports?.type
9✔
543
    when "Identifier" // default import
9✔
544
      processCallMemberExpression
2✔
545
        type: "CallExpression"
2✔
546
        children: []
2✔
547
          parenthesizeExpression awaitExpression
2✔
548
          {}
2✔
549
            type: "PropertyAccess"
2✔
550
            dot
2✔
551
            name: "default"
2✔
552
            children: [ws1, dot, "default"]
2✔
553
    when "Star"
9✔
554
      parenthesizeExpression awaitExpression
1✔
555
    when "Declaration" // named imports
9✔
556
      object := convertNamedImportsToObject imports
6✔
557
      processCallMemberExpression
6✔
558
        type: "CallExpression"
6✔
559
        children: []
6✔
560
          parenthesizeExpression awaitExpression
6✔
561
          {}
6✔
562
            type: "PropertyGlob"
6✔
563
            dot
6✔
564
            object
6✔
565
            children: [ws1, dot, object]
6✔
566
            reversed: true
6✔
567
    else
9!
568
      throw new Error "Unsupported dynamic import"
×
569

1✔
570
function convertWithClause(withClause: WithClause, extendsClause?: [ASTLeaf, WSNode, ExpressionNode])
3✔
571
  let extendsToken, extendsTarget, ws: WSNode
3✔
572
  if extendsClause
3✔
573
    [ extendsToken, ws, extendsTarget ] = extendsClause
1✔
574
  else
2✔
575
    extendsToken =
2✔
576
      type: "Extends"
2✔
577
      children: [" extends"]
2✔
578
    ws = ""
2✔
579
    extendsTarget = "Object"
2✔
580

3✔
581
  wrapped := withClause.targets.reduce (extendsTarget, [wsNext, withTarget]) =>
3✔
582
    args := [ extendsTarget ]
10✔
583
    exp := {
10✔
584
      type: "CallExpression",
10✔
585
      children: [
10✔
586
        makeLeftHandSideExpression(withTarget),
10✔
587
        {
10✔
588
          type: "Call",
10✔
589
          args,
10✔
590
          children: ["(", trimFirstSpace(ws), args, ")"],
10✔
591
        },
10✔
592
      ],
10✔
593
    } as ExpressionNode
10✔
594

10✔
595
    ws = wsNext
10✔
596

10✔
597
    return exp
10✔
598
  , extendsTarget
3✔
599

3✔
600
  // Ensure at least one space after extends token
3✔
601
  return [ extendsToken, insertTrimmingSpace(ws, " "), wrapped ]
3✔
602

1✔
603
export {
1✔
604
  convertWithClause
1✔
605
  dynamizeImportDeclaration
1✔
606
  dynamizeImportDeclarationExpression
1✔
607
  prependStatementExpressionBlock
1✔
608
  processAssignmentDeclaration
1✔
609
  processDeclarationConditions
1✔
610
  processDeclarations
1✔
611
}
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