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

DanielXMoore / Civet / 21493057172

29 Jan 2026 08:08PM UTC coverage: 91.488% (-0.1%) from 91.587%
21493057172

push

github

web-flow
Merge pull request #1843 from DanielXMoore/cli-prompt

Fix REPL continuation prompt to `...`, clarifying indentation

3777 of 4116 branches covered (91.76%)

Branch coverage included in aggregate %.

1 of 33 new or added lines in 1 file covered. (3.03%)

1 existing line in 1 file now uncovered.

19182 of 20979 relevant lines covered (91.43%)

17425.81 hits per line

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

33.95
/source/cli.civet
1
{ compile, generate, parse, lib, isCompileError, SourceMap } from ./main.civet
1✔
2
type { ASTError, BlockStatement, ParseError, ParseErrors } from ./main.civet
1✔
3
{ findConfig, loadConfig } from ./config.civet
1✔
4

1✔
5
// unplugin ends up getting installed in the same dist directory
1✔
6
{rawPlugin} from ./unplugin/unplugin.civet
1✔
7
let unplugin:
1✔
8
  buildStart: () => Promise<void>
1✔
9
  buildEnd: (this: {emitFile: (data: {source: string, fileName: string, type: string}) => void}, useConfigFileNames?: boolean) => Promise<void>
1✔
10
  load: (this: {addWatchFile: (filename: string) => void}, filename: string) => Promise<{code: string, map: unknown}>
1✔
11

1✔
12
export function version: Promise<string>
×
13
  // Once import assertions (with form) are universal, we can switch to this:
×
14
  //import package from "../package.json" with type: "json"
×
15
  //package.version
×
16
  if import.meta.url  // ESM mode (e.g. testing)
×
17
    { createRequire } from node:module
×
18
    createRequire(import.meta.url)('../package.json').version
×
19
  else
×
20
    require('../package.json').version
×
21

1✔
22
encoding .= "utf8" as BufferEncoding
1✔
23

1✔
24
fs from node:fs/promises
1✔
25
type { Stats } from node:fs
1✔
26
path from node:path
1✔
27

1✔
28
// TODO: Once the types are exported within the Civet source code,
1✔
29
// we should import them directly here, instead of looking at an old release.
1✔
30
type { CompileOptions } from @danielx/civet
1✔
31

1✔
32
export interface Options extends CompileOptions
1✔
33
  version?: boolean
1✔
34
  help?: boolean
1✔
35
  run?: boolean
1✔
36
  compile?: boolean
1✔
37
  output?: string
1✔
38
  outputDir?: string
1✔
39
  outputExt?: string
1✔
40
  outputPath?: path.ParsedPath
1✔
41
  eval?: string
1✔
42
  config?: string | false | undefined
1✔
43
  ast?: boolean | "raw"
1✔
44
  repl?: boolean
1✔
45
  typecheck?: boolean
1✔
46
  emitDeclaration?: boolean
1✔
47
  typescript?: boolean
1✔
48

1✔
49
export interface ParsedArgs
1✔
50
  filenames: string[]
1✔
51
  scriptArgs: string[]
1✔
52
  options: Options
1✔
53

1✔
54
export function parseArgs(args: string[], isTTY = process.stdin.isTTY): Promise<ParsedArgs>
54✔
55
  options: Options := {}
54✔
56
  isRun := => not (or)
54✔
57
    options.ast
60✔
58
    options.compile
59✔
59
    options.typecheck
42✔
60
    options.emitDeclaration
39✔
61
  filenames: string[] .= []
54✔
62
  scriptArgs: string[] .= []
54✔
63

54✔
64
  options.version = true if args.includes '-version'
54✔
65
  options.help = true if args.includes '-help'
54✔
66
  return {filenames, scriptArgs, options} if options.version or options.help
54✔
67

52✔
68
  i .= 0
52✔
69
  errors .= 0
52✔
70
  function endOfArgs(j: number): void
52✔
71
    i = args.length  // trigger end of loop
×
72
    return if j >= args.length  // no more args
×
73
    if options.run
×
74
      filenames.push args[j]
×
75
      scriptArgs = args[j+1..]
×
76
    else
