| 640 | } |
| 641 | |
| 642 | function parseString( |
| 643 | input: string, |
| 644 | startIdx: number, |
| 645 | quoteChar: number, |
| 646 | source: Source | null = null, |
| 647 | ): number { |
| 648 | let peekChar: number |
| 649 | |
| 650 | // We need to ensure that the closing quote is the same as the opening |
| 651 | // quote. |
| 652 | // |
| 653 | // E.g.: |
| 654 | // |
| 655 | // ```css |
| 656 | // .foo { |
| 657 | // content: "This is a string with a 'quote' in it"; |
| 658 | // ^ ^ -> These are not the end of the string. |
| 659 | // } |
| 660 | // ``` |
| 661 | for (let i = startIdx + 1; i < input.length; i++) { |
| 662 | peekChar = input.charCodeAt(i) |
| 663 | |
| 664 | // Current character is a `\` therefore the next character is escaped. |
| 665 | if (peekChar === BACKSLASH) { |
| 666 | i += 1 |
| 667 | } |
| 668 | |
| 669 | // End of the string. |
| 670 | else if (peekChar === quoteChar) { |
| 671 | return i |
| 672 | } |
| 673 | |
| 674 | // End of the line without ending the string but with a `;` at the end. |
| 675 | // |
| 676 | // E.g.: |
| 677 | // |
| 678 | // ```css |
| 679 | // .foo { |
| 680 | // content: "This is a string with a; |
| 681 | // ^ Missing " |
| 682 | // } |
| 683 | // ``` |
| 684 | else if ( |
| 685 | peekChar === SEMICOLON && |
| 686 | (input.charCodeAt(i + 1) === LINE_BREAK || |
| 687 | (input.charCodeAt(i + 1) === CARRIAGE_RETURN && input.charCodeAt(i + 2) === LINE_BREAK)) |
| 688 | ) { |
| 689 | throw new CssSyntaxError( |
| 690 | `Unterminated string: ${input.slice(startIdx, i + 1) + String.fromCharCode(quoteChar)}`, |
| 691 | source ? [source, startIdx, i + 1] : null, |
| 692 | ) |
| 693 | } |
| 694 | |
| 695 | // End of the line without ending the string. |
| 696 | // |
| 697 | // E.g.: |
| 698 | // |
| 699 | // ```css |