Search code examples
javascriptnode.jsgruntjsglobnode-glob

globbing - Correct way to match some files extensions but not others


I wrote the snippet below as part of grunt-aws-s3 task.

var weekCache = 'css|js';
var yearCache = 'jpg|jpeg|png|gif|ico|eot|otf|ttf|woff|woff2';
var prodFiles = [
    {expand: true, cwd: 'public', src: ['**/*.!('+weekCache+'|'+yearCache+')'], dest: '/'},
    {expand: true, cwd: 'public', src: ['**/*.@('+weekCache+')'], dest: '/', stream: true, params: {CacheControl: 'max-age=604800, public'}}, // enable stream to allow large files
    {expand: true, cwd: 'public', src: ['**/*.@('+yearCache+')'], dest: '/', stream: true, params: {CacheControl: 'max-age=31536000, public'}},
];

The idea is to have three different matches inside prodFiles variable:

  1. Match all files except the ones matched by yearCache and weekCache
  2. Match all files with extensions carried in weekCache
  3. Match all files with extensions carried in yearCache

It works more or less, I have 63 files inside public directory and its subdirectories. However those rules match 72 files, it is clear some files are matched twice at least.

What is wrong with my globbing?

EDIT:

Testing with node-glob-all shown that error is in the first pattern:

$ glob-all '**/*.!(css|js)'
assets/css/style-nuvue6sithwirecbhvw3dkaobiojqvtadsnhguwi7k04xklybw5djl1smadp.min.css
assets/images/favicon.ico
assets/js/jquery.fancybox.js
assets/js/jquery.fancybox-thumbs.js

I was expecting this pattern to return everything but exclude *.js and *.css. Testing against the other two rules returned correct files.

$ glob-all '**/*.@(css|js)'
assets/css/style-nuvue6sithwirecbhvw3dkaobiojqvtadsnhguwi7k04xklybw5djl1smadp.min.css
assets/js/jquery.fancybox.js
assets/js/jquery.fancybox-thumbs.js

So, the question is how to correctly negate the pattern '**/*.!(css|js)'?


Solution

  • I believe your problem is that your filenames contain multiple dots (.).

    Your pattern specifies to find all file names that have some number of non-dot characters followed by a dot character followed by any characters other than "css" or "js".

    Let's take, for example, the filename "jquery.fancybox.js". With this file name we find there are some non-dot characters, "jquery", followed by a dot, ".", followed by characters that are not "css" and not "js", "fancybox.js". This file name matches the pattern and is thus returned.

    Had the above file name been "fancybox.js", it would have matched some non-dot characters, "fancybox", followed by a dot, ".", but not followed by characters that are not "css" and not "js". Thus this file name would fail the pattern match and would not be returned.

    What we want to do is to specify in our negation pattern that there may be more non-dot characters before our ".css" or ".js" extension. We can do this with the following pattern:

    '**/*.!(*(*.)js|*(*.)css)'