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

protofire / solhint / #143847

09 May 2026 04:07PM UTC coverage: 92.035% (-5.8%) from 97.831%
#143847

push

400 of 483 branches covered (82.82%)

1063 of 1155 relevant lines covered (92.03%)

117.56 hits per line

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

96.67
/../lib/index.js
1
const fs = require('fs')
1✔
2
const path = require('path')
1✔
3
const parser = require('@solidity-parser/parser')
1✔
4
const { globSync } = require('glob')
1✔
5
const ignore = require('ignore')
1✔
6
const astParents = require('ast-parents')
1✔
7
const { applyExtends, loadConfigForFile } = require('./config/config-file')
1✔
8
const Reporter = require('./reporter')
1✔
9
const TreeListener = require('./tree-listener')
1✔
10
const checkers = require('./rules/index')
11
const { readCache, writeCache, shouldLint, updateCacheEntry } = require('./cache/cache-manager')
12

198✔
13
function parseInput(inputStr) {
14
  try {
198✔
15
    // first we try to parse the string as we normally do
16
    return parser.parse(inputStr, { loc: true, range: true })
5✔
17
  } catch (e) {
18
    // using 'loc' may throw when inputStr is empty or only has comments
5✔
19
    return parser.parse(inputStr, {})
20
  }
21
}
22

×
23
function processStr(inputStr, config = {}, fileName = '') {
24
  config = applyExtends(config)
25

26
  let ast
27
  try {
196!
28
    ast = parseInput(inputStr)
198✔
29
  } catch (e) {
30
    if (e instanceof parser.ParserError) {
198✔
31
      const reporter = new Reporter([], config)
198✔
32
      for (const error of e.errors) {
198✔
33
        reporter.addReport(
198✔
34
          error.line,
35
          error.column,
198✔
36
          Reporter.SEVERITY.ERROR,
198✔
37
          `Parse error: ${error.message}`,
38
        )
198✔
39
      }
40
      return reporter
41
    } else {
42
      throw e
2✔
43
    }
2✔
44
  }
45

2✔
46
  const tokens = parser.tokenize(inputStr, { loc: true, range: true })
47
  const reporter = new Reporter(tokens, config)
48
  const listener = new TreeListener(checkers(reporter, config, inputStr, tokens, fileName))
49

1✔
50
  astParents(ast)
51
  parser.visit(ast, listener)
1✔
52

1✔
53
  return reporter
54
}
1✔
55

56
function processAndCache(file, config, cacheState = null) {
57
  const useCache = config?.cache
1✔
58

59
  const code = fs.readFileSync(file, 'utf8')
60

61
  if (useCache && cacheState) {
62
    const { cacheData, updatedCache } = cacheState
63
    if (!shouldLint(file, code, config, cacheData)) {
64
      const emptyReport = new Reporter([], config)
65
      emptyReport.file = file
66
      emptyReport.skipped = true
67
      return { report: emptyReport, updatedCache }
68
    }
69
  }
70

71
  const report = processStr(code, config, file)
72
  report.file = file
73

74
  const hasErrors = report.reports.some((r) => r.severity === 2)
75

76
  if (useCache && cacheState && !hasErrors) {
77
    updateCacheEntry(file, code, config, cacheState.updatedCache)
78
  }
79

80
  return { report, updatedCache: cacheState?.updatedCache }
81
}
82

83
// eslint-disable-next-line default-param-last
84
function processFile(file, config, rootDir = process.cwd(), explicitConfigPath) {
85
  const finalConfig =
86
    config !== undefined ? config : loadConfigForFile(file, rootDir, explicitConfigPath)
87

88
  const defaultCachePath = path.join('node_modules', '.cache', 'solhint', '.solhintcache.json')
89
  const cachePath = finalConfig.cacheLocation || defaultCachePath
90

91
  let cacheState = null
92
  if (finalConfig.cache) {
93
    const cacheData = readCache(cachePath)
94
    cacheState = {
95
      cacheData,
96
      updatedCache: { ...cacheData },
97
    }
98
  }
99

100
  const { report, updatedCache } = processAndCache(file, finalConfig, cacheState)
101

102
  if (finalConfig.cache && updatedCache) {
103
    fs.mkdirSync(path.dirname(cachePath), { recursive: true }) // make sure the directory exists
104
    writeCache(cachePath, updatedCache)
105
  }
106

107
  return report
108
}
109

110
// eslint-disable-next-line default-param-last
111
function processPath(pattern, config, rootDir = process.cwd(), explicitConfigPath) {
112
  const ignoreFilter = ignore({ allowRelativePaths: true }).add(config?.excludedFiles || [])
113
  const allFiles = globSync(pattern, { nodir: true })
114
  const files = ignoreFilter.filter(allFiles)
115

116
  const useCache = config?.cache
117
  const defaultCachePath = path.join('node_modules', '.cache', 'solhint', '.solhintcache.json')
118
  const cachePath = config?.cacheLocation || defaultCachePath
119

120
  const cacheData = useCache ? readCache(cachePath) : {}
121
  const updatedCache = { ...cacheData }
122

123
  const reports = []
124

125
  for (const file of files) {
126
    const fileConfig =
127
      config !== undefined ? config : loadConfigForFile(file, rootDir, explicitConfigPath)
128

129
    const { report } = processAndCache(file, fileConfig, {
130
      cacheData,
131
      updatedCache,
132
    })
133

134
    reports.push(report)
135
  }
136

137
  if (useCache) {
138
    fs.mkdirSync(path.dirname(cachePath), { recursive: true }) // make sure the directory exists
139
    writeCache(cachePath, updatedCache)
140
  }
141

142
  return reports
143
}
144

145
module.exports = { processPath, processFile, processStr }
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