@tbela99/css-parser 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +267 -2
- package/dist/config.json.js +611 -4
- package/dist/index-umd-web.js +2898 -1223
- package/dist/index.cjs +2898 -1223
- package/dist/lib/ast/expand.js +11 -11
- package/dist/lib/ast/features/calc.js +33 -224
- package/dist/lib/ast/features/index.js +3 -3
- package/dist/lib/ast/features/inlinecssvariables.js +46 -31
- package/dist/lib/ast/features/shorthand.js +7 -7
- package/dist/lib/ast/features/utils/math.js +95 -0
- package/dist/lib/ast/math/expression.js +185 -0
- package/dist/lib/ast/math/math.js +95 -0
- package/dist/lib/ast/minify.js +34 -29
- package/dist/lib/ast/types.js +108 -78
- package/dist/lib/ast/walk.js +42 -9
- package/dist/lib/fs/resolve.js +4 -3
- package/dist/lib/iterable/set.js +48 -0
- package/dist/lib/iterable/weakmap.js +53 -0
- package/dist/lib/iterable/weakset.js +48 -0
- package/dist/lib/parser/declaration/list.js +7 -3
- package/dist/lib/parser/declaration/map.js +86 -7
- package/dist/lib/parser/declaration/set.js +43 -23
- package/dist/lib/parser/parse.js +561 -387
- package/dist/lib/parser/tokenize.js +42 -13
- package/dist/lib/parser/utils/declaration.js +67 -0
- package/dist/lib/parser/utils/syntax.js +32 -2
- package/dist/lib/parser/utils/type.js +7 -2
- package/dist/lib/renderer/render.js +163 -47
- package/dist/lib/renderer/utils/calccolor.js +238 -0
- package/dist/lib/renderer/utils/color.js +36 -164
- package/dist/lib/renderer/utils/hex.js +124 -0
- package/dist/lib/renderer/utils/hsl.js +49 -0
- package/dist/lib/renderer/utils/hsv.js +15 -0
- package/dist/lib/renderer/utils/hwb.js +50 -0
- package/dist/lib/renderer/utils/rgb.js +66 -0
- package/dist/node/index.js +8 -12
- package/dist/web/index.js +8 -12
- package/package.json +9 -7
- package/dist/index.d.ts +0 -1056
- /package/dist/lib/ast/{utiles → utils}/minifyfeature.js +0 -0
|
@@ -16,7 +16,6 @@ function* tokenize(stream) {
|
|
|
16
16
|
};
|
|
17
17
|
let value;
|
|
18
18
|
let buffer = '';
|
|
19
|
-
// let input: string = '';
|
|
20
19
|
function consumeWhiteSpace() {
|
|
21
20
|
let count = 0;
|
|
22
21
|
while (isWhiteSpace(stream.charAt(count + ind + 1).charCodeAt(0))) {
|
|
@@ -26,7 +25,7 @@ function* tokenize(stream) {
|
|
|
26
25
|
return count;
|
|
27
26
|
}
|
|
28
27
|
function pushToken(token, hint) {
|
|
29
|
-
const result = { token, hint, position: { ...position }, bytesIn: ind };
|
|
28
|
+
const result = { token, hint, position: { ...position }, bytesIn: ind + 1 };
|
|
30
29
|
position.ind = ind;
|
|
31
30
|
position.lin = lin;
|
|
32
31
|
position.col = col == 0 ? 1 : col;
|
|
@@ -223,8 +222,10 @@ function* tokenize(stream) {
|
|
|
223
222
|
// EOF
|
|
224
223
|
if (!(value = next())) {
|
|
225
224
|
// end of stream ignore \\
|
|
226
|
-
|
|
227
|
-
|
|
225
|
+
if (buffer.length > 0) {
|
|
226
|
+
yield pushToken(buffer);
|
|
227
|
+
buffer = '';
|
|
228
|
+
}
|
|
228
229
|
break;
|
|
229
230
|
}
|
|
230
231
|
buffer += prev() + value;
|
|
@@ -233,29 +234,51 @@ function* tokenize(stream) {
|
|
|
233
234
|
case "'":
|
|
234
235
|
yield* consumeString(value);
|
|
235
236
|
break;
|
|
237
|
+
case '^':
|
|
236
238
|
case '~':
|
|
237
239
|
case '|':
|
|
240
|
+
case '$':
|
|
241
|
+
if (value == '|' && peek() == '|') {
|
|
242
|
+
next();
|
|
243
|
+
yield pushToken('', EnumToken.ColumnCombinatorTokenType);
|
|
244
|
+
buffer = '';
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
238
247
|
if (buffer.length > 0) {
|
|
239
248
|
yield pushToken(buffer);
|
|
240
249
|
buffer = '';
|
|
241
250
|
}
|
|
242
251
|
buffer += value;
|
|
243
|
-
if (!(value =
|
|
252
|
+
if (!(value = peek())) {
|
|
244
253
|
yield pushToken(buffer);
|
|
245
254
|
buffer = '';
|
|
246
255
|
break;
|
|
247
256
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
257
|
+
// ~=
|
|
258
|
+
// ^=
|
|
259
|
+
// $=
|
|
260
|
+
// |=
|
|
261
|
+
if (peek() == '=') {
|
|
262
|
+
next();
|
|
263
|
+
switch (buffer.charAt(0)) {
|
|
264
|
+
case '~':
|
|
265
|
+
yield pushToken(buffer, EnumToken.IncludeMatchTokenType);
|
|
266
|
+
break;
|
|
267
|
+
case '^':
|
|
268
|
+
yield pushToken(buffer, EnumToken.StartMatchTokenType);
|
|
269
|
+
break;
|
|
270
|
+
case '$':
|
|
271
|
+
yield pushToken(buffer, EnumToken.EndMatchTokenType);
|
|
272
|
+
break;
|
|
273
|
+
case '|':
|
|
274
|
+
yield pushToken(buffer, EnumToken.DashMatchTokenType);
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
251
277
|
buffer = '';
|
|
252
278
|
break;
|
|
253
279
|
}
|
|
254
280
|
yield pushToken(buffer);
|
|
255
|
-
|
|
256
|
-
value = next();
|
|
257
|
-
}
|
|
258
|
-
buffer = value;
|
|
281
|
+
buffer = '';
|
|
259
282
|
break;
|
|
260
283
|
case '>':
|
|
261
284
|
if (buffer !== '') {
|
|
@@ -289,7 +312,13 @@ function* tokenize(stream) {
|
|
|
289
312
|
yield pushToken(buffer);
|
|
290
313
|
buffer = '';
|
|
291
314
|
}
|
|
292
|
-
|
|
315
|
+
const val = peek();
|
|
316
|
+
if (val == '=') {
|
|
317
|
+
next();
|
|
318
|
+
yield pushToken(value + val, EnumToken.ContainMatchTokenType);
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
if (value == ':' && ':' == val) {
|
|
293
322
|
buffer += value + next();
|
|
294
323
|
break;
|
|
295
324
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { EnumToken } from '../../ast/types.js';
|
|
2
|
+
import '../../ast/minify.js';
|
|
3
|
+
import { walkValues } from '../../ast/walk.js';
|
|
4
|
+
import '../parse.js';
|
|
5
|
+
import { isWhiteSpace } from './syntax.js';
|
|
6
|
+
import '../../renderer/utils/color.js';
|
|
7
|
+
import '../../renderer/sourcemap/lib/encode.js';
|
|
8
|
+
|
|
9
|
+
function parseDeclaration(node, errors, src, position) {
|
|
10
|
+
while (node.val[0]?.typ == EnumToken.WhitespaceTokenType) {
|
|
11
|
+
node.val.shift();
|
|
12
|
+
}
|
|
13
|
+
if (node.val.filter((t) => ![EnumToken.WhitespaceTokenType, EnumToken.CommentTokenType].includes(t.typ)).length == 0) {
|
|
14
|
+
errors.push({
|
|
15
|
+
action: 'drop',
|
|
16
|
+
message: 'doParse: invalid declaration',
|
|
17
|
+
location: { src, ...position }
|
|
18
|
+
});
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
for (const { value: val, parent } of walkValues(node.val, node)) {
|
|
22
|
+
if (val.typ == EnumToken.AttrTokenType && val.chi.every((t) => [EnumToken.IdenTokenType, EnumToken.WhitespaceTokenType, EnumToken.CommentTokenType].includes(t.typ))) {
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
val.typ = EnumToken.IdenListTokenType;
|
|
25
|
+
}
|
|
26
|
+
else if (val.typ == EnumToken.StringTokenType && (node.nam == 'grid' || node.nam == 'grid-template-areas' || node.nam == 'grid-template-rows' || node.nam == 'grid-template-columns')) {
|
|
27
|
+
val.val = val.val.at(0) + parseGridTemplate(val.val.slice(1, -1)) + val.val.at(-1);
|
|
28
|
+
// @ts-ignore
|
|
29
|
+
const array = parent?.chi ?? node.val;
|
|
30
|
+
const index = array.indexOf(val);
|
|
31
|
+
if (index > 0 && array[index - 1].typ == EnumToken.WhitespaceTokenType) {
|
|
32
|
+
array.splice(index - 1, 1);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return node;
|
|
37
|
+
}
|
|
38
|
+
function parseGridTemplate(template) {
|
|
39
|
+
let result = '';
|
|
40
|
+
let buffer = '';
|
|
41
|
+
for (let i = 0; i < template.length; i++) {
|
|
42
|
+
const char = template[i];
|
|
43
|
+
if (isWhiteSpace(char.codePointAt(0))) {
|
|
44
|
+
while (i + 1 < template.length && isWhiteSpace(template[i + 1].codePointAt(0))) {
|
|
45
|
+
i++;
|
|
46
|
+
}
|
|
47
|
+
result += buffer + ' ';
|
|
48
|
+
buffer = '';
|
|
49
|
+
}
|
|
50
|
+
else if (char == '.') {
|
|
51
|
+
while (i + 1 < template.length && template[i + 1] == '.') {
|
|
52
|
+
i++;
|
|
53
|
+
}
|
|
54
|
+
if (isWhiteSpace((result.at(-1)?.codePointAt(0)))) {
|
|
55
|
+
result = result.slice(0, -1);
|
|
56
|
+
}
|
|
57
|
+
result += buffer + char;
|
|
58
|
+
buffer = '';
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
buffer += char;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return buffer.length > 0 ? result + buffer : result;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { parseDeclaration };
|
|
@@ -37,10 +37,37 @@ function isColor(token) {
|
|
|
37
37
|
// named color
|
|
38
38
|
return token.val.toLowerCase() in COLORS_NAMES;
|
|
39
39
|
}
|
|
40
|
+
let isLegacySyntax = false;
|
|
40
41
|
if (token.typ == EnumToken.FunctionTokenType && token.chi.length > 0 && colorsFunc.includes(token.val)) {
|
|
42
|
+
const keywords = ['from', 'none'];
|
|
43
|
+
if (['rgb', 'hsl', 'hwb'].includes(token.val)) {
|
|
44
|
+
keywords.push('a', ...token.val.split(''));
|
|
45
|
+
}
|
|
46
|
+
// console.debug(JSON.stringify({token}, null, 1));
|
|
41
47
|
// @ts-ignore
|
|
42
48
|
for (const v of token.chi) {
|
|
43
|
-
|
|
49
|
+
// console.debug(JSON.stringify({v}, null, 1));
|
|
50
|
+
if (v.typ == EnumToken.CommaTokenType) {
|
|
51
|
+
isLegacySyntax = true;
|
|
52
|
+
}
|
|
53
|
+
if (v.typ == EnumToken.IdenTokenType) {
|
|
54
|
+
if (!(keywords.includes(v.val) || v.val.toLowerCase() in COLORS_NAMES)) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
if (keywords.includes(v.val)) {
|
|
58
|
+
if (isLegacySyntax) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
if (v.val == 'from' && ['rgba', 'hsla'].includes(token.val)) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (v.typ == EnumToken.FunctionTokenType && (v.val == 'calc' || colorsFunc.includes(v.val))) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (![EnumToken.ColorTokenType, EnumToken.IdenTokenType, EnumToken.NumberTokenType, EnumToken.AngleTokenType, EnumToken.PercentageTokenType, EnumToken.CommaTokenType, EnumToken.WhitespaceTokenType, EnumToken.LiteralTokenType].includes(v.typ)) {
|
|
44
71
|
return false;
|
|
45
72
|
}
|
|
46
73
|
}
|
|
@@ -200,6 +227,9 @@ function isDimension(name) {
|
|
|
200
227
|
function isPercentage(name) {
|
|
201
228
|
return name.endsWith('%') && isNumber(name.slice(0, -1));
|
|
202
229
|
}
|
|
230
|
+
function isFlex(name) {
|
|
231
|
+
return name.endsWith('fr') && isNumber(name.slice(0, -2));
|
|
232
|
+
}
|
|
203
233
|
function parseDimension(name) {
|
|
204
234
|
let index = name.length;
|
|
205
235
|
while (index--) {
|
|
@@ -267,4 +297,4 @@ function isWhiteSpace(codepoint) {
|
|
|
267
297
|
codepoint == 0xa || codepoint == 0xc || codepoint == 0xd;
|
|
268
298
|
}
|
|
269
299
|
|
|
270
|
-
export { isAngle, isAtKeyword, isColor, isDigit, isDimension, isFrequency, isFunction, isHash, isHexColor, isIdent, isIdentCodepoint, isIdentStart, isLength, isNewLine, isNonPrintable, isNumber, isPercentage, isPseudo, isResolution, isTime, isWhiteSpace, parseDimension };
|
|
300
|
+
export { isAngle, isAtKeyword, isColor, isDigit, isDimension, isFlex, isFrequency, isFunction, isHash, isHexColor, isIdent, isIdentCodepoint, isIdentStart, isLength, isNewLine, isNonPrintable, isNumber, isPercentage, isPseudo, isResolution, isTime, isWhiteSpace, parseDimension };
|
|
@@ -4,6 +4,7 @@ import '../parse.js';
|
|
|
4
4
|
import '../../renderer/utils/color.js';
|
|
5
5
|
import '../../renderer/sourcemap/lib/encode.js';
|
|
6
6
|
|
|
7
|
+
// https://www.w3.org/TR/css-values-4/#math-function
|
|
7
8
|
const funcList = ['clamp', 'calc'];
|
|
8
9
|
function matchType(val, properties) {
|
|
9
10
|
if (val.typ == EnumToken.IdenTokenType && properties.keywords.includes(val.val) ||
|
|
@@ -19,8 +20,12 @@ function matchType(val, properties) {
|
|
|
19
20
|
return typ == EnumToken.LengthTokenType || typ == EnumToken.AngleTokenType;
|
|
20
21
|
});
|
|
21
22
|
}
|
|
22
|
-
if (val.typ == EnumToken.FunctionTokenType
|
|
23
|
-
|
|
23
|
+
if (val.typ == EnumToken.FunctionTokenType) {
|
|
24
|
+
if (funcList.includes(val.val)) {
|
|
25
|
+
return val.chi.every((t => [EnumToken.LiteralTokenType, EnumToken.CommaTokenType, EnumToken.WhitespaceTokenType, EnumToken.StartParensTokenType, EnumToken.EndParensTokenType].includes(t.typ) || matchType(t, properties)));
|
|
26
|
+
}
|
|
27
|
+
// match type defined like function 'symbols()', 'url()', 'attr()' etc.
|
|
28
|
+
// return properties.types.includes((<FunctionToken>val).val + '()')
|
|
24
29
|
}
|
|
25
30
|
return false;
|
|
26
31
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { getAngle,
|
|
1
|
+
import { getAngle, clamp, COLORS_NAMES, NAMES_COLORS } from './utils/color.js';
|
|
2
|
+
import { rgb2Hex, hsl2Hex, hwb2hex, cmyk2hex } from './utils/hex.js';
|
|
2
3
|
import { EnumToken } from '../ast/types.js';
|
|
3
4
|
import '../ast/minify.js';
|
|
4
5
|
import { expand } from '../ast/expand.js';
|
|
5
6
|
import { SourceMap } from './sourcemap/sourcemap.js';
|
|
6
7
|
import '../parser/parse.js';
|
|
7
|
-
import { isNewLine } from '../parser/utils/syntax.js';
|
|
8
|
+
import { isColor, isNewLine } from '../parser/utils/syntax.js';
|
|
9
|
+
import { parseRelativeColor } from './utils/calccolor.js';
|
|
8
10
|
|
|
9
11
|
const colorsFunc = ['rgb', 'rgba', 'hsl', 'hsla', 'hwb', 'device-cmyk'];
|
|
10
12
|
function reduceNumber(val) {
|
|
11
|
-
val = (+val)
|
|
13
|
+
val = String(+val);
|
|
12
14
|
if (val === '0') {
|
|
13
15
|
return '0';
|
|
14
16
|
}
|
|
@@ -47,7 +49,7 @@ function doRender(data, options = {}) {
|
|
|
47
49
|
newLine: '\n',
|
|
48
50
|
compress: false,
|
|
49
51
|
removeComments: false,
|
|
50
|
-
}), sourcemap: false,
|
|
52
|
+
}), sourcemap: false, convertColor: true, expandNestingRules: false, preserveLicense: false, ...options
|
|
51
53
|
};
|
|
52
54
|
const startTime = performance.now();
|
|
53
55
|
const errors = [];
|
|
@@ -76,21 +78,19 @@ function doRender(data, options = {}) {
|
|
|
76
78
|
}
|
|
77
79
|
if (sourcemap != null) {
|
|
78
80
|
result.map = sourcemap.toJSON();
|
|
79
|
-
// @ts-ignore
|
|
80
|
-
// result.map.sources = result.map.sources?.map(s => <string>options?.resolve(s, <string>options?.cwd)?.relative)
|
|
81
81
|
}
|
|
82
82
|
return result;
|
|
83
83
|
}
|
|
84
84
|
function updateSourceMap(node, options, cache, sourcemap, position, str) {
|
|
85
|
-
if ([
|
|
85
|
+
if ([EnumToken.RuleNodeType, EnumToken.AtRuleNodeType].includes(node.typ)) {
|
|
86
86
|
let src = node.loc?.src ?? '';
|
|
87
87
|
let output = options.output ?? '';
|
|
88
|
-
if (src !== '') {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
88
|
+
// if (src !== '') {
|
|
89
|
+
if (!(src in cache)) {
|
|
90
|
+
// @ts-ignore
|
|
91
|
+
cache[src] = options.resolve(src, options.cwd ?? '').relative;
|
|
93
92
|
}
|
|
93
|
+
// }
|
|
94
94
|
if (!(output in cache)) {
|
|
95
95
|
// @ts-ignore
|
|
96
96
|
cache[output] = options.resolve(output, options.cwd).relative;
|
|
@@ -115,16 +115,16 @@ function renderAstNode(data, options, sourcemap, position, errors, reducer, cach
|
|
|
115
115
|
const indent = indents[level];
|
|
116
116
|
const indentSub = indents[level + 1];
|
|
117
117
|
switch (data.typ) {
|
|
118
|
-
case
|
|
118
|
+
case EnumToken.DeclarationNodeType:
|
|
119
119
|
return `${data.nam}:${options.indent}${data.val.reduce(reducer, '')}`;
|
|
120
|
-
case
|
|
121
|
-
case
|
|
120
|
+
case EnumToken.CommentNodeType:
|
|
121
|
+
case EnumToken.CDOCOMMNodeType:
|
|
122
122
|
if (data.val.startsWith('# sourceMappingURL=')) {
|
|
123
123
|
// ignore sourcemap
|
|
124
124
|
return '';
|
|
125
125
|
}
|
|
126
126
|
return !options.removeComments || (options.preserveLicense && data.val.startsWith('/*!')) ? data.val : '';
|
|
127
|
-
case
|
|
127
|
+
case EnumToken.StyleSheetNodeType:
|
|
128
128
|
return data.chi.reduce((css, node) => {
|
|
129
129
|
const str = renderAstNode(node, options, sourcemap, { ...position }, errors, reducer, cache, level, indents);
|
|
130
130
|
if (str === '') {
|
|
@@ -142,18 +142,18 @@ function renderAstNode(data, options, sourcemap, position, errors, reducer, cach
|
|
|
142
142
|
}
|
|
143
143
|
return `${css}${options.newLine}${str}`;
|
|
144
144
|
}, '');
|
|
145
|
-
case
|
|
146
|
-
case
|
|
147
|
-
if (data.typ ==
|
|
145
|
+
case EnumToken.AtRuleNodeType:
|
|
146
|
+
case EnumToken.RuleNodeType:
|
|
147
|
+
if (data.typ == EnumToken.AtRuleNodeType && !('chi' in data)) {
|
|
148
148
|
return `${indent}@${data.nam}${data.val === '' ? '' : options.indent || ' '}${data.val};`;
|
|
149
149
|
}
|
|
150
150
|
// @ts-ignore
|
|
151
151
|
let children = data.chi.reduce((css, node) => {
|
|
152
152
|
let str;
|
|
153
|
-
if (node.typ ==
|
|
153
|
+
if (node.typ == EnumToken.CommentNodeType) {
|
|
154
154
|
str = options.removeComments && (!options.preserveLicense || !node.val.startsWith('/*!')) ? '' : node.val;
|
|
155
155
|
}
|
|
156
|
-
else if (node.typ ==
|
|
156
|
+
else if (node.typ == EnumToken.DeclarationNodeType) {
|
|
157
157
|
if (node.val.length == 0) {
|
|
158
158
|
// @ts-ignore
|
|
159
159
|
errors.push({
|
|
@@ -165,7 +165,7 @@ function renderAstNode(data, options, sourcemap, position, errors, reducer, cach
|
|
|
165
165
|
}
|
|
166
166
|
str = `${node.nam}:${options.indent}${node.val.reduce(reducer, '').trimEnd()};`;
|
|
167
167
|
}
|
|
168
|
-
else if (node.typ ==
|
|
168
|
+
else if (node.typ == EnumToken.AtRuleNodeType && !('chi' in node)) {
|
|
169
169
|
str = `${data.val === '' ? '' : options.indent || ' '}${data.val};`;
|
|
170
170
|
}
|
|
171
171
|
else {
|
|
@@ -182,7 +182,7 @@ function renderAstNode(data, options, sourcemap, position, errors, reducer, cach
|
|
|
182
182
|
if (children.endsWith(';')) {
|
|
183
183
|
children = children.slice(0, -1);
|
|
184
184
|
}
|
|
185
|
-
if (data.typ ==
|
|
185
|
+
if (data.typ == EnumToken.AtRuleNodeType) {
|
|
186
186
|
return `@${data.nam}${data.val === '' ? '' : options.indent || ' '}${data.val}${options.indent}{${options.newLine}` + (children === '' ? '' : indentSub + children + options.newLine) + indent + `}`;
|
|
187
187
|
}
|
|
188
188
|
return data.sel + `${options.indent}{${options.newLine}` + (children === '' ? '' : indentSub + children + options.newLine) + indent + `}`;
|
|
@@ -201,11 +201,52 @@ function renderToken(token, options = {}, cache = Object.create(null), reducer,
|
|
|
201
201
|
return acc + renderToken(curr, options, cache, reducer, errors);
|
|
202
202
|
};
|
|
203
203
|
}
|
|
204
|
+
if (token.typ == EnumToken.FunctionTokenType && colorsFunc.includes(token.val)) {
|
|
205
|
+
if (isColor(token)) {
|
|
206
|
+
// @ts-ignore
|
|
207
|
+
token.typ = EnumToken.ColorTokenType;
|
|
208
|
+
if (token.chi[0].typ == EnumToken.IdenTokenType && token.chi[0].val == 'from') {
|
|
209
|
+
// @ts-ignore
|
|
210
|
+
token.cal = 'rel';
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
token.chi = token.chi.filter((t) => ![EnumToken.WhitespaceTokenType, EnumToken.CommaTokenType, EnumToken.CommentTokenType].includes(t.typ));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
204
217
|
switch (token.typ) {
|
|
205
218
|
case EnumToken.ListToken:
|
|
206
219
|
return token.chi.reduce((acc, curr) => acc + renderToken(curr, options, cache), '');
|
|
207
220
|
case EnumToken.BinaryExpressionTokenType:
|
|
221
|
+
if ([EnumToken.Mul, EnumToken.Div].includes(token.op)) {
|
|
222
|
+
let result = '';
|
|
223
|
+
if (token.l.typ == EnumToken.BinaryExpressionTokenType &&
|
|
224
|
+
[EnumToken.Add, EnumToken.Sub].includes(token.l.op)) {
|
|
225
|
+
result = '(' + renderToken(token.l, options, cache) + ')';
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
result = renderToken(token.l, options, cache);
|
|
229
|
+
}
|
|
230
|
+
result += token.op == EnumToken.Mul ? '*' : '/';
|
|
231
|
+
if (token.r.typ == EnumToken.BinaryExpressionTokenType &&
|
|
232
|
+
[EnumToken.Add, EnumToken.Sub].includes(token.r.op)) {
|
|
233
|
+
result += '(' + renderToken(token.r, options, cache) + ')';
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
result += renderToken(token.r, options, cache);
|
|
237
|
+
}
|
|
238
|
+
return result;
|
|
239
|
+
}
|
|
208
240
|
return renderToken(token.l, options, cache) + (token.op == EnumToken.Add ? ' + ' : (token.op == EnumToken.Sub ? ' - ' : (token.op == EnumToken.Mul ? '*' : '/'))) + renderToken(token.r, options, cache);
|
|
241
|
+
case EnumToken.FractionTokenType:
|
|
242
|
+
const fraction = renderToken(token.l) + '/' + renderToken(token.r);
|
|
243
|
+
if (+token.r.val != 0) {
|
|
244
|
+
const value = reduceNumber(+token.l.val / +token.r.val);
|
|
245
|
+
if (value.length <= fraction.length) {
|
|
246
|
+
return value;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return fraction;
|
|
209
250
|
case EnumToken.Add:
|
|
210
251
|
return ' + ';
|
|
211
252
|
case EnumToken.Sub:
|
|
@@ -215,10 +256,46 @@ function renderToken(token, options = {}, cache = Object.create(null), reducer,
|
|
|
215
256
|
case EnumToken.Div:
|
|
216
257
|
return '/';
|
|
217
258
|
case EnumToken.ColorTokenType:
|
|
218
|
-
if (options.
|
|
219
|
-
if (token.
|
|
259
|
+
if (options.convertColor) {
|
|
260
|
+
if (token.cal == 'rel' && ['rgb', 'hsl', 'hwb'].includes(token.val)) {
|
|
261
|
+
const chi = token.chi.filter(x => ![
|
|
262
|
+
EnumToken.LiteralTokenType, EnumToken.CommaTokenType, EnumToken.WhitespaceTokenType, EnumToken.CommentTokenType
|
|
263
|
+
].includes(x.typ));
|
|
264
|
+
const components = parseRelativeColor(token.val.split(''), chi[1], chi[2], chi[3], chi[4], chi[5]);
|
|
265
|
+
if (components != null) {
|
|
266
|
+
token.chi = Object.values(components);
|
|
267
|
+
delete token.cal;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (token.cal) {
|
|
271
|
+
let slice = false;
|
|
272
|
+
if (token.cal == 'rel') {
|
|
273
|
+
const last = token.chi.at(-1);
|
|
274
|
+
if ((last.typ == EnumToken.NumberTokenType && last.val == '1') || (last.typ == EnumToken.IdenTokenType && last.val == 'none')) {
|
|
275
|
+
const prev = token.chi.at(-2);
|
|
276
|
+
if (prev.typ == EnumToken.LiteralTokenType && prev.val == '/') {
|
|
277
|
+
slice = true;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return clamp(token).val + '(' + (slice ? token.chi.slice(0, -2) : token.chi).reduce((acc, curr) => {
|
|
282
|
+
const val = renderToken(curr, options, cache);
|
|
283
|
+
if ([EnumToken.LiteralTokenType, EnumToken.CommaTokenType].includes(curr.typ)) {
|
|
284
|
+
return acc + val;
|
|
285
|
+
}
|
|
286
|
+
if (acc.length > 0) {
|
|
287
|
+
return acc + (['/', ','].includes(acc.at(-1)) ? '' : ' ') + val;
|
|
288
|
+
}
|
|
289
|
+
return val;
|
|
290
|
+
}, '') + ')';
|
|
291
|
+
}
|
|
292
|
+
if (token.kin == 'lit' && token.val.localeCompare('currentcolor', undefined, { sensitivity: 'base' }) == 0) {
|
|
220
293
|
return 'currentcolor';
|
|
221
294
|
}
|
|
295
|
+
clamp(token);
|
|
296
|
+
if (Array.isArray(token.chi) && token.chi.some((t) => t.typ == EnumToken.FunctionTokenType || (t.typ == EnumToken.ColorTokenType && Array.isArray(t.chi)))) {
|
|
297
|
+
return (token.val.endsWith('a') ? token.val.slice(0, -1) : token.val) + '(' + token.chi.reduce((acc, curr) => acc + (acc.length > 0 && !(acc.endsWith('/') || curr.typ == EnumToken.LiteralTokenType) ? ' ' : '') + renderToken(curr, options, cache), '') + ')';
|
|
298
|
+
}
|
|
222
299
|
let value = token.kin == 'hex' ? token.val.toLowerCase() : (token.kin == 'lit' ? COLORS_NAMES[token.val.toLowerCase()] : '');
|
|
223
300
|
if (token.val == 'rgb' || token.val == 'rgba') {
|
|
224
301
|
value = rgb2Hex(token);
|
|
@@ -255,24 +332,52 @@ function renderToken(token, options = {}, cache = Object.create(null), reducer,
|
|
|
255
332
|
if (token.kin == 'hex' || token.kin == 'lit') {
|
|
256
333
|
return token.val;
|
|
257
334
|
}
|
|
335
|
+
if (Array.isArray(token.chi)) {
|
|
336
|
+
return (token.val.endsWith('a') ? token.val.slice(0, -1) : token.val) + '(' + token.chi.reduce((acc, curr) => acc + (acc.length > 0 && !(acc.endsWith('/') || curr.typ == EnumToken.LiteralTokenType) ? ' ' : '') + renderToken(curr, options, cache), '') + ')';
|
|
337
|
+
}
|
|
258
338
|
case EnumToken.ParensTokenType:
|
|
259
339
|
case EnumToken.FunctionTokenType:
|
|
260
340
|
case EnumToken.UrlFunctionTokenType:
|
|
341
|
+
case EnumToken.ImageFunctionTokenType:
|
|
342
|
+
case EnumToken.TimingFunctionTokenType:
|
|
261
343
|
case EnumToken.PseudoClassFuncTokenType:
|
|
344
|
+
case EnumToken.TimelineFunctionTokenType:
|
|
345
|
+
case EnumToken.GridTemplateFuncTokenType:
|
|
262
346
|
if (token.typ == EnumToken.FunctionTokenType &&
|
|
263
347
|
token.val == 'calc' &&
|
|
264
|
-
token.chi.length == 1
|
|
348
|
+
token.chi.length == 1 &&
|
|
349
|
+
token.chi[0].typ != EnumToken.BinaryExpressionTokenType &&
|
|
350
|
+
token.chi[0].typ != EnumToken.FractionTokenType &&
|
|
351
|
+
token.chi[0].val?.typ != EnumToken.FractionTokenType) {
|
|
265
352
|
// calc(200px) => 200px
|
|
266
353
|
return token.chi.reduce((acc, curr) => acc + renderToken(curr, options, cache, reducer), '');
|
|
267
354
|
}
|
|
268
355
|
// @ts-ignore
|
|
269
356
|
return ( /* options.minify && 'Pseudo-class-func' == token.typ && token.val.slice(0, 2) == '::' ? token.val.slice(1) :*/token.val ?? '') + '(' + token.chi.reduce(reducer, '') + ')';
|
|
357
|
+
case EnumToken.MatchExpressionTokenType:
|
|
358
|
+
return renderToken(token.l, options, cache, reducer, errors) +
|
|
359
|
+
renderToken({ typ: token.op }, options, cache, reducer, errors) +
|
|
360
|
+
renderToken(token.r, options, cache, reducer, errors) +
|
|
361
|
+
(token.attr ? ' ' + token.attr : '');
|
|
362
|
+
case EnumToken.NameSpaceAttributeTokenType:
|
|
363
|
+
return (token.l == null ? '' : renderToken(token.l, options, cache, reducer, errors) + '|') +
|
|
364
|
+
renderToken(token.r, options, cache, reducer, errors);
|
|
365
|
+
case EnumToken.BlockStartTokenType:
|
|
366
|
+
return '{';
|
|
367
|
+
case EnumToken.BlockEndTokenType:
|
|
368
|
+
return '}';
|
|
270
369
|
case EnumToken.StartParensTokenType:
|
|
271
370
|
return '(';
|
|
272
|
-
case EnumToken.
|
|
371
|
+
case EnumToken.IncludeMatchTokenType:
|
|
273
372
|
return '~=';
|
|
274
373
|
case EnumToken.DashMatchTokenType:
|
|
275
374
|
return '|=';
|
|
375
|
+
case EnumToken.StartMatchTokenType:
|
|
376
|
+
return '^=';
|
|
377
|
+
case EnumToken.EndMatchTokenType:
|
|
378
|
+
return '$=';
|
|
379
|
+
case EnumToken.ContainMatchTokenType:
|
|
380
|
+
return '*=';
|
|
276
381
|
case EnumToken.LtTokenType:
|
|
277
382
|
return '<';
|
|
278
383
|
case EnumToken.LteTokenType:
|
|
@@ -281,6 +386,8 @@ function renderToken(token, options = {}, cache = Object.create(null), reducer,
|
|
|
281
386
|
return '>';
|
|
282
387
|
case EnumToken.GteTokenType:
|
|
283
388
|
return '>=';
|
|
389
|
+
case EnumToken.ColumnCombinatorTokenType:
|
|
390
|
+
return '||';
|
|
284
391
|
case EnumToken.EndParensTokenType:
|
|
285
392
|
return ')';
|
|
286
393
|
case EnumToken.AttrStartTokenType:
|
|
@@ -298,6 +405,7 @@ function renderToken(token, options = {}, cache = Object.create(null), reducer,
|
|
|
298
405
|
case EnumToken.ImportantTokenType:
|
|
299
406
|
return '!important';
|
|
300
407
|
case EnumToken.AttrTokenType:
|
|
408
|
+
case EnumToken.IdenListTokenType:
|
|
301
409
|
return '[' + token.chi.reduce(reducer, '') + ']';
|
|
302
410
|
case EnumToken.TimeTokenType:
|
|
303
411
|
case EnumToken.AngleTokenType:
|
|
@@ -305,19 +413,9 @@ function renderToken(token, options = {}, cache = Object.create(null), reducer,
|
|
|
305
413
|
case EnumToken.DimensionTokenType:
|
|
306
414
|
case EnumToken.FrequencyTokenType:
|
|
307
415
|
case EnumToken.ResolutionTokenType:
|
|
308
|
-
|
|
309
|
-
const result = renderToken(token.val, options, cache);
|
|
310
|
-
if (!('unit' in token)) {
|
|
311
|
-
return result;
|
|
312
|
-
}
|
|
313
|
-
if (!result.includes(' ')) {
|
|
314
|
-
return result + token.unit;
|
|
315
|
-
}
|
|
316
|
-
return `(${result})*1${token.unit}`;
|
|
317
|
-
}
|
|
318
|
-
let val = reduceNumber(token.val);
|
|
416
|
+
let val = token.val.typ == EnumToken.FractionTokenType ? renderToken(token.val, options, cache) : reduceNumber(token.val);
|
|
319
417
|
let unit = token.unit;
|
|
320
|
-
if (token.typ == EnumToken.AngleTokenType) {
|
|
418
|
+
if (token.typ == EnumToken.AngleTokenType && !val.includes('/')) {
|
|
321
419
|
const angle = getAngle(token);
|
|
322
420
|
let v;
|
|
323
421
|
let value = val + unit;
|
|
@@ -374,16 +472,34 @@ function renderToken(token, options = {}, cache = Object.create(null), reducer,
|
|
|
374
472
|
}
|
|
375
473
|
return '0';
|
|
376
474
|
}
|
|
377
|
-
|
|
475
|
+
if (token.typ == EnumToken.TimeTokenType) {
|
|
476
|
+
if (unit == 'ms') {
|
|
477
|
+
// @ts-ignore
|
|
478
|
+
const v = reduceNumber(val / 1000);
|
|
479
|
+
if (v.length + 1 <= val.length) {
|
|
480
|
+
return v + 's';
|
|
481
|
+
}
|
|
482
|
+
return val + 'ms';
|
|
483
|
+
}
|
|
484
|
+
return val + 's';
|
|
485
|
+
}
|
|
486
|
+
return val.includes('/') ? val.replace('/', unit + '/') : val + unit;
|
|
487
|
+
case EnumToken.FlexTokenType:
|
|
378
488
|
case EnumToken.PercentageTokenType:
|
|
379
|
-
const
|
|
380
|
-
|
|
489
|
+
const uni = token.typ == EnumToken.PercentageTokenType ? '%' : 'fr';
|
|
490
|
+
const perc = token.val.typ == EnumToken.FractionTokenType ? renderToken(token.val, options, cache) : reduceNumber(token.val);
|
|
491
|
+
return options.minify && perc == '0' ? '0' : (perc.includes('/') ? perc.replace('/', uni + '/') : perc + uni);
|
|
381
492
|
case EnumToken.NumberTokenType:
|
|
382
|
-
return reduceNumber(token.val);
|
|
493
|
+
return token.val.typ == EnumToken.FractionTokenType ? renderToken(token.val, options, cache) : reduceNumber(token.val);
|
|
383
494
|
case EnumToken.CommentTokenType:
|
|
384
495
|
if (options.removeComments && (!options.preserveLicense || !token.val.startsWith('/*!'))) {
|
|
385
496
|
return '';
|
|
386
497
|
}
|
|
498
|
+
case EnumToken.PseudoClassTokenType:
|
|
499
|
+
// https://www.w3.org/TR/selectors-4/#single-colon-pseudos
|
|
500
|
+
if (token.typ == EnumToken.PseudoClassTokenType && ['::before', '::after', '::first-line', '::first-letter'].includes(token.val)) {
|
|
501
|
+
return token.val.slice(1);
|
|
502
|
+
}
|
|
387
503
|
case EnumToken.UrlTokenTokenType:
|
|
388
504
|
if (token.typ == EnumToken.UrlTokenTokenType) {
|
|
389
505
|
if (options.output != null) {
|
|
@@ -407,13 +523,13 @@ function renderToken(token, options = {}, cache = Object.create(null), reducer,
|
|
|
407
523
|
token.val = cache[token.original];
|
|
408
524
|
}
|
|
409
525
|
}
|
|
410
|
-
case EnumToken.AtRuleTokenType:
|
|
411
526
|
case EnumToken.HashTokenType:
|
|
412
|
-
case EnumToken.PseudoClassTokenType:
|
|
413
|
-
case EnumToken.LiteralTokenType:
|
|
414
|
-
case EnumToken.StringTokenType:
|
|
415
527
|
case EnumToken.IdenTokenType:
|
|
416
528
|
case EnumToken.DelimTokenType:
|
|
529
|
+
case EnumToken.AtRuleTokenType:
|
|
530
|
+
case EnumToken.StringTokenType:
|
|
531
|
+
case EnumToken.LiteralTokenType:
|
|
532
|
+
case EnumToken.DashedIdenTokenType:
|
|
417
533
|
return /* options.minify && 'Pseudo-class' == token.typ && '::' == token.val.slice(0, 2) ? token.val.slice(1) : */ token.val;
|
|
418
534
|
}
|
|
419
535
|
errors?.push({ action: 'ignore', message: `render: unexpected token ${JSON.stringify(token, null, 1)}` });
|