×
77
      filenames.push ...args[j..]
×
78
  while i < args.length
52✔
79
    arg := args[i]
66✔
80
    // Split -ab into -a -b
66✔
81
    if /^-\w{2,}$/.test arg
66✔
82
      args[i..i] =
1✔
83
        for each char of arg[1..]
1✔
84
          `-${char}`
2✔
85
      continue
1✔
86
    // Main argument handling
65✔
87
    switch arg
65✔
88
      when '-v', '--version'
2✔
89
        options.version = true
2✔
90
      when '-h', '--help'
3✔
91
        options.help = true
3✔
92
      when '-c', '--compile'
8✔
93
        options.compile = true
8✔
94
      when '-o', '--output'
16✔
95
        options.output = args[++i]
16✔
96
      when '-e', '--eval'
66✔
97
        options.eval = args[++i]
2✔
98
      when '--config'
66✔
99
        options.config = args[++i]
1✔
100
      when '--no-config'
66✔
101
        options.config = false
1✔
102
      when '--civet'
66✔
103
        Object.assign options.parseOptions ??= {},
5✔
104
          parse `civet ${args[++i]}`,
5✔
105
            startRule: 'CivetPrologueContent'
5✔
106
            filename: '--civet argument'
5✔
107
          |> .config
5✔
108
      when '--comptime'
66✔
109
        (options.parseOptions ??= {}).comptime = true
3✔
110
      when '--no-comptime'
66✔
111
        (options.parseOptions ??= {}).comptime = false
3✔
112
      when '--ast'
66✔
113
        options.ast = true
1✔
114
      when '--no-cache'
66✔
115
        options.noCache = true
1✔
116
      when '--inline-map'
66✔
117
        options.inlineMap = true
1✔
118
      when '--js'
66✔
119
        options.js = true
1✔
120
      when '--hits'
66✔
121
        options.hits = args[++i]
1✔
122
      when '--trace'
66✔
123
        options.trace = args[++i]
1✔
124
      when '--typecheck'
66✔
125
        options.typecheck = true
3✔
126
      when '--emit-declaration', '--emitDeclaration'
4✔
127
        options.emitDeclaration = true
4✔
128
      when '--'
66!
129
        endOfArgs ++i  // remaining arguments are filename and/or arguments
66✔
130
      else
66✔
131
        if arg.startsWith('-') and arg is not '-'
8!
132
          console.error `Invalid command-line argument: ${arg}`
×
133
          errors++
×
134
        else if (options.run = isRun())
8✔
135
          endOfArgs i  // remaining arguments are arguments to the script
8!
136
        else
8✔
137
          filenames.push arg
8✔
138
    i++
65✔
139

52✔
140
  options.typescript = true if options.typecheck or options.emitDeclaration
54✔
141

52✔
142
  unless filenames.length or options.typescript or options.eval
54✔
143
    if isTTY
41✔
144
      options.repl = true
40✔
145
    else
1✔
146
      // When piped, default to old behavior of transpiling stdin to stdout
1✔
147
      options.compile = true
1✔
148
      options.run = false
1✔
149
      filenames = ['-']
1✔
150

52✔
151
  // Parse `output` option into forced directory, extension, and/or full path
52✔
152
  if options.output and options.output is not '-'
54✔
153
    optionsPath := path.parse options.output
14✔
154
    let stat: Stats | null
14✔
155
    try
14✔
156
      stat = await fs.stat options.output
2✔
157
    catch
14✔
158
      stat = null
12✔
159
    if stat?.isDirectory() or options.output.endsWith(path.sep) or
14✔
160
                              options.output.endsWith('/')
10✔
161
      // -o dir writes outputs into that directory with default name
4✔
162
      options.outputDir = options.output
4✔
163
    else if /^(\.[^]+)+$/.test optionsPath.base
10✔
164
      // -o .js or .ts or .civet.js or .civet.ts etc. to control extension
6✔
165
      options.outputExt = optionsPath.base
6✔
166
      // Can also be prefixed by a directory name
6✔
167
      options.outputDir = optionsPath.dir if optionsPath.dir
6✔
168
    else
4✔
169
      // -o filename fully specifies the output filename
