(code: string, attrs: Record<string, string>)
| 419 | } |
| 420 | |
| 421 | export function mergeAttributes(code: string, attrs: Record<string, string>): string { |
| 422 | if (!code || !Object.keys(attrs).length) return code |
| 423 | |
| 424 | let i = 0 |
| 425 | // Skip leading whitespace |
| 426 | while (i < code.length && /\s/.test(code[i])) i++ |
| 427 | |
| 428 | if (code[i] !== '<') return code |
| 429 | i++ |
| 430 | |
| 431 | // Scan Tag Name |
| 432 | while (i < code.length && /[a-zA-Z0-9\-_:.]/.test(code[i])) i++ |
| 433 | const tagNameEnd = i |
| 434 | |
| 435 | const existingAttrs = new Map< |
| 436 | string, |
| 437 | { nameStart: number; valueStart: number; valueEnd: number; quote: string } |
| 438 | >() |
| 439 | |
| 440 | while (i < code.length) { |
| 441 | // Skip whitespace |
| 442 | while (i < code.length && /\s/.test(code[i])) i++ |
| 443 | |
| 444 | if (i >= code.length) break |
| 445 | |
| 446 | // Check for end of tag |
| 447 | if (code[i] === '>') { |
| 448 | break |
| 449 | } |
| 450 | if (code[i] === '/' && code[i + 1] === '>') { |
| 451 | break |
| 452 | } |
| 453 | |
| 454 | // Attribute Name |
| 455 | const attrNameStart = i |
| 456 | while (i < code.length && /[^=\s/>]/.test(code[i])) i++ |
| 457 | const attrName = code.slice(attrNameStart, i) |
| 458 | |
| 459 | // Skip whitespace after name |
| 460 | while (i < code.length && /\s/.test(code[i])) i++ |
| 461 | |
| 462 | // Check for equals |
| 463 | if (code[i] === '=') { |
| 464 | i++ // skip = |
| 465 | // Skip whitespace after = |
| 466 | while (i < code.length && /\s/.test(code[i])) i++ |
| 467 | |
| 468 | // Attribute Value |
| 469 | let quote = '' |
| 470 | let valueStart = i |
| 471 | let valueEnd |
| 472 | |
| 473 | if (code[i] === '"' || code[i] === "'") { |
| 474 | quote = code[i] |
| 475 | i++ |
| 476 | valueStart = i |
| 477 | while (i < code.length && code[i] !== quote) { |
| 478 | if (code[i] === '\\') i++ // skip escaped char |
no test coverage detected