There is a lot of detail missing from your question, like your :help 'path'
, your :help 'include'
, your :help 'includeexpr'
, etc. but also a lot missing in Vim itself. Let's see what we can do.
This is your file structure as I understand it:
.
├── main.js
└── src
└── components
├── A.js
└── B.js
Out of the box, Vim knows how to find "defines" in a JavaScript file but it doesn't know how to find "includes". That is because, when I updated the default ftplugin for JavaScript a couple years ago, a working &include
was not part of the update. My reasoning was that, while it is relatively easy to come up with a working one, writing a working &includeexpr
is a lot more involved and I felt that having one without the other didn't make much sense.
The final nail in the coffin is the long standing issue with Vim itself that you can't have both a &define
and an &include
match on the same line, which is essentially the scenario for:
export { A } from './B';
where you import and export A
in one statement. The default &define
as I wrote it doesn't support that notation because it simply can't work.
So, without any configuration, Vim is incapable of doing anything with the import
line in main.js
or the export
line in A.js
.
Bummer.
Now, here is the &include
I've had in my config for many years, which would have been part of that update:
" in after/ftplugin/javascript.vim
setlocal include=^\\s*[^\/]\\+\\(from\\\|require(\\)\\s*['\"]\\ze
If we open main.js
in a clean Vim and set the option globally:
$ vim --clean main.js
:set include=^\\s*[^\/]\\+\\(from\\\|require(\\)\\s*['\"]\\ze
we can check with :help :checkpath
that the include is found but that the file is not, which is not surprising given the provided path:
:checkpath
--- Included files not found in path ---
_components/A
The include is also found in A.js
:
--- Included files in path ---
/Users/romainl/Desktop/test/src/components/./B.js
Now, that we can find includes, we must deal with that funky file name in main.js
. :help 'suffixesadd'
is correctly set out of the box so Vim will find A.js
if we give it A
, but the _
alias is weird and non-standard. There are two ways to deal with it.
Add src
to your &path
and get rid of the _
in the filename:
:set path+=src
:set includeexpr=substitute(v:fname,'^_','','')
:checkpath!
--- Included files in path ---
src/components/A.js
src/components/A.js -->
src/components/./B.js
This is for the simplest case. In reality you would have to collect all the current aliases in a list and do that for each of them.
Substitute the _
in the filename with the corresponding path.
:set includeexpr=substitute(v:fname,'^_','src/','')
:checkpath!
--- Included files in path ---
src/components/A.js
src/components/A.js -->
src/components/./B.js
Again, this is for the simplest case.
At this point, you should be able to do [D
on A
in main.js
and get:

--- EDIT ---
So, now that we have your exact file structure, the extra index.js
adds a bit of complexity because A
and B
are considered by Vim as legitimate targets and their index.js
is never searched.
The fix is to add /index.js
(or /index
, thanks to &suffixesadd
) to the final file name. If you are confident that there will always be an index.js
, then it is a simple concatenation or substitution. If you are not (and you shouldn't, IMO), then it will probably require the use of :help glob()
to match either A.js
or A/index.js
given A
.
Something like:
glob(substitute('_components/A','^_','src/','')..'/{index.js,}')
which, in this case, should return:
src/components/A/index.js
src/components/A
with the first one being the most likely candidate.
includeexpr
and maybe à simplified version of the file structure.