You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
202 lines
7.8 KiB
202 lines
7.8 KiB
'use strict'; |
|
|
|
Object.defineProperty(exports, "__esModule", { |
|
value: true |
|
}); |
|
|
|
/*! |
|
* XRegExp.matchRecursive 4.0.0 |
|
* <xregexp.com> |
|
* Steven Levithan (c) 2009-2017 MIT License |
|
*/ |
|
|
|
exports.default = function (XRegExp) { |
|
|
|
/** |
|
* Returns a match detail object composed of the provided values. |
|
* |
|
* @private |
|
*/ |
|
function row(name, value, start, end) { |
|
return { |
|
name: name, |
|
value: value, |
|
start: start, |
|
end: end |
|
}; |
|
} |
|
|
|
/** |
|
* Returns an array of match strings between outermost left and right delimiters, or an array of |
|
* objects with detailed match parts and position data. An error is thrown if delimiters are |
|
* unbalanced within the data. |
|
* |
|
* @memberOf XRegExp |
|
* @param {String} str String to search. |
|
* @param {String} left Left delimiter as an XRegExp pattern. |
|
* @param {String} right Right delimiter as an XRegExp pattern. |
|
* @param {String} [flags] Any native or XRegExp flags, used for the left and right delimiters. |
|
* @param {Object} [options] Lets you specify `valueNames` and `escapeChar` options. |
|
* @returns {Array} Array of matches, or an empty array. |
|
* @example |
|
* |
|
* // Basic usage |
|
* let str = '(t((e))s)t()(ing)'; |
|
* XRegExp.matchRecursive(str, '\\(', '\\)', 'g'); |
|
* // -> ['t((e))s', '', 'ing'] |
|
* |
|
* // Extended information mode with valueNames |
|
* str = 'Here is <div> <div>an</div></div> example'; |
|
* XRegExp.matchRecursive(str, '<div\\s*>', '</div>', 'gi', { |
|
* valueNames: ['between', 'left', 'match', 'right'] |
|
* }); |
|
* // -> [ |
|
* // {name: 'between', value: 'Here is ', start: 0, end: 8}, |
|
* // {name: 'left', value: '<div>', start: 8, end: 13}, |
|
* // {name: 'match', value: ' <div>an</div>', start: 13, end: 27}, |
|
* // {name: 'right', value: '</div>', start: 27, end: 33}, |
|
* // {name: 'between', value: ' example', start: 33, end: 41} |
|
* // ] |
|
* |
|
* // Omitting unneeded parts with null valueNames, and using escapeChar |
|
* str = '...{1}.\\{{function(x,y){return {y:x}}}'; |
|
* XRegExp.matchRecursive(str, '{', '}', 'g', { |
|
* valueNames: ['literal', null, 'value', null], |
|
* escapeChar: '\\' |
|
* }); |
|
* // -> [ |
|
* // {name: 'literal', value: '...', start: 0, end: 3}, |
|
* // {name: 'value', value: '1', start: 4, end: 5}, |
|
* // {name: 'literal', value: '.\\{', start: 6, end: 9}, |
|
* // {name: 'value', value: 'function(x,y){return {y:x}}', start: 10, end: 37} |
|
* // ] |
|
* |
|
* // Sticky mode via flag y |
|
* str = '<1><<<2>>><3>4<5>'; |
|
* XRegExp.matchRecursive(str, '<', '>', 'gy'); |
|
* // -> ['1', '<<2>>', '3'] |
|
*/ |
|
XRegExp.matchRecursive = function (str, left, right, flags, options) { |
|
flags = flags || ''; |
|
options = options || {}; |
|
var global = flags.indexOf('g') !== -1; |
|
var sticky = flags.indexOf('y') !== -1; |
|
// Flag `y` is controlled internally |
|
var basicFlags = flags.replace(/y/g, ''); |
|
var escapeChar = options.escapeChar; |
|
var vN = options.valueNames; |
|
var output = []; |
|
var openTokens = 0; |
|
var delimStart = 0; |
|
var delimEnd = 0; |
|
var lastOuterEnd = 0; |
|
var outerStart = void 0; |
|
var innerStart = void 0; |
|
var leftMatch = void 0; |
|
var rightMatch = void 0; |
|
var esc = void 0; |
|
left = XRegExp(left, basicFlags); |
|
right = XRegExp(right, basicFlags); |
|
|
|
if (escapeChar) { |
|
if (escapeChar.length > 1) { |
|
throw new Error('Cannot use more than one escape character'); |
|
} |
|
escapeChar = XRegExp.escape(escapeChar); |
|
// Example of concatenated `esc` regex: |
|
// `escapeChar`: '%' |
|
// `left`: '<' |
|
// `right`: '>' |
|
// Regex is: /(?:%[\S\s]|(?:(?!<|>)[^%])+)+/ |
|
esc = new RegExp('(?:' + escapeChar + '[\\S\\s]|(?:(?!' + |
|
// Using `XRegExp.union` safely rewrites backreferences in `left` and `right`. |
|
// Intentionally not passing `basicFlags` to `XRegExp.union` since any syntax |
|
// transformation resulting from those flags was already applied to `left` and |
|
// `right` when they were passed through the XRegExp constructor above. |
|
XRegExp.union([left, right], '', { conjunction: 'or' }).source + ')[^' + escapeChar + '])+)+', |
|
// Flags `gy` not needed here |
|
flags.replace(/[^imu]+/g, '')); |
|
} |
|
|
|
while (true) { |
|
// If using an escape character, advance to the delimiter's next starting position, |
|
// skipping any escaped characters in between |
|
if (escapeChar) { |
|
delimEnd += (XRegExp.exec(str, esc, delimEnd, 'sticky') || [''])[0].length; |
|
} |
|
leftMatch = XRegExp.exec(str, left, delimEnd); |
|
rightMatch = XRegExp.exec(str, right, delimEnd); |
|
// Keep the leftmost match only |
|
if (leftMatch && rightMatch) { |
|
if (leftMatch.index <= rightMatch.index) { |
|
rightMatch = null; |
|
} else { |
|
leftMatch = null; |
|
} |
|
} |
|
// Paths (LM: leftMatch, RM: rightMatch, OT: openTokens): |
|
// LM | RM | OT | Result |
|
// 1 | 0 | 1 | loop |
|
// 1 | 0 | 0 | loop |
|
// 0 | 1 | 1 | loop |
|
// 0 | 1 | 0 | throw |
|
// 0 | 0 | 1 | throw |
|
// 0 | 0 | 0 | break |
|
// The paths above don't include the sticky mode special case. The loop ends after the |
|
// first completed match if not `global`. |
|
if (leftMatch || rightMatch) { |
|
delimStart = (leftMatch || rightMatch).index; |
|
delimEnd = delimStart + (leftMatch || rightMatch)[0].length; |
|
} else if (!openTokens) { |
|
break; |
|
} |
|
if (sticky && !openTokens && delimStart > lastOuterEnd) { |
|
break; |
|
} |
|
if (leftMatch) { |
|
if (!openTokens) { |
|
outerStart = delimStart; |
|
innerStart = delimEnd; |
|
} |
|
++openTokens; |
|
} else if (rightMatch && openTokens) { |
|
if (! --openTokens) { |
|
if (vN) { |
|
if (vN[0] && outerStart > lastOuterEnd) { |
|
output.push(row(vN[0], str.slice(lastOuterEnd, outerStart), lastOuterEnd, outerStart)); |
|
} |
|
if (vN[1]) { |
|
output.push(row(vN[1], str.slice(outerStart, innerStart), outerStart, innerStart)); |
|
} |
|
if (vN[2]) { |
|
output.push(row(vN[2], str.slice(innerStart, delimStart), innerStart, delimStart)); |
|
} |
|
if (vN[3]) { |
|
output.push(row(vN[3], str.slice(delimStart, delimEnd), delimStart, delimEnd)); |
|
} |
|
} else { |
|
output.push(str.slice(innerStart, delimStart)); |
|
} |
|
lastOuterEnd = delimEnd; |
|
if (!global) { |
|
break; |
|
} |
|
} |
|
} else { |
|
throw new Error('Unbalanced delimiter found in string'); |
|
} |
|
// If the delimiter matched an empty string, avoid an infinite loop |
|
if (delimStart === delimEnd) { |
|
++delimEnd; |
|
} |
|
} |
|
|
|
if (global && !sticky && vN && vN[0] && str.length > lastOuterEnd) { |
|
output.push(row(vN[0], str.slice(lastOuterEnd), lastOuterEnd, str.length)); |
|
} |
|
|
|
return output; |
|
}; |
|
}; |
|
|
|
module.exports = exports['default']; |