4✔
170
      // (don't use this with multiple input files)
4✔
171
      options.outputPath = optionsPath
4✔
172
      options.outputExt = optionsPath.ext
4✔
173
  // For CLI compilations, default to rewriting imports to output extension,
54✔
174
  // with .jsx instead of .tsx to satisfy TypeScript.
54✔
175
  (options.parseOptions ??= {}).rewriteCivetImports ??=
54✔
176
    options.outputExt ?? '.civet.jsx'
54✔
177

54✔
178
  process.exit Math.min 255, errors if errors
54!
179
  options.run = isRun()
52✔
180
  {filenames, scriptArgs, options}
52✔
181

1✔
182
type ReadFile = (
1✔
183
  filename: string
1✔
184
  stdin: boolean
1✔
185
  content?: string
1✔
186
  error?: unknown
1✔
187
) & ((content: string) | (error: unknown))
1✔
188

1✔
189
function readFiles(filenames: string[], evalString?: string): AsyncGenerator<ReadFile>
×
190
  if evalString?
×
191
    yield
×
192
      filename: '<eval>'
×
193
      content: evalString + '\n'
×
194
      stdin: true
×
195

×
196
  for each let filename of filenames
×
197
    stdin := filename is '-'
×
198
    try
×
199
      let content: string
×
200
      if stdin
×
201
        process.stdin.setEncoding encoding
×
202

×
203
        // Try to guess filename for stdin, such as /dev/fd/filename
×
204
        filename = "<stdin>"
×
205
        try
×
206
          filename = await fs.realpath '/dev/stdin'
×
207

×
208
        if process.stdin.isTTY
×
209
          // In interactive stdin, `readline` lets user end the file via ^D.
×
210
          lines: string[] := []
×
211
          rl := import('node:readline') |> await |> .createInterface process.stdin, process.stdout
×
212
          rl.on 'line', (buffer: string) => lines.push buffer + '\n'
×
213
          content = await new Promise (resolve, reject) =>
×
214
            rl.on 'SIGINT', =>
×
215
              reject '^C'
×
216
            rl.on 'close', =>
×
217
              resolve lines.join ''
×
218
        else
×
219
          // For piped stdin, read stdin directly to avoid potential echo.
×
220
          content = (chunk for await chunk of process.stdin).join ''
×
221
      else
×
222
        content = await fs.readFile filename
×
223
      yield {filename, content, stdin}
×
224
    catch error
×
225
      yield {filename, error, stdin}
×
226

1✔
227
declare global
1✔
228
  var quit: () => void, exit: () => void
1✔
229
  var v8debug: unknown
1✔
230

1✔
231
export function repl(args: string[], options: Options)
×
232
  * as vm from 'node:vm'
×
233
  // Node 21.7.0+ supports dynamic import() in vm calls via this constant:
×
234
  importModuleDynamically .= vm.constants?.USE_MAIN_CONTEXT_DEFAULT_LOADER
×
235
  unless importModuleDynamically
×
236
    // For older Node, we need to provide our own dynamic import function,
×
237
    // which requires `--experimental-vm-modules`.  Check if we did it already:
×
238
    if vm.SourceTextModule?
×
239
      { pathToFileURL } from node:url
×
240
      importModuleDynamically = (specifier: string) =>
×
241
        if /^\.\.?[/\\]/.test specifier
×
242
          import pathToFileURL(path.join process.cwd(), specifier).href
×
243
        else
×
244
          import specifier
×
245
    else
×
246
      // If not, run this script with `--experimental-vm-modules`.
×
247
      execArgv := [ '--experimental-vm-modules' ]
×
248
      /* This doesn't work; --loader seems to force ESM mode which breaks CLI.
×
249
      { register } from node:module
×
250
      unless register
×
251
        // On Node <20.6.0, we need to use `--loader` for the ESM loader.
×
252
        execArgv.push '--loader', '@danielx/civet/esm'
×
253
      */
×
254
      { fork } from node:child_process
×
255
      fork __filename, args, {
×
256
        execArgv
×
257
        stdio: 'inherit'
×
258
      }
×
259
      return
×
260

×
261
  await import '../register.js'
