/** * Copyright (c) HashiCorp, Inc. * SPDX-License-Identifier: MPL-2.0 */ // @ts-check const WILDCARD_GLOB = '*'; /** * * @param {string} pattern * @param {string} comparable * @example matchGlob('foo', 'foo') // true * @example matchGlob('foo', 'bar') // false * @example matchGlob('foo*', 'footbar') // true * @example matchGlob('foo*', 'bar') // false * @example matchGlob('*foo', 'foobar') // false * @example matchGlob('*foo', 'barfoo') // true * @example matchGlob('foo*bar', 'footbar') // true * @returns {boolean} */ export default function matchGlob(pattern, comparable) { const parts = pattern?.split(WILDCARD_GLOB); const hasLeadingGlob = pattern?.startsWith(WILDCARD_GLOB); const hasTrailingGlob = pattern?.endsWith(WILDCARD_GLOB); const lastPartOfPattern = parts[parts.length - 1]; const isPatternWithoutGlob = parts.length === 1 && !hasLeadingGlob; if (!pattern || !comparable || isPatternWithoutGlob) { return pattern === comparable; } if (pattern === WILDCARD_GLOB) { return true; } let subStringToMatchOn = comparable; for (let i = 0; i < parts.length; i++) { const part = parts[i]; const idx = subStringToMatchOn?.indexOf(part); const doesStringIncludeSubPattern = idx > -1; const doesMatchOnFirstSubpattern = idx === 0; if (i === 0 && !hasLeadingGlob && !doesMatchOnFirstSubpattern) { return false; } if (!doesStringIncludeSubPattern) { return false; } subStringToMatchOn = subStringToMatchOn.slice(0, idx + comparable.length); } return hasTrailingGlob || comparable.endsWith(lastPartOfPattern); }