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

Keylan / yamllint-ts / 21097328415

17 Jan 2026 04:17PM UTC coverage: 84.739%. Remained the same
21097328415

push

github

Keylan
Improve test coverage, fix bugs

1431 of 1720 branches covered (83.2%)

Branch coverage included in aggregate %.

82 of 89 new or added lines in 3 files covered. (92.13%)

71 existing lines in 2 files now uncovered.

1523 of 1766 relevant lines covered (86.24%)

3072.64 hits per line

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

0.0
/src/cli.ts
1
#!/usr/bin/env node
2

3
/**
4
 * yamllint-ts - TypeScript YAML Linter
5
 * Command Line Interface
6
 *
7
 * Copyright (C) 2024
8
 * Licensed under GPL-3.0
9
 */
10

11
import { program } from 'commander';
12
import * as fs from 'fs';
13
import { run } from './linter.js';
14
import type { LintProblem } from './linter.js';
15
import { YamlLintConfig, YamlLintConfigError } from './config.js';
16
// Import rules to ensure they are registered before config is used
17
import './rules/index.js';
18
import {
19
  APP_NAME,
20
  APP_VERSION,
21
  APP_DESCRIPTION,
22
  PROBLEM_LEVELS,
23
  findFilesRecursively,
24
  findProjectConfigFilepath,
25
  getUserGlobalConfigPath,
26
  getEffectiveFormat,
27
  Format,
28
} from './cli-utils.js';
29

30
// =============================================================================
31
// Problem Display
32
// =============================================================================
33

34
/**
35
 * Show problems for a file.
36
 */
37
function showProblems(
38
  problems: Generator<LintProblem>,
39
  file: string,
40
  format: string,
41
  noWarn: boolean
42
): number {
43
  let maxLevel = 0;
×
44
  let first = true;
×
45

NEW
46
  const effectiveFormat = getEffectiveFormat(format);
×
47

48
  for (const problem of problems) {
×
UNCOV
49
    const level = PROBLEM_LEVELS[problem.level ?? 'error'] ?? 0;
×
UNCOV
50
    maxLevel = Math.max(maxLevel, level);
×
51

52
    // Skip warnings if --no-warnings
UNCOV
53
    if (noWarn && problem.level !== 'error') {
×
54
      continue;
×
55
    }
56

57
    if (effectiveFormat === 'parsable') {
×
58
      console.log(Format.parsable(problem, file));
×
59
    } else if (effectiveFormat === 'github') {
×
60
      if (first) {
×
61
        console.log(`::group::${file}`);
×
UNCOV
62
        first = false;
×
63
      }
64
      console.log(Format.github(problem, file));
×
UNCOV
65
    } else if (effectiveFormat === 'colored') {
×
UNCOV
66
      if (first) {
×
NEW
67
        console.log(file);
×
UNCOV
68
        first = false;
×
69
      }
70
      console.log(Format.standardColor(problem, file));
×
71
    } else {
72
      // standard
UNCOV
73
      if (first) {
×
74
        console.log(file);
×
75
        first = false;
×
76
      }
UNCOV
77
      console.log(Format.standard(problem, file));
×
78
    }
79
  }
80

81
  // End github group
UNCOV
82
  if (!first && effectiveFormat === 'github') {
×
83
    console.log('::endgroup::');
×
84
  }
85

86
  // Add blank line after file (except for parsable)
UNCOV
87
  if (!first && effectiveFormat !== 'parsable') {
×
UNCOV
88
    console.log('');
×
89
  }
90

91
  return maxLevel;
×
92
}
93

94
// =============================================================================
95
// Main CLI
96
// =============================================================================
97