×
262
  console.log `Civet ${await version()} REPL.  Enter a blank line to ${
×
263
    switch
×
264
      when options.ast then 'parse'
×
265
      when options.compile then 'transpile'
×
266
      else 'execute'
×
267
  } code.`
×
268
  global.quit = global.exit = => process.exit 0
×
269
  import * as nodeRepl from node:repl
×
NEW
270
  prompt :=
×
NEW
271
    switch
×
NEW
272
      when options.ast then '🌲> '
×
NEW
273
      when options.compile then '🐈> '
×
NEW
274
      else '🐱> '
×
NEW
275
  r := nodeRepl.start {}
×
NEW
276
    prompt
×
277
    writer:
×
278
      if options.ast
×
279
        (obj: unknown) =>
×
280
          try
×
281
            JSON.stringify obj, null, 2
×
282
          catch e
×
283
            console.log `Failed to stringify: ${e}`
×
284
            ''
×
285
      else if options.compile
×
286
        (obj: unknown) =>
×
287
          if obj <? 'string'
×
288
            obj?.replace /\n*$/, ''
×
289
          else
×
290
            ''
×
291
    eval: (input: string, context, filename: string, callback) ->
×
292
      // Newer Node versions use \r for intermediate line endings
×
293
      input = input.replace /\r/g, '\n'
×
294
      if input is '\n'  // blank input
×
295
        callback null, undefined
×
296
      else if input in ['quit\n', 'exit\n', 'quit()\n', 'exit()\n']
×
297
        process.exit 0
×
298
      else if input.endsWith '\n\n'  // finished input with blank line
×
299
        function showError(error: ???)
×
300
          console.error "Error while parsing Civet code:"
×
301
          if isCompileError error
×
302
            // Unwrap ParseErrors to first error
×
303
            if (error as ParseErrors).errors?
×
304
              error = (error as ParseErrors).errors[0]
×
305
            console.log ```
×
306
              ${input.split('\n')[0...(error as ParseError).line].join '\n'}
×
307
              ${' '.repeat (error as ParseError).column - 1}^ ${(error as ParseError).header}
×
308
            ```
×
309
          else
×
310
            console.error error
×
311

×
312
        let output: string
×
313
        if options.compile or options.ast
×
314
          try
×
315
            output = await compile input, {...options, filename}
×
316
          catch error
×
317
            showError error
×
318
            return callback null, undefined
×
319
          return callback null, output
×
320

×
321
        parseOptions := {...options.parseOptions,
×
322
          repl: true
×
323
        }
×
324

×
325
        let ast: BlockStatement
×
326
        try
×
327
          ast = await compile input, {...options, parseOptions, filename, ast: true}
×
328
        catch error
×
329
          showError error
×
330
          return callback null, undefined
×
331

×
332
        errors: ASTError[] .= []
×
333
        try
×
334
          output = generate ast, { ...options, errors, sourceMap: undefined }
×
335
        catch error
×
336
          //console.error "Failed to transpile Civet:"
×
337
          console.error error
×
338
          return callback null, undefined
×
339
        if errors#
×
340
          // Rerun with sourceMap
×
341
          errors = []
×
342
          generate ast, { ...options, errors, sourceMap: new SourceMap input }
×
343
          showError errors[0]
×
344
          return callback null, undefined
×
345

×
346
        let result: string
×
347
        try
×
348
          result = vm.runInContext output, context, {
×
349
            filename
×
350
            importModuleDynamically
×
351
          }
×
352
        catch error
×
353
          return callback error as Error, undefined
×
354

×
355
        if ast.topLevelAwait
×
356
          // If there was a top-level await, the result is a promise
×
357
          // that we need to await before returning it.
×
358
          try
×
359
            result = await result
×
360
          catch error
×
361
            callback error as Error, undefined
×
362
          else
×
363
            callback null, result
×
364
        else
×
365
          callback null, result
×
366
      else  // still reading
×
367
        callback (new nodeRepl.Recoverable new Error "Enter a blank line to execute code."), null
×
UNCOV
368

×
NEW
369
  // Hack to force '...' prompt for continuation input lines
×
NEW
370
  bufferedCommandSymbol :=
