|
1589 | 1589 | compress: false, |
1590 | 1590 | removeComments: false, |
1591 | 1591 | }, { colorConvert: true, preserveLicense: false }, opt); |
1592 | | - function reducer(acc, curr, index, original) { |
1593 | | - if (curr.typ == 'Comment' && options.removeComments) { |
1594 | | - if (!options.preserveLicense || !curr.val.startsWith('/*!')) { |
1595 | | - return acc; |
| 1592 | + return { code: doRender(data, options, function reducer(acc, curr) { |
| 1593 | + if (curr.typ == 'Comment' && options.removeComments) { |
| 1594 | + if (!options.preserveLicense || !curr.val.startsWith('/*!')) { |
| 1595 | + return acc; |
| 1596 | + } |
| 1597 | + return acc + curr.val; |
1596 | 1598 | } |
1597 | | - } |
1598 | | - return acc + renderToken(curr, options); |
1599 | | - } |
1600 | | - return { code: doRender(data, options, reducer, 0), stats: { |
| 1599 | + return acc + renderToken(curr, options, reducer); |
| 1600 | + }, 0), stats: { |
1601 | 1601 | total: `${(performance.now() - startTime).toFixed(2)}ms` |
1602 | 1602 | } }; |
1603 | 1603 | } |
|
1613 | 1613 | const indentSub = indents[level + 1]; |
1614 | 1614 | switch (data.typ) { |
1615 | 1615 | case 'Declaration': |
1616 | | - return `${data.nam}:${options.indent}${data.val.reduce((acc, curr) => acc + renderToken(curr), '')}`; |
| 1616 | + return `${data.nam}:${options.indent}${data.val.reduce(reducer, '')}`; |
1617 | 1617 | case 'Comment': |
1618 | | - return options.removeComments ? '' : data.val; |
| 1618 | + return !options.removeComments || (options.preserveLicense && data.val.startsWith('/*!')) ? data.val : ''; |
1619 | 1619 | case 'StyleSheet': |
1620 | 1620 | return data.chi.reduce((css, node) => { |
1621 | 1621 | const str = doRender(node, options, reducer, level, indents); |
|
1636 | 1636 | let children = data.chi.reduce((css, node) => { |
1637 | 1637 | let str; |
1638 | 1638 | if (node.typ == 'Comment') { |
1639 | | - str = options.removeComments ? '' : node.val; |
| 1639 | + str = options.removeComments && (!options.preserveLicense || !node.val.startsWith('/*!')) ? '' : node.val; |
1640 | 1640 | } |
1641 | 1641 | else if (node.typ == 'Declaration') { |
1642 | 1642 | if (node.val.length == 0) { |
|
1669 | 1669 | } |
1670 | 1670 | return ''; |
1671 | 1671 | } |
1672 | | - function renderToken(token, options = {}) { |
| 1672 | + function renderToken(token, options = {}, reducer) { |
| 1673 | + if (reducer == null) { |
| 1674 | + reducer = function (acc, curr) { |
| 1675 | + if (curr.typ == 'Comment' && options.removeComments) { |
| 1676 | + if (!options.preserveLicense || !curr.val.startsWith('/*!')) { |
| 1677 | + return acc; |
| 1678 | + } |
| 1679 | + return acc + curr.val; |
| 1680 | + } |
| 1681 | + return acc + renderToken(curr, options, reducer); |
| 1682 | + }; |
| 1683 | + } |
1673 | 1684 | switch (token.typ) { |
1674 | 1685 | case 'Color': |
1675 | 1686 | if (options.minify || options.colorConvert) { |
|
1720 | 1731 | case 'UrlFunc': |
1721 | 1732 | case 'Pseudo-class-func': |
1722 | 1733 | // @ts-ignore |
1723 | | - return ( /* options.minify && 'Pseudo-class-func' == token.typ && token.val.slice(0, 2) == '::' ? token.val.slice(1) :*/token.val ?? '') + '(' + token.chi.reduce((acc, curr) => { |
1724 | | - if (options.removeComments && curr.typ == 'Comment') { |
1725 | | - if (!options.preserveLicense || !curr.val.startsWith('/*!')) { |
1726 | | - return acc; |
1727 | | - } |
1728 | | - } |
1729 | | - return acc + renderToken(curr, options); |
1730 | | - }, '') + ')'; |
| 1734 | + return ( /* options.minify && 'Pseudo-class-func' == token.typ && token.val.slice(0, 2) == '::' ? token.val.slice(1) :*/token.val ?? '') + '(' + token.chi.reduce(reducer, '') + ')'; |
1731 | 1735 | case 'Includes': |
1732 | 1736 | return '~='; |
1733 | 1737 | case 'Dash-match': |
1734 | 1738 | return '|='; |
1735 | 1739 | case 'Lt': |
1736 | 1740 | return '<'; |
| 1741 | + case 'Lte': |
| 1742 | + return '<='; |
1737 | 1743 | case 'Gt': |
1738 | 1744 | return '>'; |
| 1745 | + case 'Gte': |
| 1746 | + return '>='; |
1739 | 1747 | case 'End-parens': |
1740 | 1748 | return ')'; |
1741 | 1749 | case 'Attr-start': |
|
1753 | 1761 | case 'Important': |
1754 | 1762 | return '!important'; |
1755 | 1763 | case 'Attr': |
1756 | | - return '[' + token.chi.reduce((acc, curr) => acc + renderToken(curr, options), '') + ']'; |
| 1764 | + return '[' + token.chi.reduce(reducer, '') + ']'; |
1757 | 1765 | case 'Time': |
1758 | 1766 | case 'Frequency': |
1759 | 1767 | case 'Angle': |
|
1800 | 1808 | } |
1801 | 1809 | return num; |
1802 | 1810 | case 'Comment': |
1803 | | - if (options.removeComments) { |
| 1811 | + if (options.removeComments && (!options.preserveLicense || !token.val.startsWith('/*!'))) { |
1804 | 1812 | return ''; |
1805 | 1813 | } |
1806 | 1814 | case 'Url-token': |
|
2071 | 2079 | i--; |
2072 | 2080 | continue; |
2073 | 2081 | } |
| 2082 | + // @ts-ignore |
2074 | 2083 | if (('propertyName' in acc[i] && acc[i].propertyName == property) || matchType(acc[i], props)) { |
2075 | 2084 | if ('prefix' in props && props.previous != null && !(props.previous in tokens)) { |
2076 | 2085 | return acc; |
|
2236 | 2245 | if (props.multiple && props.separator != null && props.separator.typ == val.typ && eq(props.separator, val)) { |
2237 | 2246 | continue; |
2238 | 2247 | } |
2239 | | - match = matchType(val, curr[1]); |
| 2248 | + // @ts-ignore |
| 2249 | + match = val.typ == 'Comment' || matchType(val, curr[1]); |
2240 | 2250 | if (isShorthand) { |
2241 | 2251 | isShorthand = match; |
2242 | 2252 | } |
| 2253 | + // @ts-ignore |
2243 | 2254 | if (('propertyName' in val && val.propertyName == property) || match) { |
2244 | 2255 | if (!(curr[0] in tokens)) { |
2245 | 2256 | tokens[curr[0]] = [[]]; |
2246 | 2257 | } |
2247 | 2258 | // is default value |
2248 | 2259 | tokens[curr[0]][current].push(val); |
2249 | | - // continue; |
2250 | 2260 | } |
2251 | 2261 | else { |
2252 | 2262 | acc.push(curr[0]); |
|
2263 | 2273 | if (!isShorthand || Object.entries(this.config.properties).some(entry => { |
2264 | 2274 | // missing required property |
2265 | 2275 | return entry[1].required && !(entry[0] in tokens); |
2266 | | - }) || !Object.values(tokens).every(v => v.length == count)) { |
| 2276 | + }) || |
| 2277 | + // @ts-ignore |
| 2278 | + !Object.values(tokens).every(v => v.filter(t => t.typ != 'Comment').length == count)) { |
2267 | 2279 | // @ts-ignore |
2268 | 2280 | iterable = this.declarations.values(); |
2269 | 2281 | } |
|
2816 | 2828 | continue; |
2817 | 2829 | // } |
2818 | 2830 | } |
| 2831 | + // @ts-ignore |
| 2832 | + if (hasDeclaration(node)) { |
| 2833 | + // @ts-ignore |
| 2834 | + minifyRule(node); |
| 2835 | + } |
| 2836 | + else { |
| 2837 | + minify(node, options, recursive); |
| 2838 | + } |
| 2839 | + previous = node; |
| 2840 | + nodeIndex = i; |
| 2841 | + continue; |
2819 | 2842 | } |
2820 | 2843 | // @ts-ignore |
2821 | 2844 | if (node.typ == 'Rule') { |
|
3330 | 3353 | } |
3331 | 3354 | buffer += quoteStr; |
3332 | 3355 | while (value = peek()) { |
3333 | | - // if (ind >= iterator.length) { |
3334 | | - // |
3335 | | - // yield pushToken(buffer, hasNewLine ? 'Bad-string' : 'Unclosed-string'); |
3336 | | - // break; |
3337 | | - // } |
3338 | 3356 | if (value == '\\') { |
3339 | 3357 | const sequence = peek(6); |
3340 | 3358 | let escapeSequence = ''; |
|
3376 | 3394 | next(escapeSequence.length + 1); |
3377 | 3395 | continue; |
3378 | 3396 | } |
3379 | | - // buffer += value; |
3380 | | - // if (ind >= iterator.length) { |
3381 | | - // |
3382 | | - // // drop '\\' at the end |
3383 | | - // yield pushToken(buffer); |
3384 | | - // break; |
3385 | | - // } |
3386 | 3397 | buffer += next(2); |
3387 | 3398 | continue; |
3388 | 3399 | } |
|
3403 | 3414 | break; |
3404 | 3415 | } |
3405 | 3416 | buffer += value; |
3406 | | - // i += value.length; |
3407 | 3417 | next(); |
3408 | 3418 | } |
3409 | 3419 | } |
|
3519 | 3529 | yield pushToken(buffer); |
3520 | 3530 | buffer = ''; |
3521 | 3531 | } |
| 3532 | + if (peek() == '=') { |
| 3533 | + yield pushToken('', 'Lte'); |
| 3534 | + next(); |
| 3535 | + break; |
| 3536 | + } |
3522 | 3537 | buffer += value; |
3523 | 3538 | value = next(); |
3524 | 3539 | if (ind >= iterator.length) { |
|
3587 | 3602 | yield pushToken(buffer); |
3588 | 3603 | buffer = ''; |
3589 | 3604 | } |
3590 | | - yield pushToken('', 'Gt'); |
| 3605 | + if (peek() == '=') { |
| 3606 | + yield pushToken('', 'Gte'); |
| 3607 | + next(); |
| 3608 | + } |
| 3609 | + else { |
| 3610 | + yield pushToken('', 'Gt'); |
| 3611 | + } |
3591 | 3612 | consumeWhiteSpace(); |
3592 | 3613 | break; |
3593 | 3614 | case '.': |
|
3746 | 3767 | } |
3747 | 3768 |
|
3748 | 3769 | const urlTokenMatcher = /^(["']?)[a-zA-Z0-9_/.-][a-zA-Z0-9_/:.#?-]+(\1)$/; |
| 3770 | + const trimWhiteSpace = ['Gt', 'Gte', 'Lt', 'Lte']; |
3749 | 3771 | const funcLike = ['Start-parens', 'Func', 'UrlFunc', 'Pseudo-class-func']; |
3750 | 3772 | /** |
3751 | 3773 | * |
|
3910 | 3932 | // https://www.w3.org/TR/css-nesting-1/#conditionals |
3911 | 3933 | // allowed nesting at-rules |
3912 | 3934 | // there must be a top level rule in the stack |
3913 | | - const raw = tokens.reduce((acc, curr) => { |
| 3935 | + const raw = parseTokens(tokens, { minify: options.minify }).reduce((acc, curr) => { |
3914 | 3936 | acc.push(renderToken(curr, { removeComments: true })); |
3915 | 3937 | return acc; |
3916 | 3938 | }, []); |
|
3941 | 3963 | const uniq = new Map; |
3942 | 3964 | parseTokens(tokens, { minify: options.minify }).reduce((acc, curr, index, array) => { |
3943 | 3965 | if (curr.typ == 'Whitespace') { |
3944 | | - if (array[index - 1]?.typ == 'Gt' || |
3945 | | - array[index + 1]?.typ == 'Gt' || |
| 3966 | + if (trimWhiteSpace.includes(array[index - 1]?.typ) || |
| 3967 | + trimWhiteSpace.includes(array[index + 1]?.typ) || |
3946 | 3968 | combinators.includes(array[index - 1]?.val) || |
3947 | 3969 | combinators.includes(array[index + 1]?.val)) { |
3948 | 3970 | return acc; |
|
4146 | 4168 | return ([ |
4147 | 4169 | 'Whitespace', 'Semi-colon', 'Colon', 'Block-start', |
4148 | 4170 | 'Block-start', 'Attr-start', 'Attr-end', 'Start-parens', 'End-parens', |
4149 | | - 'Comma', 'Gt', 'Lt' |
| 4171 | + 'Comma', 'Gt', 'Lt', 'Gte', 'Lte' |
4150 | 4172 | ].includes(hint) ? { typ: hint } : { typ: hint, val }); |
4151 | 4173 | } |
4152 | 4174 | if (val == ' ') { |
|
4274 | 4296 | const t = tokens[i]; |
4275 | 4297 | if (t.typ == 'Whitespace' && ((i == 0 || |
4276 | 4298 | i + 1 == tokens.length || |
4277 | | - ['Comma'].includes(tokens[i + 1].typ) || |
| 4299 | + ['Comma', 'Gte', 'Lte'].includes(tokens[i + 1].typ)) || |
4278 | 4300 | (i > 0 && |
4279 | | - tokens[i + 1]?.typ != 'Literal' && |
4280 | | - funcLike.includes(tokens[i - 1].typ) && |
4281 | | - !['var', 'calc'].includes(tokens[i - 1].val))))) { |
| 4301 | + // tokens[i + 1]?.typ != 'Literal' || |
| 4302 | + // funcLike.includes(tokens[i - 1].typ) && |
| 4303 | + // !['var', 'calc'].includes((<FunctionToken>tokens[i - 1]).val)))) && |
| 4304 | + trimWhiteSpace.includes(tokens[i - 1].typ)))) { |
4282 | 4305 | tokens.splice(i--, 1); |
4283 | 4306 | continue; |
4284 | 4307 | } |
|
4395 | 4418 | let m = t.chi.length; |
4396 | 4419 | while (m-- > 0) { |
4397 | 4420 | // @ts-ignore |
4398 | | - if (t.chi[m].typ == 'Literal') { |
| 4421 | + if (['Literal'].concat(trimWhiteSpace).includes(t.chi[m].typ)) { |
4399 | 4422 | // @ts-ignore |
4400 | 4423 | if (t.chi[m + 1]?.typ == 'Whitespace') { |
4401 | 4424 | // @ts-ignore |
|
0 commit comments