(
pattern: string[],
source: string[],
locale: string,
options?: { forbid?: { patternIndex: number; sourceIndex: number } },
)
| 320 | // patternIndex to a given sourceIndex, which lets us probe for alternative |
| 321 | // [locale] placements. |
| 322 | function alignPatternToSource( |
| 323 | pattern: string[], |
| 324 | source: string[], |
| 325 | locale: string, |
| 326 | options?: { forbid?: { patternIndex: number; sourceIndex: number } }, |
| 327 | ): { patToSrc: number[] } | null { |
| 328 | const patternLength = pattern.length; |
| 329 | const sourceLength = source.length; |
| 330 | const memo = new Map<string, boolean>(); |
| 331 | const parent = new Map<string, { i2: number; j2: number }>(); |
| 332 | const isDoubleStar = (segment: string) => segment === "**"; |
| 333 | const segmentMatches = (patternSegment: string, sourceSegment: string) => { |
| 334 | const concrete = patternSegment.replaceAll("[locale]", locale); |
| 335 | return minimatch(sourceSegment, concrete, { dot: true, nocase: true }); |
| 336 | }; |
| 337 | const forbid = options?.forbid; |
| 338 | const key = (i: number, j: number) => `${i}|${j}`; |
| 339 | const dfs = (i: number, j: number): boolean => { |
| 340 | const memoKey = key(i, j); |
| 341 | if (memo.has(memoKey)) { |
| 342 | return memo.get(memoKey)!; |
| 343 | } |
| 344 | if (i === patternLength) { |
| 345 | const done = j === sourceLength; |
| 346 | memo.set(memoKey, done); |
| 347 | return done; |
| 348 | } |
| 349 | let matched = false; |
| 350 | if (isDoubleStar(pattern[i])) { |
| 351 | for (let k = j; k <= sourceLength; k += 1) { |
| 352 | if (dfs(i + 1, k)) { |
| 353 | parent.set(memoKey, { i2: i + 1, j2: k }); |
| 354 | matched = true; |
| 355 | break; |
| 356 | } |
| 357 | } |
| 358 | } else if (j < sourceLength && segmentMatches(pattern[i], source[j])) { |
| 359 | const blocked = |
| 360 | forbid && forbid.patternIndex === i && forbid.sourceIndex === j; |
| 361 | if (!blocked && dfs(i + 1, j + 1)) { |
| 362 | parent.set(memoKey, { i2: i + 1, j2: j + 1 }); |
| 363 | matched = true; |
| 364 | } |
| 365 | } |
| 366 | memo.set(memoKey, matched); |
| 367 | return matched; |
| 368 | }; |
| 369 | |
| 370 | if (!dfs(0, 0)) { |
| 371 | return null; |
| 372 | } |
| 373 | |
| 374 | const patToSrc = Array(patternLength).fill(-1) as number[]; |
| 375 | let i = 0; |
| 376 | let j = 0; |
| 377 | while (i < patternLength) { |
| 378 | const step = parent.get(key(i, j)); |
| 379 | if (!step) { |
no test coverage detected