×
NEW
371
    Object.getOwnPropertySymbols(r).find (symbol) =>
×
NEW
372
      symbol.description is 'bufferedCommand'
×
NEW
373
  interfaceSetPrompt :=
×
NEW
374
    Object.getPrototypeOf(Object.getPrototypeOf(r))?.setPrompt
×
NEW
375
  writeToOutputSymbol :=
×
NEW
376
    Object.getOwnPropertySymbols(Object.getPrototypeOf(Object.getPrototypeOf(r))).find (symbol) =>
×
NEW
377
      symbol.description is '_writeToOutput'
×
NEW
378
  if bufferedCommandSymbol and interfaceSetPrompt and writeToOutputSymbol
×
NEW
379
    // Fix display of continuation prompt from `| ` to `... `
×
NEW
380
    originalWriteToOutput := r[writeToOutputSymbol]
×
NEW
381
    Object.defineProperty r, writeToOutputSymbol,
×
NEW
382
      value: (text: string) =>
×
NEW
383
        originalWriteToOutput.call r, text.replace /(^|\n)\| /g, '$1... '
×
NEW
384
      configurable: true
×
NEW
385
      writable: true
×
NEW
386
    // Fix internal prompt string from `| ` to `... `
×
NEW
387
    r.displayPrompt = (preserveCursor?: boolean) =>
×
NEW
388
      interfaceSetPrompt.call r,
×
NEW
389
        if r[bufferedCommandSymbol]?#
×
NEW
390
          '... '
×
NEW
391
        else
×
NEW
392
          prompt
×
NEW
393
      r.prompt preserveCursor
×
394

1✔
395
export function cli(args = process.argv[2..])
×
396
  // process.argv gets overridden when running scripts, but gets saved here
×
397

×
398
  {filenames, scriptArgs, options} .= await parseArgs args
×
399

×
400
  if options.version
×
401
    console.log await version()
×
402
    process.exit(0)
×
403

×
404
  if options.help
×
405
    process.stderr.write """
×
406
       ▄▄· ▪   ▌ ▐·▄▄▄ .▄▄▄▄▄
×
407
      ▐█ ▌▪██ ▪█·█▌▀▄.▀·•██       _._     _,-'""`-._
×
408
      ██ ▄▄▐█·▐█▐█•▐▀▀▪▄ ▐█.▪    (,-.`._,'(       |\\`-/|
×
409
      ▐███▌▐█▌ ███ ▐█▄▄▌ ▐█▌·        `-.-' \\ )-`( , o o)
×
410
      ·▀▀▀ ▀▀▀. ▀   ▀▀▀  ▀▀▀               `-    \\`_`"'-
×
411

×
412

×
413
Usage:
×
414

×
415
    civet                                        # REPL for executing code
×
416
    civet -c                                     # REPL for transpiling code
×
417
    civet --ast                                  # REPL for parsing code
×
418
    civet [options] input.civet                  # run input.civet
×
419
    civet [options] -c input.civet               # -> input.civet.tsx
×
420
    civet [options] -c input.civet -o .ts        # -> input.ts
×
421
    civet [options] -c input.civet -o dir        # -> dir/input.civet.tsx
×
422
    civet [options] -c input.civet -o dir/.ts    # -> dir/input.ts
×
423
    civet [options] -c input.civet -o output.ts  # -> output.ts
×
424
    civet [options] < input.civet > output.ts    # pipe form
×
425

×
426
Options:
×
427
  --help           Show this help message
×
428
  --version        Show the version number
×
429
  -o / --output XX Specify output directory and/or extension, or filename
×
430
  -c / --compile   Compile input files to TypeScript (or JavaScript)
×
431
  -e / --eval XX   Evaluate specified code (or compile it with -c)
×
432
  --config XX      Specify a config file (default scans for a config.civet, civet.json, civetconfig.civet or civetconfig.json file, optionally in a .config directory, or starting with a .)
×
433
  --civet XX       Specify civet compiler flag, as in "civet XX" prologue
×
434
  --comptime       Enable execution of code during compilation via comptime
×
435
  --no-config      Don't scan for a config file
×
436
  --js             Strip out all type annotations; default to .jsx extension
×
437
  --ast            Print the AST instead of the compiled code
×
438
  --inline-map     Generate a sourcemap
×
439
  --no-cache       Disable compiler caching (slow, for debugging)
×
440
  --typecheck      Run TypeScript and output diagnostics
×
441
  --emit-declaration  Run TypeScript and emit .d.ts files (if no errors)
×
442
  --trace XX       Log detailed parsing notes to a file, for parser debugging
×
443

×
444
You can use - to read from stdin or (prefixed by -o) write to stdout.
×
445

×
446
By default, .civet imports get rewritten to use the output extension.
×
447
You can override this behavior via: --civet rewriteCivetImports=.ext
×
448

×
449

×
450
    """
