[6a3a178] | 1 | 'use strict';
|
---|
| 2 |
|
---|
| 3 | const path = require('path');
|
---|
| 4 | const scan = require('./scan');
|
---|
| 5 | const parse = require('./parse');
|
---|
| 6 | const utils = require('./utils');
|
---|
| 7 | const constants = require('./constants');
|
---|
| 8 | const isObject = val => val && typeof val === 'object' && !Array.isArray(val);
|
---|
| 9 |
|
---|
| 10 | /**
|
---|
| 11 | * Creates a matcher function from one or more glob patterns. The
|
---|
| 12 | * returned function takes a string to match as its first argument,
|
---|
| 13 | * and returns true if the string is a match. The returned matcher
|
---|
| 14 | * function also takes a boolean as the second argument that, when true,
|
---|
| 15 | * returns an object with additional information.
|
---|
| 16 | *
|
---|
| 17 | * ```js
|
---|
| 18 | * const picomatch = require('picomatch');
|
---|
| 19 | * // picomatch(glob[, options]);
|
---|
| 20 | *
|
---|
| 21 | * const isMatch = picomatch('*.!(*a)');
|
---|
| 22 | * console.log(isMatch('a.a')); //=> false
|
---|
| 23 | * console.log(isMatch('a.b')); //=> true
|
---|
| 24 | * ```
|
---|
| 25 | * @name picomatch
|
---|
| 26 | * @param {String|Array} `globs` One or more glob patterns.
|
---|
| 27 | * @param {Object=} `options`
|
---|
| 28 | * @return {Function=} Returns a matcher function.
|
---|
| 29 | * @api public
|
---|
| 30 | */
|
---|
| 31 |
|
---|
| 32 | const picomatch = (glob, options, returnState = false) => {
|
---|
| 33 | if (Array.isArray(glob)) {
|
---|
| 34 | const fns = glob.map(input => picomatch(input, options, returnState));
|
---|
| 35 | const arrayMatcher = str => {
|
---|
| 36 | for (const isMatch of fns) {
|
---|
| 37 | const state = isMatch(str);
|
---|
| 38 | if (state) return state;
|
---|
| 39 | }
|
---|
| 40 | return false;
|
---|
| 41 | };
|
---|
| 42 | return arrayMatcher;
|
---|
| 43 | }
|
---|
| 44 |
|
---|
| 45 | const isState = isObject(glob) && glob.tokens && glob.input;
|
---|
| 46 |
|
---|
| 47 | if (glob === '' || (typeof glob !== 'string' && !isState)) {
|
---|
| 48 | throw new TypeError('Expected pattern to be a non-empty string');
|
---|
| 49 | }
|
---|
| 50 |
|
---|
| 51 | const opts = options || {};
|
---|
| 52 | const posix = utils.isWindows(options);
|
---|
| 53 | const regex = isState
|
---|
| 54 | ? picomatch.compileRe(glob, options)
|
---|
| 55 | : picomatch.makeRe(glob, options, false, true);
|
---|
| 56 |
|
---|
| 57 | const state = regex.state;
|
---|
| 58 | delete regex.state;
|
---|
| 59 |
|
---|
| 60 | let isIgnored = () => false;
|
---|
| 61 | if (opts.ignore) {
|
---|
| 62 | const ignoreOpts = { ...options, ignore: null, onMatch: null, onResult: null };
|
---|
| 63 | isIgnored = picomatch(opts.ignore, ignoreOpts, returnState);
|
---|
| 64 | }
|
---|
| 65 |
|
---|
| 66 | const matcher = (input, returnObject = false) => {
|
---|
| 67 | const { isMatch, match, output } = picomatch.test(input, regex, options, { glob, posix });
|
---|
| 68 | const result = { glob, state, regex, posix, input, output, match, isMatch };
|
---|
| 69 |
|
---|
| 70 | if (typeof opts.onResult === 'function') {
|
---|
| 71 | opts.onResult(result);
|
---|
| 72 | }
|
---|
| 73 |
|
---|
| 74 | if (isMatch === false) {
|
---|
| 75 | result.isMatch = false;
|
---|
| 76 | return returnObject ? result : false;
|
---|
| 77 | }
|
---|
| 78 |
|
---|
| 79 | if (isIgnored(input)) {
|
---|
| 80 | if (typeof opts.onIgnore === 'function') {
|
---|
| 81 | opts.onIgnore(result);
|
---|
| 82 | }
|
---|
| 83 | result.isMatch = false;
|
---|
| 84 | return returnObject ? result : false;
|
---|
| 85 | }
|
---|
| 86 |
|
---|
| 87 | if (typeof opts.onMatch === 'function') {
|
---|
| 88 | opts.onMatch(result);
|
---|
| 89 | }
|
---|
| 90 | return returnObject ? result : true;
|
---|
| 91 | };
|
---|
| 92 |
|
---|
| 93 | if (returnState) {
|
---|
| 94 | matcher.state = state;
|
---|
| 95 | }
|
---|
| 96 |
|
---|
| 97 | return matcher;
|
---|
| 98 | };
|
---|
| 99 |
|
---|
| 100 | /**
|
---|
| 101 | * Test `input` with the given `regex`. This is used by the main
|
---|
| 102 | * `picomatch()` function to test the input string.
|
---|
| 103 | *
|
---|
| 104 | * ```js
|
---|
| 105 | * const picomatch = require('picomatch');
|
---|
| 106 | * // picomatch.test(input, regex[, options]);
|
---|
| 107 | *
|
---|
| 108 | * console.log(picomatch.test('foo/bar', /^(?:([^/]*?)\/([^/]*?))$/));
|
---|
| 109 | * // { isMatch: true, match: [ 'foo/', 'foo', 'bar' ], output: 'foo/bar' }
|
---|
| 110 | * ```
|
---|
| 111 | * @param {String} `input` String to test.
|
---|
| 112 | * @param {RegExp} `regex`
|
---|
| 113 | * @return {Object} Returns an object with matching info.
|
---|
| 114 | * @api public
|
---|
| 115 | */
|
---|
| 116 |
|
---|
| 117 | picomatch.test = (input, regex, options, { glob, posix } = {}) => {
|
---|
| 118 | if (typeof input !== 'string') {
|
---|
| 119 | throw new TypeError('Expected input to be a string');
|
---|
| 120 | }
|
---|
| 121 |
|
---|
| 122 | if (input === '') {
|
---|
| 123 | return { isMatch: false, output: '' };
|
---|
| 124 | }
|
---|
| 125 |
|
---|
| 126 | const opts = options || {};
|
---|
| 127 | const format = opts.format || (posix ? utils.toPosixSlashes : null);
|
---|
| 128 | let match = input === glob;
|
---|
| 129 | let output = (match && format) ? format(input) : input;
|
---|
| 130 |
|
---|
| 131 | if (match === false) {
|
---|
| 132 | output = format ? format(input) : input;
|
---|
| 133 | match = output === glob;
|
---|
| 134 | }
|
---|
| 135 |
|
---|
| 136 | if (match === false || opts.capture === true) {
|
---|
| 137 | if (opts.matchBase === true || opts.basename === true) {
|
---|
| 138 | match = picomatch.matchBase(input, regex, options, posix);
|
---|
| 139 | } else {
|
---|
| 140 | match = regex.exec(output);
|
---|
| 141 | }
|
---|
| 142 | }
|
---|
| 143 |
|
---|
| 144 | return { isMatch: Boolean(match), match, output };
|
---|
| 145 | };
|
---|
| 146 |
|
---|
| 147 | /**
|
---|
| 148 | * Match the basename of a filepath.
|
---|
| 149 | *
|
---|
| 150 | * ```js
|
---|
| 151 | * const picomatch = require('picomatch');
|
---|
| 152 | * // picomatch.matchBase(input, glob[, options]);
|
---|
| 153 | * console.log(picomatch.matchBase('foo/bar.js', '*.js'); // true
|
---|
| 154 | * ```
|
---|
| 155 | * @param {String} `input` String to test.
|
---|
| 156 | * @param {RegExp|String} `glob` Glob pattern or regex created by [.makeRe](#makeRe).
|
---|
| 157 | * @return {Boolean}
|
---|
| 158 | * @api public
|
---|
| 159 | */
|
---|
| 160 |
|
---|
| 161 | picomatch.matchBase = (input, glob, options, posix = utils.isWindows(options)) => {
|
---|
| 162 | const regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options);
|
---|
| 163 | return regex.test(path.basename(input));
|
---|
| 164 | };
|
---|
| 165 |
|
---|
| 166 | /**
|
---|
| 167 | * Returns true if **any** of the given glob `patterns` match the specified `string`.
|
---|
| 168 | *
|
---|
| 169 | * ```js
|
---|
| 170 | * const picomatch = require('picomatch');
|
---|
| 171 | * // picomatch.isMatch(string, patterns[, options]);
|
---|
| 172 | *
|
---|
| 173 | * console.log(picomatch.isMatch('a.a', ['b.*', '*.a'])); //=> true
|
---|
| 174 | * console.log(picomatch.isMatch('a.a', 'b.*')); //=> false
|
---|
| 175 | * ```
|
---|
| 176 | * @param {String|Array} str The string to test.
|
---|
| 177 | * @param {String|Array} patterns One or more glob patterns to use for matching.
|
---|
| 178 | * @param {Object} [options] See available [options](#options).
|
---|
| 179 | * @return {Boolean} Returns true if any patterns match `str`
|
---|
| 180 | * @api public
|
---|
| 181 | */
|
---|
| 182 |
|
---|
| 183 | picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str);
|
---|
| 184 |
|
---|
| 185 | /**
|
---|
| 186 | * Parse a glob pattern to create the source string for a regular
|
---|
| 187 | * expression.
|
---|
| 188 | *
|
---|
| 189 | * ```js
|
---|
| 190 | * const picomatch = require('picomatch');
|
---|
| 191 | * const result = picomatch.parse(pattern[, options]);
|
---|
| 192 | * ```
|
---|
| 193 | * @param {String} `pattern`
|
---|
| 194 | * @param {Object} `options`
|
---|
| 195 | * @return {Object} Returns an object with useful properties and output to be used as a regex source string.
|
---|
| 196 | * @api public
|
---|
| 197 | */
|
---|
| 198 |
|
---|
| 199 | picomatch.parse = (pattern, options) => {
|
---|
| 200 | if (Array.isArray(pattern)) return pattern.map(p => picomatch.parse(p, options));
|
---|
| 201 | return parse(pattern, { ...options, fastpaths: false });
|
---|
| 202 | };
|
---|
| 203 |
|
---|
| 204 | /**
|
---|
| 205 | * Scan a glob pattern to separate the pattern into segments.
|
---|
| 206 | *
|
---|
| 207 | * ```js
|
---|
| 208 | * const picomatch = require('picomatch');
|
---|
| 209 | * // picomatch.scan(input[, options]);
|
---|
| 210 | *
|
---|
| 211 | * const result = picomatch.scan('!./foo/*.js');
|
---|
| 212 | * console.log(result);
|
---|
| 213 | * { prefix: '!./',
|
---|
| 214 | * input: '!./foo/*.js',
|
---|
| 215 | * start: 3,
|
---|
| 216 | * base: 'foo',
|
---|
| 217 | * glob: '*.js',
|
---|
| 218 | * isBrace: false,
|
---|
| 219 | * isBracket: false,
|
---|
| 220 | * isGlob: true,
|
---|
| 221 | * isExtglob: false,
|
---|
| 222 | * isGlobstar: false,
|
---|
| 223 | * negated: true }
|
---|
| 224 | * ```
|
---|
| 225 | * @param {String} `input` Glob pattern to scan.
|
---|
| 226 | * @param {Object} `options`
|
---|
| 227 | * @return {Object} Returns an object with
|
---|
| 228 | * @api public
|
---|
| 229 | */
|
---|
| 230 |
|
---|
| 231 | picomatch.scan = (input, options) => scan(input, options);
|
---|
| 232 |
|
---|
| 233 | /**
|
---|
| 234 | * Compile a regular expression from the `state` object returned by the
|
---|
| 235 | * [parse()](#parse) method.
|
---|
| 236 | *
|
---|
| 237 | * @param {Object} `state`
|
---|
| 238 | * @param {Object} `options`
|
---|
| 239 | * @param {Boolean} `returnOutput` Intended for implementors, this argument allows you to return the raw output from the parser.
|
---|
| 240 | * @param {Boolean} `returnState` Adds the state to a `state` property on the returned regex. Useful for implementors and debugging.
|
---|
| 241 | * @return {RegExp}
|
---|
| 242 | * @api public
|
---|
| 243 | */
|
---|
| 244 |
|
---|
| 245 | picomatch.compileRe = (state, options, returnOutput = false, returnState = false) => {
|
---|
| 246 | if (returnOutput === true) {
|
---|
| 247 | return state.output;
|
---|
| 248 | }
|
---|
| 249 |
|
---|
| 250 | const opts = options || {};
|
---|
| 251 | const prepend = opts.contains ? '' : '^';
|
---|
| 252 | const append = opts.contains ? '' : '$';
|
---|
| 253 |
|
---|
| 254 | let source = `${prepend}(?:${state.output})${append}`;
|
---|
| 255 | if (state && state.negated === true) {
|
---|
| 256 | source = `^(?!${source}).*$`;
|
---|
| 257 | }
|
---|
| 258 |
|
---|
| 259 | const regex = picomatch.toRegex(source, options);
|
---|
| 260 | if (returnState === true) {
|
---|
| 261 | regex.state = state;
|
---|
| 262 | }
|
---|
| 263 |
|
---|
| 264 | return regex;
|
---|
| 265 | };
|
---|
| 266 |
|
---|
| 267 | /**
|
---|
| 268 | * Create a regular expression from a parsed glob pattern.
|
---|
| 269 | *
|
---|
| 270 | * ```js
|
---|
| 271 | * const picomatch = require('picomatch');
|
---|
| 272 | * const state = picomatch.parse('*.js');
|
---|
| 273 | * // picomatch.compileRe(state[, options]);
|
---|
| 274 | *
|
---|
| 275 | * console.log(picomatch.compileRe(state));
|
---|
| 276 | * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/
|
---|
| 277 | * ```
|
---|
| 278 | * @param {String} `state` The object returned from the `.parse` method.
|
---|
| 279 | * @param {Object} `options`
|
---|
| 280 | * @param {Boolean} `returnOutput` Implementors may use this argument to return the compiled output, instead of a regular expression. This is not exposed on the options to prevent end-users from mutating the result.
|
---|
| 281 | * @param {Boolean} `returnState` Implementors may use this argument to return the state from the parsed glob with the returned regular expression.
|
---|
| 282 | * @return {RegExp} Returns a regex created from the given pattern.
|
---|
| 283 | * @api public
|
---|
| 284 | */
|
---|
| 285 |
|
---|
| 286 | picomatch.makeRe = (input, options = {}, returnOutput = false, returnState = false) => {
|
---|
| 287 | if (!input || typeof input !== 'string') {
|
---|
| 288 | throw new TypeError('Expected a non-empty string');
|
---|
| 289 | }
|
---|
| 290 |
|
---|
| 291 | let parsed = { negated: false, fastpaths: true };
|
---|
| 292 |
|
---|
| 293 | if (options.fastpaths !== false && (input[0] === '.' || input[0] === '*')) {
|
---|
| 294 | parsed.output = parse.fastpaths(input, options);
|
---|
| 295 | }
|
---|
| 296 |
|
---|
| 297 | if (!parsed.output) {
|
---|
| 298 | parsed = parse(input, options);
|
---|
| 299 | }
|
---|
| 300 |
|
---|
| 301 | return picomatch.compileRe(parsed, options, returnOutput, returnState);
|
---|
| 302 | };
|
---|
| 303 |
|
---|
| 304 | /**
|
---|
| 305 | * Create a regular expression from the given regex source string.
|
---|
| 306 | *
|
---|
| 307 | * ```js
|
---|
| 308 | * const picomatch = require('picomatch');
|
---|
| 309 | * // picomatch.toRegex(source[, options]);
|
---|
| 310 | *
|
---|
| 311 | * const { output } = picomatch.parse('*.js');
|
---|
| 312 | * console.log(picomatch.toRegex(output));
|
---|
| 313 | * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/
|
---|
| 314 | * ```
|
---|
| 315 | * @param {String} `source` Regular expression source string.
|
---|
| 316 | * @param {Object} `options`
|
---|
| 317 | * @return {RegExp}
|
---|
| 318 | * @api public
|
---|
| 319 | */
|
---|
| 320 |
|
---|
| 321 | picomatch.toRegex = (source, options) => {
|
---|
| 322 | try {
|
---|
| 323 | const opts = options || {};
|
---|
| 324 | return new RegExp(source, opts.flags || (opts.nocase ? 'i' : ''));
|
---|
| 325 | } catch (err) {
|
---|
| 326 | if (options && options.debug === true) throw err;
|
---|
| 327 | return /$^/;
|
---|
| 328 | }
|
---|
| 329 | };
|
---|
| 330 |
|
---|
| 331 | /**
|
---|
| 332 | * Picomatch constants.
|
---|
| 333 | * @return {Object}
|
---|
| 334 | */
|
---|
| 335 |
|
---|
| 336 | picomatch.constants = constants;
|
---|
| 337 |
|
---|
| 338 | /**
|
---|
| 339 | * Expose "picomatch"
|
---|
| 340 | */
|
---|
| 341 |
|
---|
| 342 | module.exports = picomatch;
|
---|