|
165 | 165 | return true; |
166 | 166 | } |
167 | 167 | function isDimension(name) { |
168 | | - let index = 0; |
169 | | - while (index++ < name.length) { |
170 | | - if (isDigit(name.charCodeAt(name.length - index))) { |
171 | | - index--; |
172 | | - break; |
173 | | - } |
174 | | - if (index == 3) { |
175 | | - break; |
| 168 | + let index = name.length; |
| 169 | + while (index--) { |
| 170 | + if (isLetter(name.charCodeAt(index))) { |
| 171 | + continue; |
176 | 172 | } |
| 173 | + index++; |
| 174 | + break; |
177 | 175 | } |
178 | | - if (index == 0 || index > 3) { |
179 | | - return false; |
180 | | - } |
181 | | - const number = name.slice(0, -index); |
182 | | - return number.length > 0 && isIdentStart(name.charCodeAt(name.length - index)) && isNumber(number); |
| 176 | + const number = name.slice(0, index); |
| 177 | + return number.length > 0 && isIdentStart(name.charCodeAt(index)) && isNumber(number); |
183 | 178 | } |
184 | 179 | function isPercentage(name) { |
185 | 180 | return name.endsWith('%') && isNumber(name.slice(0, -1)); |
186 | 181 | } |
187 | 182 | function parseDimension(name) { |
188 | | - let index = 0; |
189 | | - while (index++ < name.length) { |
190 | | - if (isDigit(name.charCodeAt(name.length - index))) { |
191 | | - index--; |
192 | | - break; |
193 | | - } |
194 | | - if (index == 3) { |
195 | | - break; |
| 183 | + let index = name.length; |
| 184 | + while (index--) { |
| 185 | + if (isLetter(name.charCodeAt(index))) { |
| 186 | + continue; |
196 | 187 | } |
| 188 | + index++; |
| 189 | + break; |
197 | 190 | } |
198 | | - const dimension = { typ: 'Dimension', val: name.slice(0, -index), unit: name.slice(-index) }; |
| 191 | + const dimension = { typ: 'Dimension', val: name.slice(0, index), unit: name.slice(index) }; |
199 | 192 | if (isAngle(dimension)) { |
200 | 193 | // @ts-ignore |
201 | 194 | dimension.typ = 'Angle'; |
|
1504 | 1497 | return `#${rgb.reduce((acc, curr) => acc + curr.toString(16).padStart(2, '0'), '')}`; |
1505 | 1498 | } |
1506 | 1499 | function getAngle(token) { |
1507 | | - if (token.typ == 'Dimension') { |
| 1500 | + if (token.typ == 'Angle') { |
1508 | 1501 | switch (token.unit) { |
1509 | 1502 | case 'deg': |
1510 | 1503 | // @ts-ignore |
|
1577 | 1570 | return values; |
1578 | 1571 | } |
1579 | 1572 |
|
| 1573 | + function reduceNumber(val) { |
| 1574 | + val = (+val).toString(); |
| 1575 | + if (val === '0') { |
| 1576 | + return '0'; |
| 1577 | + } |
| 1578 | + const chr = val.charAt(0); |
| 1579 | + if (chr == '-') { |
| 1580 | + const slice = val.slice(0, 2); |
| 1581 | + if (slice == '-0') { |
| 1582 | + return val.length == 2 ? '0' : '-' + val.slice(2); |
| 1583 | + } |
| 1584 | + } |
| 1585 | + if (chr == '0') { |
| 1586 | + return val.slice(1); |
| 1587 | + } |
| 1588 | + return val; |
| 1589 | + } |
1580 | 1590 | function render(data, opt = {}) { |
1581 | 1591 | const startTime = performance.now(); |
1582 | 1592 | const options = Object.assign(opt.minify ?? true ? { |
|
1589 | 1599 | compress: false, |
1590 | 1600 | removeComments: false, |
1591 | 1601 | }, { colorConvert: true, preserveLicense: false }, opt); |
1592 | | - return { code: doRender(data, options, function reducer(acc, curr) { |
| 1602 | + return { |
| 1603 | + code: doRender(data, options, function reducer(acc, curr) { |
1593 | 1604 | if (curr.typ == 'Comment' && options.removeComments) { |
1594 | 1605 | if (!options.preserveLicense || !curr.val.startsWith('/*!')) { |
1595 | 1606 | return acc; |
|
1599 | 1610 | return acc + renderToken(curr, options, reducer); |
1600 | 1611 | }, 0), stats: { |
1601 | 1612 | total: `${(performance.now() - startTime).toFixed(2)}ms` |
1602 | | - } }; |
| 1613 | + } |
| 1614 | + }; |
1603 | 1615 | } |
1604 | 1616 | // @ts-ignore |
1605 | 1617 | function doRender(data, options, reducer, level = 0, indents = []) { |
|
1763 | 1775 | case 'Attr': |
1764 | 1776 | return '[' + token.chi.reduce(reducer, '') + ']'; |
1765 | 1777 | case 'Time': |
1766 | | - case 'Frequency': |
1767 | 1778 | case 'Angle': |
1768 | 1779 | case 'Length': |
1769 | 1780 | case 'Dimension': |
1770 | | - const val = (+token.val).toString(); |
| 1781 | + case 'Frequency': |
| 1782 | + case 'Resolution': |
| 1783 | + let val = reduceNumber(token.val); |
| 1784 | + let unit = token.unit; |
| 1785 | + if (token.typ == 'Angle') { |
| 1786 | + const angle = getAngle(token); |
| 1787 | + let v; |
| 1788 | + let value = val + unit; |
| 1789 | + for (const u of ['turn', 'deg', 'rad', 'grad']) { |
| 1790 | + if (token.unit == u) { |
| 1791 | + continue; |
| 1792 | + } |
| 1793 | + switch (u) { |
| 1794 | + case 'turn': |
| 1795 | + v = reduceNumber(angle); |
| 1796 | + if (v.length + 4 < value.length) { |
| 1797 | + val = v; |
| 1798 | + unit = u; |
| 1799 | + value = v + u; |
| 1800 | + } |
| 1801 | + break; |
| 1802 | + case 'deg': |
| 1803 | + v = reduceNumber(angle * 360); |
| 1804 | + if (v.length + 3 < value.length) { |
| 1805 | + val = v; |
| 1806 | + unit = u; |
| 1807 | + value = v + u; |
| 1808 | + } |
| 1809 | + break; |
| 1810 | + case 'rad': |
| 1811 | + v = reduceNumber(angle * (2 * Math.PI)); |
| 1812 | + if (v.length + 3 < value.length) { |
| 1813 | + val = v; |
| 1814 | + unit = u; |
| 1815 | + value = v + u; |
| 1816 | + } |
| 1817 | + break; |
| 1818 | + case 'grad': |
| 1819 | + v = reduceNumber(angle * 400); |
| 1820 | + if (v.length + 4 < value.length) { |
| 1821 | + val = v; |
| 1822 | + unit = u; |
| 1823 | + value = v + u; |
| 1824 | + } |
| 1825 | + break; |
| 1826 | + } |
| 1827 | + } |
| 1828 | + } |
1771 | 1829 | if (val === '0') { |
1772 | | - if (token.typ == 'Time') { |
| 1830 | + if (unit == 'Time') { |
1773 | 1831 | return '0s'; |
1774 | 1832 | } |
1775 | | - if (token.typ == 'Frequency') { |
| 1833 | + if (unit == 'Frequency') { |
1776 | 1834 | return '0Hz'; |
1777 | 1835 | } |
1778 | 1836 | // @ts-ignore |
1779 | | - if (token.typ == 'Resolution') { |
| 1837 | + if (unit == 'Resolution') { |
1780 | 1838 | return '0x'; |
1781 | 1839 | } |
1782 | 1840 | return '0'; |
1783 | 1841 | } |
1784 | | - const chr = val.charAt(0); |
1785 | | - if (chr == '-') { |
1786 | | - const slice = val.slice(0, 2); |
1787 | | - if (slice == '-0') { |
1788 | | - return (val.length == 2 ? '0' : '-' + val.slice(2)) + token.unit; |
1789 | | - } |
1790 | | - } |
1791 | | - else if (chr == '0') { |
1792 | | - return val.slice(1) + token.unit; |
1793 | | - } |
1794 | | - return val + token.unit; |
| 1842 | + return val + unit; |
1795 | 1843 | case 'Perc': |
1796 | 1844 | return token.val + '%'; |
1797 | 1845 | case 'Number': |
|
3372 | 3420 | } |
3373 | 3421 | break; |
3374 | 3422 | } |
| 3423 | + // @ts-ignore |
| 3424 | + if (isNewLine(codepoint)) { |
| 3425 | + if (i == 1) { |
| 3426 | + buffer += value + escapeSequence.slice(0, i); |
| 3427 | + next(i + 1); |
| 3428 | + continue; |
| 3429 | + } |
| 3430 | + // else { |
| 3431 | + yield pushToken(buffer + value + escapeSequence.slice(0, i), 'Bad-string'); |
| 3432 | + buffer = ''; |
| 3433 | + // } |
| 3434 | + next(i + 1); |
| 3435 | + break; |
| 3436 | + } |
3375 | 3437 | // not hex or new line |
3376 | 3438 | // @ts-ignore |
3377 | | - if (i == 1 && !isNewLine(codepoint)) { |
| 3439 | + else if (i == 1) { |
3378 | 3440 | buffer += value + sequence[i]; |
3379 | 3441 | next(2); |
3380 | 3442 | continue; |
|
3403 | 3465 | next(); |
3404 | 3466 | // i += value.length; |
3405 | 3467 | buffer = ''; |
3406 | | - break; |
| 3468 | + return; |
3407 | 3469 | } |
3408 | 3470 | if (isNewLine(value.charCodeAt(0))) { |
3409 | 3471 | hasNewLine = true; |
3410 | 3472 | } |
3411 | 3473 | if (hasNewLine && value == ';') { |
3412 | | - yield pushToken(buffer, 'Bad-string'); |
| 3474 | + yield pushToken(buffer + value, 'Bad-string'); |
3413 | 3475 | buffer = ''; |
| 3476 | + next(); |
3414 | 3477 | break; |
3415 | 3478 | } |
3416 | 3479 | buffer += value; |
3417 | 3480 | next(); |
3418 | 3481 | } |
| 3482 | + if (hasNewLine) { |
| 3483 | + yield pushToken(buffer, 'Bad-string'); |
| 3484 | + } |
| 3485 | + else { |
| 3486 | + // EOF - 'Unclosed-string' fixed |
| 3487 | + yield pushToken(buffer + quote, 'String'); |
| 3488 | + } |
| 3489 | + buffer = ''; |
3419 | 3490 | } |
3420 | 3491 | function peek(count = 1) { |
3421 | 3492 | if (count == 1) { |
|
3650 | 3721 | break; |
3651 | 3722 | case '(': |
3652 | 3723 | if (buffer.length == 0) { |
3653 | | - yield pushToken('', 'Start-parens'); |
| 3724 | + yield pushToken(value); |
3654 | 3725 | break; |
3655 | 3726 | } |
3656 | 3727 | buffer += value; |
|
3764 | 3835 | if (buffer.length > 0) { |
3765 | 3836 | yield pushToken(buffer); |
3766 | 3837 | } |
| 3838 | + // yield pushToken('', 'EOF'); |
3767 | 3839 | } |
3768 | 3840 |
|
3769 | 3841 | const urlTokenMatcher = /^(["']?)[a-zA-Z0-9_/.-][a-zA-Z0-9_/:.#?-]+(\1)$/; |
|
3814 | 3886 | let tokens = results.map(mapToken); |
3815 | 3887 | let i; |
3816 | 3888 | let loc; |
| 3889 | + // if ((<Token>tokens.at(-1))?.typ == 'EOF') { |
| 3890 | + // |
| 3891 | + // tokens.pop(); |
| 3892 | + // } |
3817 | 3893 | for (i = 0; i < tokens.length; i++) { |
3818 | 3894 | if (tokens[i].typ == 'Comment') { |
3819 | 3895 | // @ts-ignore |
|
4168 | 4244 | return ([ |
4169 | 4245 | 'Whitespace', 'Semi-colon', 'Colon', 'Block-start', |
4170 | 4246 | 'Block-start', 'Attr-start', 'Attr-end', 'Start-parens', 'End-parens', |
4171 | | - 'Comma', 'Gt', 'Lt', 'Gte', 'Lte' |
| 4247 | + 'Comma', 'Gt', 'Lt', 'Gte', 'Lte', 'EOF' |
4172 | 4248 | ].includes(hint) ? { typ: hint } : { typ: hint, val }); |
4173 | 4249 | } |
4174 | 4250 | if (val == ' ') { |
|
0 commit comments