×
451
    process.exit(0)
×
452

×
453
  if options.config is undefined
×
454
    options.config = await findConfig process.cwd()
×
455
  if options.config
×
456
    parsed := await loadConfig options.config as string
×
457
    options = {
×
458
      ...parsed
×
459
      ...options
×
460
    }
×
461
    // Deep merge
×
462
    if parsed.parseOptions and options.parseOptions
×
463
      options.parseOptions = {
×
464
        ...parsed.parseOptions
×
465
        ...options.parseOptions
×
466
      }
×
467

×
468
  if options.typescript
×
469
    unpluginOptions := {
×
470
      ...options
×
471
      ts: if options.js then 'civet' else 'preserve'
×
472
      outputExtension: '.tsx'
×
473
      declarationExtension:
×
474
        options.outputExt?.replace /\.[jt]sx?$/i, '.d.ts'
×
475
    }
×
476
    // TypeScript wants .civet.tsx files imported as .civet.jsx
×
477
    (unpluginOptions.parseOptions ??= {}).rewriteCivetImports = '.civet.jsx'
×
478
    unplugin = rawPlugin unpluginOptions, framework: 'civet-cli'
×
479
    await unplugin.buildStart()
×
480

×
481
  // In run mode, compile to JS with source maps
×
482
  if options.run
×
483
    options.js = true
×
484
    options.inlineMap = true
×
485

×
486
  return repl args, options if options.repl
×
487

×
488
  // Ignore EPIPE errors, e.g. when piping to `head`
×
489
  process.stdout.on "error", (e) =>
×
490
    if e.code is in ['EPIPE', 'EOF']
×
491
      process.exit 0
×
492
    else
×
493
      console.error e
×
494
      process.exit 1
×
495

×
496
  errors .= 0
×
497
  for await let {filename, error, content, stdin} of readFiles filenames, options.eval
×
498
    if error
×
499
      console.error `${filename} failed to load:`
×
500
      console.error error
×
501
      errors++
×
502
      continue
×
503

×
504
    outputPath: path.FormatInputPathObject := options.outputPath ??
×
505
      path.parse filename
×
506
      ||> delete .base  // use name and ext
×
507
      ||> .ext +=  // default extension
×
508
        if options.js
×
509
          ".jsx"
×
510
        else
×
511
          ".tsx"
×
512
      ||> ($) =>  // `output` option overrides
×
513
        $.dir = options.outputDir if options.outputDir?
×
514
        $.ext = options.outputExt if options.outputExt?
×
515
    outputFilename := path.format outputPath
×
516

×
517
    // Transpile
×
518
    let output: string
×
519
    try
×
520
      if unplugin?
×
521
        output = unplugin.load.call {
×
522
          addWatchFile();
×
523
        }, `${filename}.tsx`
×
524
        |> await
×
525
        |> .code
×
526
      else
×
527
        output = await compile content!, {...options, filename, outputFilename}
×
528
    catch error
×
529
      //console.error `${filename} failed to transpile:`
×
530
      console.error error
×
531
      errors++
×
532
      continue
×
533

×
534
    if options.ast
×
535
      process.stdout.write JSON.stringify(output, null, 2)
×
536
    else if options.compile
×
537
      if (stdin and not options.output) or options.output is '-'
×
538
        process.stdout.write output
×
539
      else
×
540
        // Make output directory in case it doesn't already exist
×
541
        outputDir := path.dirname outputFilename
