All patches and comments are welcome. Please squash your changes to logical
commits before using git-format-patch and git-send-email to
patches@git.madduck.net.
If you'd read over the Git project's submission guidelines and adhered to them,
I'd be especially grateful.
1 " Author: w0rp <devw0rp@gmail.com>
2 " Description: Functions for working with eslint, for checking or fixing files.
5 \ '.yarn/sdks/eslint/bin/eslint.js',
6 \ 'node_modules/.bin/eslint_d',
7 \ 'node_modules/eslint/bin/eslint.js',
8 \ 'node_modules/.bin/eslint',
10 let s:sep = has('win32') ? '\' : '/'
12 call ale#Set('javascript_eslint_options', '')
13 call ale#Set('javascript_eslint_executable', 'eslint')
14 call ale#Set('javascript_eslint_use_global', get(g:, 'ale_use_global_executables', 0))
15 call ale#Set('javascript_eslint_suppress_eslintignore', 0)
16 call ale#Set('javascript_eslint_suppress_missing_config', 0)
18 function! ale#handlers#eslint#FindConfig(buffer) abort
19 for l:path in ale#path#Upwards(expand('#' . a:buffer . ':p:h'))
22 \ 'eslint.config.mjs',
23 \ 'eslint.config.cjs',
31 let l:config = ale#path#Simplify(join([l:path, l:basename], s:sep))
33 if filereadable(l:config)
39 return ale#path#FindNearestFile(a:buffer, 'package.json')
42 function! ale#handlers#eslint#GetExecutable(buffer) abort
43 return ale#path#FindExecutable(a:buffer, 'javascript_eslint', s:executables)
46 " Given a buffer, return an appropriate working directory for ESLint.
47 function! ale#handlers#eslint#GetCwd(buffer) abort
48 return ale#path#Dirname(ale#handlers#eslint#FindConfig(a:buffer))
51 function! ale#handlers#eslint#GetCommand(buffer) abort
52 let l:executable = ale#handlers#eslint#GetExecutable(a:buffer)
54 let l:options = ale#Var(a:buffer, 'javascript_eslint_options')
56 return ale#node#Executable(a:buffer, l:executable)
57 \ . (!empty(l:options) ? ' ' . l:options : '')
58 \ . ' -f json --stdin --stdin-filename %s'
61 function! s:AddHintsForTypeScriptParsingErrors(output) abort
62 for l:item in a:output
63 let l:item.text = substitute(
65 \ '^\(Parsing error\)',
66 \ '\1 (You may need configure typescript-eslint-parser)',
72 function! s:CheckForBadConfig(buffer, lines) abort
73 let l:config_error_pattern = '\v^ESLint couldn''t find a configuration file'
74 \ . '|^Cannot read config file'
75 \ . '|^.*Configuration for rule .* is invalid'
76 \ . '|^ImportDeclaration should appear'
78 " Look for a message in the first few lines which indicates that
79 " a configuration file couldn't be found.
80 for l:line in a:lines[:10]
81 let l:match = matchlist(l:line, l:config_error_pattern)
84 " Don't show the missing config error if we've disabled it.
85 if ale#Var(a:buffer, 'javascript_eslint_suppress_missing_config')
86 \&& l:match[0] is# 'ESLint couldn''t find a configuration file'
97 function! s:parseJSON(buffer, lines) abort
100 for l:line in a:lines
102 let l:parsed = extend(l:parsed, json_decode(l:line))
107 if type(l:parsed) != v:t_list || empty(l:parsed)
111 let l:errors = l:parsed[0]['messages']
119 for l:error in l:errors
121 \ 'lnum': get(l:error, 'line', 0),
122 \ 'text': get(l:error, 'message', ''),
126 if get(l:error, 'severity', 0) is# 1
130 if has_key(l:error, 'ruleId')
131 let l:code = l:error['ruleId']
133 " Sometimes ESLint returns null here
135 let l:obj.code = l:code
139 if has_key(l:error, 'column')
140 let l:obj.col = l:error['column']
143 if has_key(l:error, 'endColumn')
144 let l:obj.end_col = l:error['endColumn'] - 1
147 if has_key(l:error, 'endLine')
148 let l:obj.end_lnum = l:error['endLine']
151 call add(l:output, l:obj)
157 let s:col_end_patterns = [
158 \ '\vParsing error: Unexpected token (.+) ?',
159 \ '\v''(.+)'' is not defined.',
160 \ '\v%(Unexpected|Redundant use of) [''`](.+)[''`]',
161 \ '\vUnexpected (console) statement',
164 function! s:parseLines(buffer, lines) abort
165 " Matches patterns line the following:
167 " /path/to/some-filename.js:47:14: Missing trailing comma. [Warning/comma-dangle]
168 " /path/to/some-filename.js:56:41: Missing semicolon. [Error/semi]
169 let l:pattern = '^.*:\(\d\+\):\(\d\+\): \(.\+\) \[\(.\+\)\]$'
170 " This second pattern matches lines like the following:
172 " /path/to/some-filename.js:13:3: Parsing error: Unexpected token
173 let l:parsing_pattern = '^.*:\(\d\+\):\(\d\+\): \(.\+\)$'
176 for l:match in ale#util#GetMatches(a:lines, [l:pattern, l:parsing_pattern])
177 let l:text = l:match[3]
180 \ 'lnum': l:match[1] + 0,
181 \ 'col': l:match[2] + 0,
186 " Take the error type from the output if available.
187 let l:split_code = split(l:match[4], '/')
189 if get(l:split_code, 0, '') is# 'Warning'
193 " The code can be something like 'Error/foo/bar', or just 'Error'
194 if !empty(get(l:split_code, 1))
195 let l:obj.code = join(l:split_code[1:], '/')
198 for l:col_match in ale#util#GetMatches(l:text, s:col_end_patterns)
199 let l:obj.end_col = l:obj.col + len(l:col_match[1]) - 1
202 call add(l:output, l:obj)
208 function! s:FilterResult(buffer, obj) abort
209 if ale#Var(a:buffer, 'javascript_eslint_suppress_eslintignore')
210 if a:obj.text =~# '^File ignored'
215 if has_key(a:obj, 'code') && a:obj.code is# 'no-trailing-spaces'
216 \&& !ale#Var(a:buffer, 'warn_about_trailing_whitespace')
223 function! s:HandleESLintOutput(buffer, lines, type) abort
224 if s:CheckForBadConfig(a:buffer, a:lines)
227 \ 'text': 'eslint configuration error (type :ALEDetail for more information)',
228 \ 'detail': join(a:lines, "\n"),
232 if a:lines == ['Could not connect']
235 \ 'text': 'Could not connect to eslint_d. Try updating eslint_d or killing it.',
240 let l:output = s:parseJSON(a:buffer, a:lines)
242 let l:output = s:parseLines(a:buffer, a:lines)
245 call filter(l:output, {idx, obj -> s:FilterResult(a:buffer, obj)})
247 if expand('#' . a:buffer . ':t') =~? '\.tsx\?$'
248 call s:AddHintsForTypeScriptParsingErrors(l:output)
254 function! ale#handlers#eslint#HandleJSON(buffer, lines) abort
255 return s:HandleESLintOutput(a:buffer, a:lines, 'json')
258 function! ale#handlers#eslint#Handle(buffer, lines) abort
259 return s:HandleESLintOutput(a:buffer, a:lines, 'lines')