98
async function main(): Promise<void> {
UNCOV
99
  program
×
100
    .name(APP_NAME)
101
    .description(APP_DESCRIPTION)
102
    .version(APP_VERSION, '-v, --version')
103
    .argument('[files...]', 'files or directories to lint')
104
    .option('-c, --config-file <file>', 'path to a custom configuration')
105
    .option('-d, --config-data <yaml>', 'custom configuration (as YAML source)')
106
    .option('--list-files', 'list files to lint and exit')
107
    .option(
108
      '-f, --format <format>',
109
      'format for parsing output (parsable, standard, colored, github, auto)',
110
      'auto'
111
    )
112
    .option('-s, --strict', 'return non-zero exit code on warnings as well as errors')
113
    .option('--no-warnings', 'output only error level problems')
114
    .option('--stdin', 'read from standard input')
115
    .parse(process.argv);
116

UNCOV
117
  const options = program.opts();
×
UNCOV
118
  const files = program.args;
×
119

120
  // Validate arguments
UNCOV
121
  if (!options.stdin && files.length === 0) {
×
UNCOV
122
    console.error('Error: No files specified. Use --stdin to read from stdin.');
×
UNCOV
123
    process.exit(1);
×
124
  }
125

126
  // Load configuration
127
  let conf: YamlLintConfig;
128

UNCOV
129
  try {
×
UNCOV
130
    if (options.configData) {
×
UNCOV
131
      let configData = options.configData;
×
132
      // If config data doesn't contain ':', treat it as an extends reference
UNCOV
133
      if (configData !== '' && !configData.includes(':')) {
×
UNCOV
134
        configData = `extends: ${configData}`;
×
135
      }
UNCOV
136
      conf = new YamlLintConfig(configData);
×
UNCOV
137
    } else if (options.configFile) {
×
UNCOV
138
      conf = YamlLintConfig.fromFile(options.configFile);
×
139
    } else {
140
      // Try to find project config
UNCOV
141
      const projectConfig = findProjectConfigFilepath();
×
UNCOV
142
      if (projectConfig) {
×
UNCOV
143
        conf = YamlLintConfig.fromFile(projectConfig);
×
144
      } else {
145
        // Try user global config
UNCOV
146
        const userConfig = getUserGlobalConfigPath();
×
UNCOV
147
        if (fs.existsSync(userConfig)) {
×
UNCOV
148
          conf = YamlLintConfig.fromFile(userConfig);
×
149
        } else {
150
          // Use default config
UNCOV
151
          conf = new YamlLintConfig('extends: default');
×
152
        }
153
      }
154
    }
155
  } catch (e) {
UNCOV
156
    if (e instanceof YamlLintConfigError) {
×
UNCOV
157
      console.error(`Configuration error: ${e.message}`);
×
158
    } else {
UNCOV
159
      console.error(`Error: ${e instanceof Error ? e.message : String(e)}`);
×
160
    }
UNCOV
161
    process.exit(1);
×
162
  }
163

164
  // List files mode
UNCOV
165
  if (options.listFiles) {
×
UNCOV
166
    for (const file of findFilesRecursively(files, conf)) {
×
UNCOV
167
      if (!conf.isFileIgnored(file)) {
×
UNCOV
168
        console.log(file);
×
169
      }
170
    }
UNCOV
171
    process.exit(0);
×
172
  }
173

UNCOV
174
  let maxLevel = 0;
×
175

176
  // Lint files
UNCOV
177
  for (const file of findFilesRecursively(files, conf)) {
×
UNCOV
178
    const filepath = file.replace(/^\.\//, '');
×
179

UNCOV
180
    try {
×
UNCOV
181
      const content = fs.readFileSync(file);
×
UNCOV
182
      const problems = run(content, conf, filepath);
×
NEW
183
      const level = showProblems(problems, file, options.format, !options.warnings);
×
UNCOV
184
      maxLevel = Math.max(maxLevel, level);
×
185
    } catch (e) {
UNCOV
186
      console.error(`Error reading ${file}: ${e instanceof Error ? e.message : String(e)}`);
×
UNCOV
187
      process.exit(1);
×
188
    }
189
  }
190

191
  // Read from stdin
UNCOV
192
  if (options.stdin) {
×
UNCOV
193
    try {
×
UNCOV
194
      const chunks: Buffer[] = [];
×
195

196
      // Read all data from stdin
UNCOV
197
      for await (const chunk of process.stdin) {
×
UNCOV
198
        chunks.push(Buffer.from(chunk));
×
199
      }
200

UNCOV
201
      const content = Buffer.concat(chunks);
×
UNCOV
202
      const problems = run(content, conf, null);
×
NEW
203
      const level = showProblems(problems, 'stdin', options.format, !options.warnings);
×
UNCOV
204
      maxLevel = Math.max(maxLevel, level);
×
205
    } catch (e) {
UNCOV
206
      console.error(`Error reading stdin: ${e instanceof Error ? e.message : String(e)}`);
×
UNCOV
207
      process.exit(1);
×
208
    }
209
  }
210

211
  // Exit code
UNCOV
212
  if (maxLevel === PROBLEM_LEVELS['error']) {
×
UNCOV
213
    process.exit(1);
×
UNCOV
214
  } else if (maxLevel === PROBLEM_LEVELS['warning']) {
×
UNCOV
215
    process.exit(options.strict ? 2 : 0);
×
216
  } else {
UNCOV
217
    process.exit(0);
×
218
  }
219
}
220

221
// Run CLI
UNCOV
222
main().catch((e) => {
×
UNCOV
223
  console.error(`Fatal error: ${e instanceof Error ? e.message : String(e)}`);
×
UNCOV
224
  process.exit(1);
×
225
});
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