×
542
        await fs.mkdir outputDir, recursive: true unless outputDir is '.'
×
543
        try
×
544
          await fs.writeFile outputFilename, output
×
545
        catch error
×
546
          console.error `${outputFilename} failed to write:`
×
547
          console.error error
×
548
          errors++
×
549
    else if options.run
×
550
      esm := do
×
551
        if output is like /\b(await|import|export)\b/  // potentially ESM
×
552
          ast := await compile content!, {...options, ast: true, filename}
×
553
          (or)
×
554
            lib.hasAwait ast
×
555
            lib.hasImportDeclaration ast
×
556
            lib.hasExportDeclaration ast
×
557
      if esm
×
558
        // Run ESM code via `node --import @danielx/civet/register` subprocess
×
559
        if stdin
×
560
          // If code was read on stdin via command-line argument "-", try to
×
561
          // save it in a temporary file in same directory so paths are correct.
×
562
          filename = `.stdin-${process.pid}.civet`
×
563
          try
×
564
            await fs.writeFile filename, content!, {encoding}
×
565
          catch e
×
566
            console.error `Could not write ${filename} for Civet ESM mode:`
×
567
            console.error e
×
568
            process.exit 1
×
569
        { fork } from node:child_process
×
570

×
571
        { register } from node:module
×
572
        let execArgv
×
573
        if register
×
574
          // On Node 20.6.0+, module.register does the work for us;
×
575
          // we just need to `--import` ESM/CJS registration.
×
576
          { join } from path
×
577
          { pathToFileURL } from node:url
×
578
          execArgv = [
×
579
            '--import'
×
580
            pathToFileURL(join __dirname, '../register.js').href
×
581
          ]
×
582
        else
×
583
          // On Node <20.6.0, we need to use `--loader` for the ESM loader.
×
584
          execArgv = [
×
585
            '--loader', '@danielx/civet/esm' // ESM
×
586
            '--require', '@danielx/civet/register' // CJS
×
587
          ]
×
588

×
589
        debugRe := /--debug|--inspect/
×
590
        isDebug := v8debug <? "object" or debugRe.test(process.execArgv.join(' ')) or debugRe.test(process.env.NODE_OPTIONS ?? '')
×
591
        if isDebug
×
592
          execArgv.push "--inspect=" + (process.debugPort + 1)
×
593
        child := fork filename, [
×
594
          ...scriptArgs
×
595
        ], {
×
596
          execArgv
×
597
          stdio: 'inherit'
×
598
        }
×
599
        child.on 'exit', (code) =>
×
600
          if stdin
×
601
            // Delete temporary file
×
602
            await fs.unlink filename
×
603

×
604
          process.exit code ?? 1
×
605
        // Instead of default behavior of exiting, pass on signals to
×
606
        // child process and let it decide whether to exit
×
607
        for signal of ['SIGINT', 'SIGTERM', 'SIGHUP']
×
608
          process.on signal, => child.kill signal
×
609

×
610
      else
×
611
        await import '../register.js'
×
612
        try
×
613
          module.filename = await fs.realpath filename
×
614
        catch
×
615
          module.filename = filename
×
616
        process.argv = ["civet", module.filename, ...scriptArgs]
×
617
        module.paths =
×
618
          import 'node:module' |> await |> ._nodeModulePaths path.dirname module.filename
×
619
        try
×
620
          module._compile output, module.filename
×
621
        catch error
×
622
          console.error `${filename} crashed while running in CJS mode:`
×
623
          console.error error
×
624
          process.exit 1
×
625

×
626
  process.exitCode = Math.min 255, errors
×
627
  if unplugin?
×
628
    try
×
629
      await unplugin.buildEnd.call {
×
630
        emitFile({source, fileName})
×
631
          fs.writeFile fileName, source
×
632
      }, not filenames.length
×
633
    catch error
×
634
      if match := (error as Error).message.match /Aborting build because of (\d+) TypeScript diagnostic/
×
635
        process.exitCode = Math.min 255, errors + +match[1]
×
636
      else
×
637
        process.exitCode = 1
×
638
        throw error
×
639

1✔
640
// build/build.sh adds a call to cli() at the end here
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