Fast, minimal glob matcher for node.js with complete Bash 4.3 wildcard support
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Factory functions for creating reusable matchers and extracting pattern captures. These functions provide advanced pattern matching capabilities for performance optimization and pattern extraction.
Create reusable matcher functions from glob patterns for repeated matching operations.
/**
* Create a reusable matcher function from a glob pattern
* @param {String} pattern - Glob pattern to create matcher function for
* @param {Object} options - Optional configuration for pattern compilation
* @returns {Function} Matcher function that takes string and returns boolean
*/
nanomatch.matcher(pattern, options);
/**
* Matcher function signature returned by nanomatch.matcher()
* @param {String} str - String to test against the compiled pattern
* @returns {Boolean} True if string matches the pattern, false otherwise
*/
type MatcherFunction = (str: string) => boolean;Usage Examples:
const nanomatch = require('nanomatch');
// Create reusable matchers for performance
const isJavaScript = nanomatch.matcher('*.js');
const isComponent = nanomatch.matcher('*.component.*');
const isInSrc = nanomatch.matcher('src/**');
// Use matchers repeatedly
console.log(isJavaScript('app.js')); //=> true
console.log(isJavaScript('styles.css')); //=> false
console.log(isComponent('user.component.ts')); //=> true
console.log(isInSrc('src/app/main.ts')); //=> true
// Matcher with options
const isCaseInsensitiveJS = nanomatch.matcher('*.js', { nocase: true });
console.log(isCaseInsensitiveJS('App.JS')); //=> true
// Complex patterns
const isTestFile = nanomatch.matcher('**/*.{test,spec}.{js,ts}');
console.log(isTestFile('src/utils.test.js')); //=> true
console.log(isTestFile('lib/component.spec.ts')); //=> true
// Negation patterns in matchers
const isNotNodeModules = nanomatch.matcher('!**/node_modules/**');
console.log(isNotNodeModules('src/app.js')); //=> true
console.log(isNotNodeModules('node_modules/lib/dep.js')); //=> false
// Using matchers with arrays
const files = ['app.js', 'styles.css', 'component.ts', 'test.spec.js'];
const jsFiles = files.filter(isJavaScript);
console.log(jsFiles); //=> ['app.js']
// Benchmark performance benefit
const pattern = '**/*.{js,ts,jsx,tsx}';
const filesToTest = [/* thousands of file paths */];
// Slow: recompiling pattern each time
filesToTest.forEach(file => {
if (nanomatch.isMatch(file, pattern)) {
// process file
}
});
// Fast: compile once, use many times
const isSourceFile = nanomatch.matcher(pattern);
filesToTest.forEach(file => {
if (isSourceFile(file)) {
// process file
}
});Extract captured groups from glob pattern matches for parsing and information extraction.
/**
* Extract captures from a pattern match
* @param {String} pattern - Glob pattern with capture groups (parentheses)
* @param {String} string - String to match and extract captures from
* @param {Object} options - Optional configuration for pattern matching
* @returns {Array|null} Array of captured groups or null if no match
*/
nanomatch.capture(pattern, string, options);Usage Examples:
const nanomatch = require('nanomatch');
// Basic capture with wildcards
console.log(nanomatch.capture('*.js', 'app.js'));
//=> ['app'] (captures the * part)
console.log(nanomatch.capture('*.js', 'styles.css'));
//=> null (no match)
// Multiple captures
console.log(nanomatch.capture('src/*/*.js', 'src/components/button.js'));
//=> ['components', 'button'] (captures both * parts)
// Directory and filename parsing
const pathPattern = '**/(*).(*).js';
console.log(nanomatch.capture(pathPattern, 'src/components/user.component.js'));
//=> ['user', 'component'] (captures name and type)
// Version string parsing
const versionPattern = 'v(*).(*).(*)-*';
console.log(nanomatch.capture(versionPattern, 'v1.2.3-beta'));
//=> ['1', '2', '3'] (captures major, minor, patch)
// URL-like pattern parsing
const routePattern = '/api/(*)/(*)/';
console.log(nanomatch.capture(routePattern, '/api/users/123/'));
//=> ['users', '123'] (captures resource and id)
// No captures in pattern
console.log(nanomatch.capture('*.js', 'app.js'));
//=> [] (pattern has no explicit capture groups)
// Empty captures
console.log(nanomatch.capture('prefix-*-suffix', 'prefix--suffix'));
//=> [''] (captures empty string between dashes)
// Nested directory capture
const nestedPattern = 'src/(**)/(*).(*)';
console.log(nanomatch.capture(nestedPattern, 'src/components/ui/button.component.tsx'));
//=> ['components/ui', 'button', 'component'] (captures path, name, type)const nanomatch = require('nanomatch');
// Scenario: Processing large file lists with multiple pattern checks
const files = [
'src/app.js', 'src/utils.js', 'lib/helper.js',
'test/app.test.js', 'test/utils.spec.js',
'docs/readme.md', 'config/webpack.js'
];
// Inefficient: recompiling patterns
function processFilesSlowly(files) {
const results = { source: [], tests: [], configs: [] };
files.forEach(file => {
if (nanomatch.isMatch(file, 'src/**/*.js')) {
results.source.push(file);
}
if (nanomatch.isMatch(file, '**/*.{test,spec}.js')) {
results.tests.push(file);
}
if (nanomatch.isMatch(file, 'config/**')) {
results.configs.push(file);
}
});
return results;
}
// Efficient: pre-compiled matchers
function processFilesQuickly(files) {
const isSource = nanomatch.matcher('src/**/*.js');
const isTest = nanomatch.matcher('**/*.{test,spec}.js');
const isConfig = nanomatch.matcher('config/**');
const results = { source: [], tests: [], configs: [] };
files.forEach(file => {
if (isSource(file)) results.source.push(file);
if (isTest(file)) results.tests.push(file);
if (isConfig(file)) results.configs.push(file);
});
return results;
}const nanomatch = require('nanomatch');
// File path parsing
function parseFilePath(filePath) {
// Extract directory, name, and extension
const captures = nanomatch.capture('(**)/(*).(*)' , filePath);
if (!captures) return null;
const [directory, name, extension] = captures;
return { directory, name, extension };
}
console.log(parseFilePath('src/components/button.tsx'));
//=> { directory: 'src/components', name: 'button', extension: 'tsx' }
// API route parsing
function parseApiRoute(route) {
const captures = nanomatch.capture('/api/v(*)/(*)/(*)', route);
if (!captures) return null;
const [version, resource, id] = captures;
return { version, resource, id: id || null };
}
console.log(parseApiRoute('/api/v1/users/123'));
//=> { version: '1', resource: 'users', id: '123' }
console.log(parseApiRoute('/api/v2/posts/'));
//=> { version: '2', resource: 'posts', id: '' }
// Configuration key parsing
function parseConfigKey(key) {
const captures = nanomatch.capture('(*).(*).(*)', key);
if (!captures) return null;
const [service, environment, setting] = captures;
return { service, environment, setting };
}
console.log(parseConfigKey('database.production.host'));
//=> { service: 'database', environment: 'production', setting: 'host' }
// Build artifact parsing
function parseBuildArtifact(filename) {
const captures = nanomatch.capture('(*)-v(*)-(*)-(*).*', filename);
if (!captures) return null;
const [name, version, platform, arch] = captures;
return { name, version, platform, arch };
}
console.log(parseBuildArtifact('myapp-v1.2.3-linux-x64.tar.gz'));
//=> { name: 'myapp', version: '1.2.3', platform: 'linux', arch: 'x64' }const nanomatch = require('nanomatch');
// Matchers have additional properties for debugging
const matcher = nanomatch.matcher('src/**/*.{js,ts}');
// Access compilation result (non-enumerable property)
console.log(matcher.result);
//=> { output: '...', ast: {...}, ... } (compilation details)
// Matcher functions are memoized
const matcher1 = nanomatch.matcher('*.js');
const matcher2 = nanomatch.matcher('*.js');
console.log(matcher1 === matcher2); //=> true (same cached function)
// Different options create different matchers
const matcher3 = nanomatch.matcher('*.js', { nocase: true });
console.log(matcher1 === matcher3); //=> false (different options)const nanomatch = require('nanomatch');
// Invalid pattern types
try {
nanomatch.matcher(123);
} catch (error) {
console.log(error.message);
//=> 'expected pattern to be an array, string or regex'
}
try {
nanomatch.matcher(null);
} catch (error) {
console.log(error.message);
//=> 'expected pattern to be an array, string or regex'
}
// Empty patterns
const emptyMatcher = nanomatch.matcher('');
console.log(emptyMatcher('anything')); //=> false
// Capture with non-matching patterns
console.log(nanomatch.capture('*.js', 'style.css')); //=> null
// Empty string inputs
console.log(nanomatch.capture('*', '')); //=> [''] (captures empty string)
console.log(nanomatch.capture('', 'test')); //=> null (empty pattern doesn't match)isMatch() may be more efficient than creating a matcher