Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write a single minimatch glob that matches all js files not in a directory

I have a situation where I need a single glob pattern (that uses minimatch) to match all JavaScript files which are not in a certain directory. Unfortunately, I'm using another tool that doesn't expose any options (like an ignore glob), so it has to be a single glob to do the job.

Here's what I have so far

screenshot of globtester

Example input (it should not match the top, but it should match the bottom):

docs/foo/thing.js
docs/thing.js
client/docs/foo/thing.js
client/docs/thing.js

src/foo/thing.js
src/thing.js
docs-src/foo/thing.js
docs-src/thing.js
client/docs-src/foo/thing.js
client/docs-src/thing.js

And here's what I have for the glob pattern so far:

**/!(docs)/*.js

With that I'm matching docs/foo/thing.js and client/docs/foo/thing.js and not matching docs-src/thing.js or client/docs-src/thing.js. If I switch my glob to **/!(docs)/**/*.js then I can match client/docs-src/thing.js, but I also match client/docs/thing.js.

I'm not certain this is possible so I may need to find another solution for my problem :-/

like image 555
kentcdodds Avatar asked Jan 19 '18 21:01

kentcdodds


2 Answers

I think you may be running into a limitation of minimatch (or any implementation of fnmatch(3)) and globstar. It's perhaps worth noting that no C implementation of fnmatch that I'm aware of actually does implement globstar, but since fnmatch impls (including minimatch) serve the interests of their globbers, this may vary.

The glob that you think should work actually does work, when used as a glob.

$ find . -type f
./docs/foo/thing.js
./docs/thing.js
./docs/nope.txt
./docs-src/foo/thing.js
./docs-src/thing.js
./x.sh
./client/docs/foo/thing.js
./client/docs/thing.js
./client/docs/nope.txt
./client/docs-src/foo/thing.js
./client/docs-src/thing.js
./client/docs-src/nope.txt
./client/nope.txt
./src/foo/thing.js
./src/thing.js

$ for i in ./!(docs)/**/*.js; do echo $i; done
./client/docs-src/foo/thing.js
./client/docs-src/thing.js
./client/docs/foo/thing.js
./client/docs/thing.js
./docs-src/foo/thing.js
./docs-src/thing.js
./src/foo/thing.js
./src/thing.js

$ node -p 'require("glob").sync("./!(docs)/**/*.js")'
[ './client/docs-src/foo/thing.js',
  './client/docs-src/thing.js',
  './client/docs/foo/thing.js',
  './client/docs/thing.js',
  './docs-src/foo/thing.js',
  './docs-src/thing.js',
  './src/foo/thing.js',
  './src/thing.js' ]

EDIT: Oh, I see, you want to only match things at any folder depth that do not have any docs path parts anywhere in the path. No, that is not possible to do in a way that supports arbitrary depth, as a glob or a minimatch pattern. You'll have to use excludes, or construct a glob that looks like: {!(docs),!(docs)/!(docs),!(docs)/!(docs)/!(docs),!(docs)/!(docs)/!(docs)/!(docs)}/*.js

Otherwise, a path like x/docs/y/z.js will match against **/!(docs)/**/*.js by saying that the first ** matches nothing, the !(docs) matches against x, the next ** matches against docs/y and then *.js matches against z.js.

like image 69
isaacs Avatar answered Oct 07 '22 13:10

isaacs


I got closer, with the following:

**/?(docs*[a-zA-Z0-9]|!(docs))/*.js

globtester

Still trying to get it to work for arbitrary depth.

like image 26
Apidcloud Avatar answered Oct 07 '22 14:10

Apidcloud