@@ -197,11 +197,97 @@ export const getPrunePath = (props: unknown) => {
197197 && props !== '' ;
198198
199199 if ( validPropsString ) {
200- // Regular expression to split the properties string by spaces,
201- // but it should not split if there is space inside value, like in:
202- // 'foo.[=]./foo bar baz/ bar' or 'foo.[=]./foo bar \/ baz/ bar'
203- const splitRegexp = / (?< ! \. \[ = \] \. \/ (?: [ ^ / ] | \\ .) * ) \s + / ;
204- const parts = props . split ( splitRegexp ) . map ( ( part ) => {
200+ /**
201+ * Safari 15 does not support lookbehind, so we need to use a custom splitter.
202+ *
203+ * Legacy approach (for engines with lookbehind) that this replaces:
204+ *
205+ * // Regular expression to split the properties string by spaces,
206+ * // but it should not split if there is space inside value, like in:
207+ * // 'foo.[=]./foo bar baz/ bar' or 'foo.[=]./foo bar \/ baz/ bar'
208+ * // const splitRegexp = /(?<!\.\[=\]\.\/(?:[^/]|\\.)*)\s+/;
209+ *
210+ * @param str splitted string
211+ * @returns array of parts
212+ */
213+ // We ignore the rule here because we need to define the function inside the function,
214+ // so that we do not have to import it additionally from the scriptlets,
215+ // also we need to use the VALUE_MARKER variable.
216+ // eslint-disable-next-line no-inner-declarations
217+ function splitProps ( str : string ) {
218+ const parts : string [ ] = [ ] ;
219+ let current = '' ;
220+ let i = 0 ;
221+ let insideRegex = false ;
222+ let escapeActive = false ;
223+
224+ while ( i < str . length ) {
225+ const ch = str [ i ] ;
226+
227+ if ( ! insideRegex ) {
228+ // split on whitespace (treat runs of whitespace as a single separator)
229+ if (
230+ ch === ' '
231+ || ch === '\n'
232+ || ch === '\t'
233+ || ch === '\r'
234+ || ch === '\f'
235+ || ch === '\v'
236+ ) {
237+ // skip consecutive whitespace
238+ while ( i < str . length && / \s / . test ( str [ i ] ) ) {
239+ i += 1 ;
240+ }
241+ if ( current !== '' ) {
242+ parts . push ( current ) ;
243+ current = '' ;
244+ }
245+ continue ;
246+ }
247+
248+ // detect VALUE_MARKER followed by '/'
249+ if ( str . startsWith ( VALUE_MARKER , i ) ) {
250+ current += VALUE_MARKER ;
251+ i += VALUE_MARKER . length ;
252+ if ( str [ i ] === '/' ) {
253+ // enter regex mode and consume opening '/'
254+ insideRegex = true ;
255+ escapeActive = false ;
256+ current += '/' ;
257+ i += 1 ;
258+ continue ;
259+ }
260+ // no regex begins; continue as normal
261+ continue ;
262+ }
263+
264+ current += ch ;
265+ i += 1 ;
266+ continue ;
267+ }
268+
269+ // inside regex body: copy until we hit an unescaped '/'
270+ current += ch ;
271+ if ( ch === '\\' ) {
272+ escapeActive = ! escapeActive ;
273+ } else if ( ch === '/' && ! escapeActive ) {
274+ insideRegex = false ;
275+ escapeActive = false ;
276+ } else {
277+ escapeActive = false ;
278+ }
279+ i += 1 ;
280+ }
281+
282+ if ( current !== '' ) {
283+ parts . push ( current ) ;
284+ }
285+
286+ return parts ;
287+ }
288+
289+ const rawParts = splitProps ( props ) ;
290+ const parts = rawParts . map ( ( part ) => {
205291 const splitPart = part . split ( VALUE_MARKER ) ;
206292 const path = splitPart [ 0 ] ;
207293 let value = splitPart [ 1 ] as any ;
0 commit comments