Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ALE eslint cannot find config #4487

Closed
theahura opened this issue Mar 28, 2023 · 11 comments · Fixed by #4781
Closed

ALE eslint cannot find config #4487

theahura opened this issue Mar 28, 2023 · 11 comments · Fixed by #4781
Labels

Comments

@theahura
Copy link

theahura commented Mar 28, 2023

Information

VIM version
VIM - Vi IMproved 9.0 (2022 Jun 28, compiled May 10 2022 08:40:37)
Included patches: 1-749

Operating System:
Ubuntu 20.04.3 LTS

What went wrong

Apologize if this is not the right place for this! Could be considered a configuration issue, not sure.

I switched from Syntastic to ALE recently and noticed that ALE was not able to find my organization's eslint.config.js file. The organization has a folder structure like so:

foo/
  node_modules/
    eslint/
  packages/
    app1/
    app2/
    app3/
      eslint.config.js

Basically we have a monorepo with multiple apps and hoist shared dependencies (like eslint) to the top level, but individual apps can have their own lint configurations among other things.

ALE says it cannot find the config:

Oops! Something went wrong! :(

ESLint: 8.35.0

ESLint couldn't find a configuration file. To set up a configuration file for this project, please run:

  npm init @eslint/config

ESLint looked for configuration files in /home/user/foo/packages/app3/src and its ancestors. If it found none, it then looked in your home directory.

When I run ALEInfo, I see the following:

(finished - exit code 2) ['/bin/bash', '-c', 'cd ''/home/user/foo'' && ''/home/user/foo/node_modules/eslint/bin/eslint.js''
-f json --stdin --stdin-filename ''/home/user/foo/packages/app3/src/index.js'' < ''/tmp/vGyg5eP/4/index.js''']
<<<OUTPUT STARTS>>>
Oops! Something went wrong! :(
ESLint: 8.35.0
ESLint couldn't find a configuration file. To set up a configuration file f
or this project, please run:
    npm init @eslint/config
ESLint looked for configuration files in /home/user/foo/packages/app3/src and its ancestors. If it found none, it then looked in your home
directory.
If you think you already have a configuration file or if you need more help
, please stop by the ESLint Discord server: https://eslint.org/chat
<<<OUTPUT ENDS>>>

The reason I don't think this is an eslint bug is because I can run eslint just fine in my app package. For what its worth, Syntastic too did not have any issues. And yet, through ALE, eslint is unable to find any config. I also tried running that eslint command in my own bash terminal and it just hangs, not sure whats up with that. If I remove the --stdin --stdin-filename and just run eslint on the file in question, it works fine in bash, but again not in ALE

Would appreciate any help!

:ALEInfo

Expand Current Filetype: javascript Available Linters: ['cspell', 'deno', 'eslint', 'fecs', 'flow', 'flow-language-server', 'jscs', 'jshint', 'standard', 'tsserver', 'xo'] Enabled Linters: ['cspell', 'deno', 'eslint', 'fecs', 'flow', 'flow-language-server', 'jscs', 'jshint', 'standard', 'tsserver', 'xo'] Ignored Linters: [] Suggested Fixers: 'clang-format' - Fix C, C++, C#, CUDA, Java, JavaScript, JSON, ObjectiveC and Protobuf files with clang-format. 'dprint' - Pluggable and configurable code formatting platform 'eslint' - Apply eslint --fix to a file. 'fecs' - Apply fecs format to a file. 'importjs' - automatic imports for javascript 'prettier' - Apply prettier to a file. 'prettier_eslint', 'prettier-eslint' - Apply prettier-eslint to a file. 'prettier_standard', 'prettier-standard' - Apply prettier-standard to a file. 'remove_trailing_lines' - Remove all blank lines at the end of a file. 'standard' - Fix JavaScript files using standard --fix 'trim_whitespace' - Remove all trailing whitespace characters at the end of every line. 'xo' - Fix JavaScript/TypeScript files using xo --fix. Linter Variables:

let g:ale_javascript_eslint_executable = 'eslint'
let g:ale_javascript_eslint_options = ''
let g:ale_javascript_eslint_suppress_eslintignore = 0
let g:ale_javascript_eslint_suppress_missing_config = 0
let g:ale_javascript_eslint_use_global = 0
let g:ale_javascript_fecs_executable = 'fecs'
let g:ale_javascript_fecs_use_global = 0
let g:ale_javascript_flow_executable = 'flow'
let g:ale_javascript_flow_ls_executable = 'flow'
let g:ale_javascript_flow_ls_use_global = 0
let g:ale_javascript_flow_use_global = 0
let g:ale_javascript_flow_use_home_config = 0
let g:ale_javascript_flow_use_respect_pragma = 1
let g:ale_javascript_jscs_executable = 'jscs'
let g:ale_javascript_jscs_use_global = 0
let g:ale_javascript_jshint_executable = 'jshint'
let g:ale_javascript_jshint_use_global = 0
let g:ale_javascript_standard_executable = 'standard'
let g:ale_javascript_standard_options = ''
let g:ale_javascript_standard_use_global = 0
let g:ale_javascript_tsserver_config_path = ''
let g:ale_javascript_tsserver_executable = 'tsserver'
let g:ale_javascript_tsserver_use_global = 0
let g:ale_javascript_xo_executable = 'xo'
let g:ale_javascript_xo_options = ''
let g:ale_javascript_xo_use_global = 0
Global Variables:

let g:ale_cache_executable_check_failures = v:null
let g:ale_change_sign_column_color = 0
let g:ale_command_wrapper = ''
let g:ale_completion_delay = v:null
let g:ale_completion_enabled = 0
let g:ale_completion_max_suggestions = v:null
let g:ale_disable_lsp = 0
let g:ale_echo_cursor = 1
let g:ale_echo_msg_error_str = 'Error'
let g:ale_echo_msg_format = '%code: %%s'
let g:ale_echo_msg_info_str = 'Info'
let g:ale_echo_msg_warning_str = 'Warning'
let g:ale_enabled = 1
let g:ale_fix_on_save = 0
let g:ale_fixers = {}
let g:ale_history_enabled = 1
let g:ale_history_log_output = 1
let g:ale_keep_list_window_open = 0
let g:ale_lint_delay = 200
let g:ale_lint_on_enter = 1
let g:ale_lint_on_filetype_changed = 1
let g:ale_lint_on_insert_leave = 1
let g:ale_lint_on_save = 1
let g:ale_lint_on_text_changed = 'normal'
let g:ale_linter_aliases = {}
let g:ale_linters = {}
let g:ale_linters_explicit = 0
let g:ale_linters_ignore = {}
let g:ale_list_vertical = 0
let g:ale_list_window_size = 10
let g:ale_loclist_msg_format = '%code: %%s'
let g:ale_max_buffer_history_size = 20
let g:ale_max_signs = -1
let g:ale_maximum_file_size = v:null
let g:ale_open_list = 0
let g:ale_pattern_options = v:null
let g:ale_pattern_options_enabled = v:null
let g:ale_root = {}
let g:ale_set_balloons = 0
let g:ale_set_highlights = 1
let g:ale_set_loclist = 1
let g:ale_set_quickfix = 0
let g:ale_set_signs = 1
let g:ale_sign_column_always = 0
let g:ale_sign_error = '>>'
let g:ale_sign_info = '--'
let g:ale_sign_offset = 1000000
let g:ale_sign_style_error = '>>'
let g:ale_sign_style_warning = '--'
let g:ale_sign_warning = '--'
let g:ale_sign_highlight_linenrs = 0
let g:ale_statusline_format = v:null
let g:ale_type_map = {}
let g:ale_use_neovim_diagnostics_api = 0
let g:ale_use_global_executables = v:null
let g:ale_virtualtext_cursor = 'all'
let g:ale_warn_about_trailing_blank_lines = 1
let g:ale_warn_about_trailing_whitespace = 1
LSP Error Messages:

(Errors for flow-language-server)
Failed to find project root, language server won't start.
Failed to find project root, language server won't start.
Command History:

(executable check - failure) cspell
(executable check - failure) deno
(finished - exit code 2) ['/bin/bash', '-c', 'cd ''/home/soot/code/soot/hearth'' && ''/home/soot/code/soot/hearth/node_modules/eslint/bin/eslint.js'' -f json --stdin --stdin-filename ''/home/soot/code/soot/hearth/packages/ui/src/index.js'' < ''/tmp/vGyg5eP/4/index.js''']

<<>>

Oops! Something went wrong! :(

ESLint: 8.35.0

ESLint couldn't find a configuration file. To set up a configuration file for this project, please run:

npm init @eslint/config

ESLint looked for configuration files in /home/soot/code/soot/hearth/packages/ui/src and its ancestors. If it found none, it then looked in your home directory.

If you think you already have a configuration file or if you need more help, please stop by the ESLint Discord server: https://eslint.org/chat

<<>>

(executable check - failure) fecs
(executable check - failure) jscs
(executable check - failure) jshint
(executable check - failure) standard
(started) ['/bin/bash', '-c', '''/home/soot/code/soot/hearth/packages/ui/node_modules/.bin/tsserver''']
(executable check - failure) xo
(executable check - failure) deno

@theahura theahura added the bug label Mar 28, 2023
@pbnj
Copy link
Contributor

pbnj commented Jul 23, 2023

Hi @theahura -

Thank you for the bug report.

I was able to replicate the issue on my end.

I think it is some incompatibility between eslint v8 and v9.

According to eslint docs for v9 [1] (emphasis mine):

This config system is feature complete but not enabled by default. To opt-in, place an eslint.config.js file in the root of your project or set the ESLINT_USE_FLAT_CONFIG environment variable to true.

and...

The ESLint configuration file is named eslint.config.js. It should be placed in the root directory of your project

Moving packages/app3/eslint.config.js to the root of my project resolves the issue.


Edit:

After digging further into eslint docs, I found this. So, in theory, it should detect eslint.config.js in the children sub-directories.

So, I think this issue is with how working directory gets resolved in ale for js/ts projects:

  • for js files https://github.com/dense-analysis/ale/blob/master/ale_linters/javascript/eslint.vim#L8
  • for ts files https://github.com/dense-analysis/ale/blob/master/ale_linters/typescript/eslint.vim#L7
  • GetCwd definition:
    function! ale#handlers#eslint#GetCwd(buffer) abort
    " ESLint 6 loads plugins/configs/parsers from the project root
    " By default, the project root is simply the CWD of the running process.
    " https://github.com/eslint/rfcs/blob/master/designs/2018-simplified-package-loading/README.md
    " https://github.com/dense-analysis/ale/issues/2787
    "
    " If eslint is installed in a directory which contains the buffer, assume
    " it is the ESLint project root. Otherwise, use nearest node_modules.
    " Note: If node_modules not present yet, can't load local deps anyway.
    let l:executable = ale#path#FindNearestExecutable(a:buffer, s:executables)
    if !empty(l:executable)
    let l:modules_index = strridx(l:executable, 'node_modules')
    let l:modules_root = l:modules_index > -1 ? l:executable[0:l:modules_index - 2] : ''
    let l:sdks_index = strridx(l:executable, ale#path#Simplify('.yarn/sdks'))
    let l:sdks_root = l:sdks_index > -1 ? l:executable[0:l:sdks_index - 2] : ''
    else
    let l:modules_dir = ale#path#FindNearestDirectory(a:buffer, 'node_modules')
    let l:modules_root = !empty(l:modules_dir) ? fnamemodify(l:modules_dir, ':h:h') : ''
    let l:sdks_dir = ale#path#FindNearestDirectory(a:buffer, ale#path#Simplify('.yarn/sdks'))
    let l:sdks_root = !empty(l:sdks_dir) ? fnamemodify(l:sdks_dir, ':h:h:h') : ''
    endif
    return strlen(l:modules_root) > strlen(l:sdks_root) ? l:modules_root : l:sdks_root
    endfunction

In short, the GetCwd should probably look for config file markers, instead of node_modules directories.

@pbnj
Copy link
Contributor

pbnj commented Jul 26, 2023

@theahura - by any chance, have you read the docs about nested eslint configurations?

I was able to resolve the issue on my end without ale code changes, by updating the eslint config file like:

module.exports = {
    // redacted for brevity

    "extends": [
        "../../eslint.config.js" // to extend the root eslint.config.js
    ],

    // redacted for brevit
}

@theahura
Copy link
Author

theahura commented Jul 31, 2023

I've read those docs before, but I don't follow what the suggestion is. Just to confirm, is the suggestion to have an eslint config at root AND an extension in each app folder?
So something like:

foo/
  eslint.config.js
  node_modules/eslint
  packages/
    app1
    app2
    app3/
      eslint.config.js

?
Previously for brevity I left out a bit of how our folders are structured, but it may be worth clarifying here. Our configs are also part of packages, so our actual folder structure looks like this:

foo/
  configs/
     root-estlint-config.js
  packages/
     app1/
        eslint.config.js
     app2/
        eslint.config.js

Each package/eslint.config.js looks something like this (pulling an actual examples)

const configs = require('@foo/configs.soot-style');

module.exports = [
  ...configs.eslint,
  {
    files: ['codegen/plugins/**/*.js'],
    rules: {
      '@typescript-eslint/no-var-requires': 'off',
    },
  },
];

so we actually are inlining the full root eslint config here

@bodymindarts
Copy link

bodymindarts commented Oct 19, 2023

Just leaving a comment here because I have run into the exact same issue with a similar setup.

If you have an idea how to fix this let me know your thoughts - perhaps I can have a go at implementing.

@bodymindarts
Copy link

Something like this perhaps #4637

@bodymindarts
Copy link

The Vader tests are a rabbit whole I can't really figure out ATM (happy for any guidance).

In the meantime I have added the following hack to $MYVIMRC - incase someone else finds this thread it is working for my mono repo setup:

augroup TypeScriptOverrides
    autocmd!
    autocmd BufRead,BufNewFile *.ts,*.tsx,*.js,*.json call s:DefineEslintFunction()
augroup END

function! s:DefineEslintFunction()
  function! ale#handlers#eslint#GetCwd(buffer) abort
      " Obtain the path to the ESLint configuration
      let l:config_path = ale#handlers#eslint#FindConfig(a:buffer)
  
      " Extract the directory from the config path
      let l:config_dir = fnamemodify(l:config_path, ':h')
  
      " Return the directory as the cwd
      return l:config_dir
  endfunction
endfunction

@sukima
Copy link

sukima commented Oct 25, 2023

I'm having the same problem. PLEASE provide an escape hatch / option override for evaluating cwd!!

Please! Please! Please!

@sukima
Copy link

sukima commented Oct 26, 2023

For context, the reason the current current directory logic does not work for my specific monorepo is that our project uses Yarn workspaces. The system does not add monorepo wide executables to each workspace's node_modules but instead they go in the repos top node_modules thus the logic to find the executable walks up the monorepo tree till it finds the monorepo root. But eslint needs the current working directory to capture workspace eslint configs.

@bodymindarts workaround does the job for my use case. It is unfortunate it needs such a sledge hammer approach though.

@sukima
Copy link

sukima commented Oct 26, 2023

I just discovered that even in a yarn workspace running from the top level will still process configs down the chain as it resolves them from the --stdin-filename and walks backwards. This might be a quirk of eslint which makes running from root OK. Maybe the workaround is unneeded in this specific use case?

@theahura
Copy link
Author

theahura commented Dec 1, 2023

I seem to have run into this issue again, unexpectedly on a new machine. I have no idea how I fixed the old machine, whoops. @bodymindarts any chance of reviving that PR that you were working on earlier?

I am getting a slightly different error now than the one i was getting when I first filed the report -- namely, Parsing error (You may need configure typescript-eslint-parser) -- but the rest of the symptoms are the same. When I run eslint in the right folder, I get no errors; when I run it from wherever ale is trying to run it (i.e. the eslint node_module folder), all the errors

@theahura
Copy link
Author

theahura commented Dec 1, 2023

Got it! I had a rogue .eslintrc file in my home directory, deleting that fixed it for me (both the original time and this time)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
4 participants