sindresorhus / del

Delete files and directories
MIT License
1.32k stars 66 forks source link

Exclude pattern hack #51

Open duzun opened 8 years ago

duzun commented 8 years ago

I've wrote a function to simplify writing exclude pattern, due to the fact that ** matches all children and the parent.

del.sync(extendNegGlobs(['public/assets/**', '!public/assets/goat.png']));
// is equivalent with 
del.sync(['public/assets/**', '!public/assets/goat.png', '!public/assets', '!public']);

Here is my function:

function extendNegGlobs(arr) {
    var ext = [];
    var negs = {};
    arr.forEach((g, i) => {
        if ( g.slice(0,1) == '!' ) {
            negs[g] = i;
        }
    });
    Object.keys(negs).forEach((g) => {
        var d;
        if ( g.slice(-1) == '*' ) do {
            g = path.dirname(d = g);
            if ( g == '!' || g == '.' || g == d ) break;
            if ( !(g in negs) ) {
                negs[g] = ext.push(g);
            }
        }
        while ( true );
    });
    return arr.concat(ext);
}

I don't pretend this is a complete/nice solution, but it might be a starting point to finding one!

Comments ?

ericmdantas commented 8 years ago

Worked for me, thanks!

ericmdantas commented 8 years ago

Why don't you open a PR where you'd introduce a new option to do that under the hood?

duzun commented 8 years ago

Here is a note from readme.md:

Beware

The glob pattern ** matches all children and the parent.

So this won't work:

del.sync(['public/assets/**', '!public/assets/goat.png']);

You have to explicitly ignore the parent directories too:

del.sync(['public/assets/**', '!public/assets', '!public/assets/goat.png']);

Suggestions on how to improve this welcome!

So I just made a suggestion. This could be implemented internally and I think it would be ok to have this as the default behavior.

But no one has left a comment since :-(

ericmdantas commented 8 years ago

Well, we could always create a "plugin" that simply returns that function of yours and use it with del, just like in your example.

schnittstabil commented 8 years ago

To be honest, I haven't commented yet, because I can't imagine how we could extend that idea to solve all variants of the problem in a maintainable way.

Some notes about the syntax from node-glob:

  • [...] Matches a range of characters, similar to a RegExp range. If the first character of the range is ! or ^ then it matches any character not in the range.
  • !(pattern|pattern|pattern) Matches anything that does not match any of the patterns provided.
  • ?(pattern|pattern|pattern) Matches zero or one occurrence of the patterns provided.
  • +(pattern|pattern|pattern) Matches one or more occurrences of the patterns provided. *(a|b|c) Matches zero or more occurrences of the patterns provided @(pattern|pat*|pat?erN) Matches exactly one of the patterns provided

Thus there are some expressions to deal with:

duzun commented 8 years ago

I think a call like del.sync(['public/assets/**', '!public/assets/goat.png']); should remove all files inside public/assets/ except public/assets/goat.png file. The intention is clear in the pattern, but the behavior is not doing what "is obvious".

The intention of my hack was to make the "obvious" thing happen, but I agree it is not universal and only works with simple patterns.

The universal solution would be to make del not remove folders that contain at least one file that matches an exclude pattern.

The issue lays in the implementation: del gets a files & folders list using globby and then removes these files and folders. But at this point exclude patterns are already applied, and the list might contains folders of excluded files, thus folders get removed together with their files.

I think the algo should be changed if we want to solve this issue. If my understanding of glob is right, there is only one pattern that excludes files (and folders) from previous patterns, and it starts with "!". So del should not remove any folder that contains at least one file that matches any exclude pattern (in any subfolder).

ibratoev commented 7 years ago

I find the current behavior confusing as well. What about making sure that after some files are whitelisted, their parent directories would not be deleted as well? Makes sense for a feature suggestion?

tantv commented 7 years ago

Hi @duzun,

I have the problems with the nested folder and how can apply your solution for this problem or we have another solution for this.

Structure:

Test folder ---- A folder ---- B folder ---- Resource folder ---- ---- assets folder ---- ---- ---- css folder ---- ---- ---- tools folder ---- ---- ---- ---- node_modules ---- ---- ---- ---- bower_components

How can I delete the Test folder without delete the node & bower folder.

My setting doesn't work

gulp.task('clean', function () {
        return del([
            'Test/**/*',
            'Test/'
            '!Test/Resource/',
            '!Test/Resource/**',
            '!Test/Resource/assets/',
            '!Test/Resource/assets/**',
            '!Test/Resource/assets/tools/',
            '!Test/Resource/assets/tools/**'
        ], {force: true});
});

Thanks,