Skip to content

Exclude Flag Pattern Matching Limited to Filename Only #78

@andrejsoucek

Description

@andrejsoucek

Issue description by Claude

Summary

The --exclude flag in node-prune only matches against the base filename/directory name (info.Name()) rather than the full path, making it impossible to exclude files or directories within specific node modules.

Current Behavior

When using the --exclude flag with glob patterns, the pattern is only matched against the filename or directory name, not the full path relative to the target directory.

Source Code Reference:

// prune returns true if the file or dir should be pruned.
func (p *Pruner) prune(path string, info os.FileInfo) bool {
    // exceptions
    for _, glob := range p.excepts {
        matched, _ := filepath.Match(glob, info.Name())  // <-- Only matches filename
        if matched {
            return false
        }
    }
    // ... rest of function
}

Expected Behavior

The exclude patterns should be able to match against the full relative path, allowing for more granular control over what gets excluded.

Use Case Example

When trying to preserve image assets in a specific package while still allowing pruning of images elsewhere:

# Current (doesn't work as expected):
node-prune --exclude="@my-package/*/images" ./node_modules

# What we want to achieve:
# - Exclude images directory in @my-package
# - Still prune images directories in other packages

Problem Scenarios

Scenario 1: Package-specific exclusions

# Want to exclude: node_modules/@stapic-libs/document-generation/images
# Current pattern: --exclude="@stapic-libs/document-generation/*/images"
# Result: Doesn't work because only "images" is matched against the pattern

Scenario 2: Path-specific exclusions

# Want to exclude: node_modules/some-package/dist/assets
# Current pattern: --exclude="some-package/dist/assets" 
# Result: Doesn't work because only "assets" is matched

Current Workaround

The only current workaround is to exclude the entire filename/directory name globally:

node-prune --exclude="images"  # Excludes ALL directories named "images"

This is too broad and prevents legitimate pruning in other locations.

Proposed Solution

Modify the exception matching logic to support both filename patterns and path-based patterns:

// prune returns true if the file or dir should be pruned.
func (p *Pruner) prune(path string, info os.FileInfo) bool {
    // Get relative path from the base directory
    relPath, _ := filepath.Rel(p.dir, path)
    
    // exceptions
    for _, glob := range p.excepts {
        // Try matching against filename first (backward compatibility)
        if matched, _ := filepath.Match(glob, info.Name()); matched {
            return false
        }
        
        // Try matching against relative path for more specific patterns
        if matched, _ := filepath.Match(glob, relPath); matched {
            return false
        }
        
        // Support Unix-style path separators in patterns on Windows
        normalizedPath := filepath.ToSlash(relPath)
        if matched, _ := filepath.Match(glob, normalizedPath); matched {
            return false
        }
    }
    // ... rest of function
}

Alternative Solution

Add a separate --exclude-path flag that specifically matches against full paths, keeping the current --exclude behavior for backward compatibility.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions