@tbela99/css-parser 1.3.4 → 1.4.1
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/CHANGELOG.md +37 -0
- package/README.md +44 -31
- package/dist/config.json.js +3 -0
- package/dist/index-umd-web.js +1245 -72
- package/dist/index.cjs +1256 -72
- package/dist/index.d.ts +282 -17
- package/dist/lib/ast/features/type.js +1 -1
- package/dist/lib/ast/minify.js +1 -1
- package/dist/lib/ast/types.js +62 -3
- package/dist/lib/ast/walk.js +16 -9
- package/dist/lib/fs/resolve.js +63 -7
- package/dist/lib/parser/declaration/list.js +4 -1
- package/dist/lib/parser/parse.js +828 -13
- package/dist/lib/parser/tokenize.js +22 -7
- package/dist/lib/parser/utils/declaration.js +54 -0
- package/dist/lib/parser/utils/hash.js +86 -0
- package/dist/lib/parser/utils/text.js +8 -0
- package/dist/lib/renderer/render.js +25 -6
- package/dist/lib/syntax/color/relativecolor.js +0 -3
- package/dist/lib/validation/config.json.js +15 -3
- package/dist/lib/validation/syntax.js +6 -1
- package/dist/lib/validation/syntaxes/compound-selector.js +1 -2
- package/dist/lib/validation/syntaxes/relative-selector-list.js +2 -5
- package/dist/node.js +48 -11
- package/dist/types.d.ts +17 -0
- package/dist/types.js +20 -0
- package/dist/web.js +35 -9
- package/package.json +16 -16
|
@@ -138,23 +138,27 @@ function* consumeString(quoteStr, buffer, parseInfo) {
|
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
function match(parseInfo, input) {
|
|
141
|
-
|
|
141
|
+
const position = parseInfo.currentPosition.ind - parseInfo.offset;
|
|
142
|
+
return parseInfo.stream.slice(position + 1, position + input.length + 1) == input;
|
|
142
143
|
}
|
|
143
144
|
function peek(parseInfo, count = 1) {
|
|
144
145
|
if (count == 1) {
|
|
145
|
-
return parseInfo.stream.charAt(parseInfo.currentPosition.ind + 1);
|
|
146
|
+
return parseInfo.stream.charAt(parseInfo.currentPosition.ind - parseInfo.offset + 1);
|
|
146
147
|
}
|
|
147
|
-
|
|
148
|
+
const position = parseInfo.currentPosition.ind - parseInfo.offset;
|
|
149
|
+
return parseInfo.stream.slice(position + 1, position + count + 1);
|
|
148
150
|
}
|
|
149
151
|
function prev(parseInfo) {
|
|
150
|
-
return parseInfo.stream.charAt(parseInfo.currentPosition.ind - 1);
|
|
152
|
+
return parseInfo.offset == parseInfo.currentPosition.ind ? parseInfo.buffer.slice(-1) : parseInfo.stream.charAt(parseInfo.currentPosition.ind - parseInfo.offset - 1);
|
|
151
153
|
}
|
|
152
154
|
function next(parseInfo, count = 1) {
|
|
153
155
|
let char = '';
|
|
154
156
|
let chr = '';
|
|
155
|
-
|
|
157
|
+
let position = parseInfo.currentPosition.ind - parseInfo.offset;
|
|
158
|
+
while (count-- && (chr = parseInfo.stream.charAt(position + 1))) {
|
|
156
159
|
char += chr;
|
|
157
|
-
const codepoint = parseInfo.stream.charCodeAt(++
|
|
160
|
+
const codepoint = parseInfo.stream.charCodeAt(++position);
|
|
161
|
+
++parseInfo.currentPosition.ind;
|
|
158
162
|
if (isNewLine(codepoint)) {
|
|
159
163
|
parseInfo.currentPosition.lin++;
|
|
160
164
|
parseInfo.currentPosition.col = 0;
|
|
@@ -570,6 +574,7 @@ async function* tokenizeStream(input) {
|
|
|
570
574
|
const parseInfo = {
|
|
571
575
|
stream: '',
|
|
572
576
|
buffer: '',
|
|
577
|
+
offset: 0,
|
|
573
578
|
position: { ind: 0, lin: 1, col: 1 },
|
|
574
579
|
currentPosition: { ind: -1, lin: 1, col: 0 }
|
|
575
580
|
};
|
|
@@ -577,7 +582,17 @@ async function* tokenizeStream(input) {
|
|
|
577
582
|
const reader = input.getReader();
|
|
578
583
|
while (true) {
|
|
579
584
|
const { done, value } = await reader.read();
|
|
580
|
-
|
|
585
|
+
const stream = ArrayBuffer.isView(value) ? decoder.decode(value, { stream: true }) : value;
|
|
586
|
+
if (!done) {
|
|
587
|
+
if (parseInfo.stream.length > 2) {
|
|
588
|
+
parseInfo.stream = parseInfo.stream.slice(-2) + stream;
|
|
589
|
+
parseInfo.offset = parseInfo.currentPosition.ind - 1;
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
parseInfo.stream = stream;
|
|
593
|
+
parseInfo.offset = Math.max(0, parseInfo.currentPosition.ind);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
581
596
|
yield* tokenize(parseInfo, done);
|
|
582
597
|
if (done) {
|
|
583
598
|
break;
|
|
@@ -20,6 +20,60 @@ function parseDeclarationNode(node, errors, location) {
|
|
|
20
20
|
});
|
|
21
21
|
return null;
|
|
22
22
|
}
|
|
23
|
+
if ('composes' == node.nam.toLowerCase()) {
|
|
24
|
+
let left = [];
|
|
25
|
+
let right = [];
|
|
26
|
+
let current = 0;
|
|
27
|
+
let hasFrom = 0;
|
|
28
|
+
for (; current < node.val.length; current++) {
|
|
29
|
+
if (EnumToken.WhitespaceTokenType == node.val[current].typ || EnumToken.CommentTokenType == node.val[current].typ) {
|
|
30
|
+
if (!hasFrom) {
|
|
31
|
+
left.push(node.val[current]);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
right.push(node.val[current]);
|
|
35
|
+
}
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (EnumToken.IdenTokenType == node.val[current].typ || EnumToken.DashedIdenTokenType == node.val[current].typ || EnumToken.StringTokenType == node.val[current].typ) {
|
|
39
|
+
if (EnumToken.IdenTokenType == node.val[current].typ) {
|
|
40
|
+
if ('from' == node.val[current].val) {
|
|
41
|
+
if (hasFrom) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
hasFrom++;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (hasFrom) {
|
|
49
|
+
right.push(node.val[current]);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
left.push(node.val[current]);
|
|
53
|
+
}
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
if (hasFrom <= 1 && current > 0) {
|
|
59
|
+
if (hasFrom == 0) {
|
|
60
|
+
node.val.splice(0, left.length, {
|
|
61
|
+
typ: EnumToken.ComposesSelectorNodeType,
|
|
62
|
+
l: left,
|
|
63
|
+
r: null
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
node.val.splice(0, current, {
|
|
68
|
+
typ: EnumToken.ComposesSelectorNodeType,
|
|
69
|
+
l: left,
|
|
70
|
+
r: right.reduce((a, b) => {
|
|
71
|
+
return a == null ? b : b.typ == EnumToken.WhitespaceTokenType || b.typ == EnumToken.CommentTokenType ? a : b;
|
|
72
|
+
}, null)
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
23
77
|
for (const { value: val, parent } of walkValues(node.val, node)) {
|
|
24
78
|
if (val.typ == EnumToken.AttrTokenType && val.chi.every((t) => [EnumToken.IdenTokenType, EnumToken.WhitespaceTokenType, EnumToken.CommentTokenType].includes(t.typ))) {
|
|
25
79
|
// @ts-ignore
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Alphabet: a-z, A-Z, 0-9, _, -
|
|
2
|
+
const LOWER = "abcdefghijklmnopqrstuvwxyz";
|
|
3
|
+
const DIGITS = "0123456789";
|
|
4
|
+
const FULL_ALPHABET = (LOWER + DIGITS).split(""); // 64 chars
|
|
5
|
+
const FIRST_ALPHABET = (LOWER).split(""); // 54 chars (no digits)
|
|
6
|
+
/**
|
|
7
|
+
* supported hash algorithms
|
|
8
|
+
*/
|
|
9
|
+
const hashAlgorithms = ['hex', 'base64', 'base64url', 'sha1', 'sha256', 'sha384', 'sha512'];
|
|
10
|
+
// simple deterministic hash → number
|
|
11
|
+
function hashCode(str) {
|
|
12
|
+
let hash = 0;
|
|
13
|
+
let l = str.length;
|
|
14
|
+
let i = 0;
|
|
15
|
+
while (i < l) {
|
|
16
|
+
hash = (hash * 31 + str.charCodeAt(i++)) >>> 0;
|
|
17
|
+
}
|
|
18
|
+
return hash;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* generate a hash id
|
|
22
|
+
* @param input
|
|
23
|
+
* @param length
|
|
24
|
+
*/
|
|
25
|
+
function hashId(input, length = 6) {
|
|
26
|
+
let n = hashCode(input);
|
|
27
|
+
const chars = [];
|
|
28
|
+
// First character: must not be a digit
|
|
29
|
+
chars.push(FIRST_ALPHABET[n % FIRST_ALPHABET.length]);
|
|
30
|
+
// Remaining characters
|
|
31
|
+
for (let i = 1; i < length; i++) {
|
|
32
|
+
n = (n + chars.length + i) % FULL_ALPHABET.length;
|
|
33
|
+
chars.push(FULL_ALPHABET[n]);
|
|
34
|
+
}
|
|
35
|
+
return chars.join("");
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* convert input to hex
|
|
39
|
+
* @param input
|
|
40
|
+
*/
|
|
41
|
+
function toHex(input) {
|
|
42
|
+
let result = '';
|
|
43
|
+
if (input instanceof ArrayBuffer || ArrayBuffer.isView(input)) {
|
|
44
|
+
for (const byte of Array.from(new Uint8Array(input))) {
|
|
45
|
+
result += byte.toString(16).padStart(2, '0');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
for (const char of String(input)) {
|
|
50
|
+
result += char.charCodeAt(0).toString(16).padStart(2, '0');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* generate a hash
|
|
57
|
+
* @param input
|
|
58
|
+
* @param length
|
|
59
|
+
* @param algo
|
|
60
|
+
*/
|
|
61
|
+
async function hash(input, length = 6, algo) {
|
|
62
|
+
let result;
|
|
63
|
+
if (algo != null) {
|
|
64
|
+
switch (algo) {
|
|
65
|
+
case 'hex':
|
|
66
|
+
return toHex(input).slice(0, length);
|
|
67
|
+
case 'base64':
|
|
68
|
+
case 'base64url':
|
|
69
|
+
result = btoa(input);
|
|
70
|
+
if (algo == 'base64url') {
|
|
71
|
+
result = result.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
72
|
+
}
|
|
73
|
+
return result.slice(0, length);
|
|
74
|
+
case 'sha1':
|
|
75
|
+
case 'sha256':
|
|
76
|
+
case 'sha384':
|
|
77
|
+
case 'sha512':
|
|
78
|
+
return toHex(await crypto.subtle.digest(algo.replace('sha', 'SHA-'), new TextEncoder().encode(input))).slice(0, length);
|
|
79
|
+
default:
|
|
80
|
+
throw new Error(`Unsupported hash algorithm: ${algo}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return hashId(input, length);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export { hash, hashAlgorithms, hashId };
|
|
@@ -29,9 +29,10 @@ function update(position, str) {
|
|
|
29
29
|
* render ast
|
|
30
30
|
* @param data
|
|
31
31
|
* @param options
|
|
32
|
+
* @param mapping
|
|
32
33
|
* @private
|
|
33
34
|
*/
|
|
34
|
-
function doRender(data, options = {}) {
|
|
35
|
+
function doRender(data, options = {}, mapping) {
|
|
35
36
|
const minify = options.minify ?? true;
|
|
36
37
|
const beautify = options.beautify ?? !minify;
|
|
37
38
|
options = {
|
|
@@ -68,12 +69,23 @@ function doRender(data, options = {}) {
|
|
|
68
69
|
const errors = [];
|
|
69
70
|
const sourcemap = options.sourcemap ? new SourceMap : null;
|
|
70
71
|
const cache = Object.create(null);
|
|
72
|
+
const position = {
|
|
73
|
+
ind: 0,
|
|
74
|
+
lin: 1,
|
|
75
|
+
col: 1
|
|
76
|
+
};
|
|
77
|
+
let code = '';
|
|
78
|
+
if (mapping != null) {
|
|
79
|
+
if (mapping.importMapping != null) {
|
|
80
|
+
for (const [key, value] of Object.entries(mapping.importMapping)) {
|
|
81
|
+
code += `:import("${key}")${options.indent}{${options.newLine}${Object.entries(value).reduce((acc, [k, v]) => acc + (acc.length > 0 ? options.newLine : '') + `${options.indent}${v}:${options.indent}${k};`, '')}${options.newLine}}${options.newLine}`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
code += `:export${options.indent}{${options.newLine}${Object.entries(mapping.mapping).reduce((acc, [k, v]) => acc + (acc.length > 0 ? options.newLine : '') + `${options.indent}${k}:${options.indent}${v};`, '')}${options.newLine}}${options.newLine}`;
|
|
85
|
+
update(position, code);
|
|
86
|
+
}
|
|
71
87
|
const result = {
|
|
72
|
-
code: renderAstNode(options.expandNestingRules && [EnumToken.StyleSheetNodeType, EnumToken.AtRuleNodeType, EnumToken.RuleNodeType].includes(data.typ) && 'chi' in data ? expand(data) : data, options, sourcemap, {
|
|
73
|
-
ind: 0,
|
|
74
|
-
lin: 1,
|
|
75
|
-
col: 1
|
|
76
|
-
}, errors, function reducer(acc, curr) {
|
|
88
|
+
code: code + renderAstNode(options.expandNestingRules && [EnumToken.StyleSheetNodeType, EnumToken.AtRuleNodeType, EnumToken.RuleNodeType].includes(data.typ) && 'chi' in data ? expand(data) : data, options, sourcemap, position, errors, function reducer(acc, curr) {
|
|
77
89
|
if (curr.typ == EnumToken.CommentTokenType && options.removeComments) {
|
|
78
90
|
if (!options.preserveLicense || !curr.val.startsWith('/*!')) {
|
|
79
91
|
return acc;
|
|
@@ -232,6 +244,11 @@ function renderAstNode(data, options, sourcemap, position, errors, reducer, cach
|
|
|
232
244
|
return `@${data.nam}${data.val === '' ? '' : options.indent || ' '}${data.val}${options.indent}{${options.newLine}` + (children === '' ? '' : indentSub + children + options.newLine) + indent + `}`;
|
|
233
245
|
}
|
|
234
246
|
return data.sel + `${options.indent}{${options.newLine}` + (children === '' ? '' : indentSub + children + options.newLine) + indent + `}`;
|
|
247
|
+
case EnumToken.CssVariableTokenType:
|
|
248
|
+
case EnumToken.CssVariableImportTokenType:
|
|
249
|
+
return `@value ${data.nam}:${options.indent}${filterValues((options.minify ? data.val : data.val)).reduce(reducer, '').trim()};`;
|
|
250
|
+
case EnumToken.CssVariableDeclarationMapTokenType:
|
|
251
|
+
return `@value ${filterValues(data.vars).reduce((acc, curr) => acc + renderToken(curr), '').trim()} from ${filterValues(data.from).reduce((acc, curr) => acc + renderToken(curr), '').trim()};`;
|
|
235
252
|
case EnumToken.InvalidDeclarationNodeType:
|
|
236
253
|
case EnumToken.InvalidRuleTokenType:
|
|
237
254
|
case EnumToken.InvalidAtRuleTokenType:
|
|
@@ -386,6 +403,8 @@ function renderToken(token, options = {}, cache = Object.create(null), reducer,
|
|
|
386
403
|
case EnumToken.NameSpaceAttributeTokenType:
|
|
387
404
|
return (token.l == null ? '' : renderToken(token.l, options, cache, reducer, errors)) + '|' +
|
|
388
405
|
renderToken(token.r, options, cache, reducer, errors);
|
|
406
|
+
case EnumToken.ComposesSelectorNodeType:
|
|
407
|
+
return token.l.reduce((acc, curr) => acc + renderToken(curr, options, cache), '') + (token.r == null ? '' : ' from ' + renderToken(token.r, options, cache, reducer, errors));
|
|
389
408
|
case EnumToken.BlockStartTokenType:
|
|
390
409
|
return '{';
|
|
391
410
|
case EnumToken.BlockEndTokenType:
|
|
@@ -77,10 +77,7 @@ function computeComponentValue(expr, converted, values) {
|
|
|
77
77
|
// normalize hue
|
|
78
78
|
for (const k of walkValues([object.h])) {
|
|
79
79
|
if (k.value.typ == EnumToken.AngleTokenType && k.value.unit == 'deg') {
|
|
80
|
-
// @ts-ignore
|
|
81
80
|
k.value.typ = EnumToken.NumberTokenType;
|
|
82
|
-
// @ts-ignore
|
|
83
|
-
delete k.value.unit;
|
|
84
81
|
}
|
|
85
82
|
}
|
|
86
83
|
}
|
|
@@ -753,7 +753,7 @@ var declarations = {
|
|
|
753
753
|
syntax: "[ <counter-name> <integer>? ]+ | none"
|
|
754
754
|
},
|
|
755
755
|
cursor: {
|
|
756
|
-
syntax: "[ [ <url> [ <x> <y> ]? , ]*
|
|
756
|
+
syntax: "[ [ <url> [ <x> <y> ]? , ]* <cursor-predefined> ] [ [ <url> [ <x> <y> ]? , ]* [ auto | default | none | context-menu | help | pointer | progress | wait | cell | crosshair | text | vertical-text | alias | copy | move | no-drop | not-allowed | e-resize | n-resize | ne-resize | nw-resize | s-resize | se-resize | sw-resize | w-resize | ew-resize | ns-resize | nesw-resize | nwse-resize | col-resize | row-resize | all-scroll | zoom-in | zoom-out | grab | grabbing | hand | -webkit-grab | -webkit-grabbing | -webkit-zoom-in | -webkit-zoom-out | -moz-grab | -moz-grabbing | -moz-zoom-in | -moz-zoom-out ] ]"
|
|
757
757
|
},
|
|
758
758
|
cx: {
|
|
759
759
|
syntax: "<length> | <percentage>"
|
|
@@ -1981,6 +1981,12 @@ var declarations = {
|
|
|
1981
1981
|
},
|
|
1982
1982
|
"white-space-trim": {
|
|
1983
1983
|
syntax: "none | discard-before || discard-after || discard-inner"
|
|
1984
|
+
},
|
|
1985
|
+
composes: {
|
|
1986
|
+
syntax: "<composes-selector>#"
|
|
1987
|
+
},
|
|
1988
|
+
"composes-selector": {
|
|
1989
|
+
syntax: "<ident>+ [from [global&&<string>]]?"
|
|
1984
1990
|
}
|
|
1985
1991
|
};
|
|
1986
1992
|
var functions = {
|
|
@@ -2006,7 +2012,7 @@ var functions = {
|
|
|
2006
2012
|
syntax: "atan2( <calc-sum>, <calc-sum> )"
|
|
2007
2013
|
},
|
|
2008
2014
|
attr: {
|
|
2009
|
-
syntax: "attr( <attr-name> <type
|
|
2015
|
+
syntax: "attr( <attr-name> <attr-type>? , <declaration-value>? )"
|
|
2010
2016
|
},
|
|
2011
2017
|
blur: {
|
|
2012
2018
|
syntax: "blur( <length>? )"
|
|
@@ -2359,7 +2365,7 @@ var syntaxes = {
|
|
|
2359
2365
|
syntax: "scroll | fixed | local"
|
|
2360
2366
|
},
|
|
2361
2367
|
"attr()": {
|
|
2362
|
-
syntax: "attr( <attr-name> <type
|
|
2368
|
+
syntax: "attr( <attr-name> <attr-type>? , <declaration-value>? )"
|
|
2363
2369
|
},
|
|
2364
2370
|
"attr-matcher": {
|
|
2365
2371
|
syntax: "[ '~' | '|' | '^' | '$' | '*' ]? '='"
|
|
@@ -2367,6 +2373,9 @@ var syntaxes = {
|
|
|
2367
2373
|
"attr-modifier": {
|
|
2368
2374
|
syntax: "i | s"
|
|
2369
2375
|
},
|
|
2376
|
+
"attr-type": {
|
|
2377
|
+
syntax: "type( <syntax> ) | raw-string | number | <attr-unit>"
|
|
2378
|
+
},
|
|
2370
2379
|
"attribute-selector": {
|
|
2371
2380
|
syntax: "'[' <wq-name> ']' | '[' <wq-name> <attr-matcher> [ <string-token> | <ident-token> ] <attr-modifier>? ']'"
|
|
2372
2381
|
},
|
|
@@ -2580,6 +2589,9 @@ var syntaxes = {
|
|
|
2580
2589
|
"cubic-bezier-easing-function": {
|
|
2581
2590
|
syntax: "ease | ease-in | ease-out | ease-in-out | cubic-bezier( <number [0,1]> , <number> , <number [0,1]> , <number> )"
|
|
2582
2591
|
},
|
|
2592
|
+
"cursor-predefined": {
|
|
2593
|
+
syntax: "auto | default | none | context-menu | help | pointer | progress | wait | cell | crosshair | text | vertical-text | alias | copy | move | no-drop | not-allowed | e-resize | n-resize | ne-resize | nw-resize | s-resize | se-resize | sw-resize | w-resize | ew-resize | ns-resize | nesw-resize | nwse-resize | col-resize | row-resize | all-scroll | zoom-in | zoom-out | grab | grabbing"
|
|
2594
|
+
},
|
|
2583
2595
|
"custom-color-space": {
|
|
2584
2596
|
syntax: "<dashed-ident>"
|
|
2585
2597
|
},
|
|
@@ -11,6 +11,7 @@ import '../renderer/sourcemap/lib/encode.js';
|
|
|
11
11
|
import { getSyntaxConfig, getParsedSyntax, getSyntax } from './config.js';
|
|
12
12
|
import './syntaxes/complex-selector.js';
|
|
13
13
|
import { funcLike, colorsFunc } from '../syntax/color/utils/constants.js';
|
|
14
|
+
import '../../types.js';
|
|
14
15
|
import '../ast/features/type.js';
|
|
15
16
|
|
|
16
17
|
const config = getSyntaxConfig();
|
|
@@ -615,6 +616,7 @@ function matchPropertyType(syntax, context, options) {
|
|
|
615
616
|
'color',
|
|
616
617
|
'integer',
|
|
617
618
|
'bg-position',
|
|
619
|
+
'composes-selector',
|
|
618
620
|
'length-percentage', 'flex', 'calc-sum', 'color',
|
|
619
621
|
'color-base', 'system-color', 'deprecated-system-color',
|
|
620
622
|
'pseudo-class-selector', 'pseudo-element-selector', 'feature-value-declaration'
|
|
@@ -645,6 +647,9 @@ function matchPropertyType(syntax, context, options) {
|
|
|
645
647
|
return { ...result, context };
|
|
646
648
|
}
|
|
647
649
|
switch (syntax.val) {
|
|
650
|
+
case 'composes-selector':
|
|
651
|
+
success = token.typ == EnumToken.ComposesSelectorNodeType;
|
|
652
|
+
break;
|
|
648
653
|
case 'bg-position': {
|
|
649
654
|
let val;
|
|
650
655
|
let keyworkMatchCount = 0;
|
|
@@ -812,7 +817,7 @@ function matchPropertyType(syntax, context, options) {
|
|
|
812
817
|
}
|
|
813
818
|
break;
|
|
814
819
|
case 'integer':
|
|
815
|
-
success = (token.typ == EnumToken.NumberTokenType &&
|
|
820
|
+
success = (token.typ == EnumToken.NumberTokenType && /^[+-]?\d+$/.test(token.val.toString())) || (token.typ == EnumToken.FunctionTokenType && mathFuncs.includes(token.val.toLowerCase()) || (token.typ == EnumToken.FunctionTokenType && wildCardFuncs.includes(token.val)));
|
|
816
821
|
if ('range' in syntax) {
|
|
817
822
|
success = success && +token.val >= +syntax.range[0] && +token.val <= +syntax.range[1];
|
|
818
823
|
}
|
|
@@ -19,15 +19,12 @@ function validateRelativeSelectorList(tokens, root, options) {
|
|
|
19
19
|
return result;
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
+
// @ts-ignore
|
|
22
23
|
return {
|
|
23
24
|
valid: SyntaxValidationResult.Valid,
|
|
24
|
-
matches: [],
|
|
25
|
-
// @ts-ignore
|
|
26
25
|
node: root,
|
|
27
|
-
// @ts-ignore
|
|
28
26
|
syntax: null,
|
|
29
|
-
error: ''
|
|
30
|
-
tokens
|
|
27
|
+
error: ''
|
|
31
28
|
};
|
|
32
29
|
}
|
|
33
30
|
|
package/dist/node.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import process from 'node:process';
|
|
2
|
-
|
|
2
|
+
import { ModuleScopeEnumOptions } from './lib/ast/types.js';
|
|
3
|
+
export { ColorType, EnumToken, ModuleCaseTransformEnum, ValidationLevel } from './lib/ast/types.js';
|
|
3
4
|
export { minify } from './lib/ast/minify.js';
|
|
4
5
|
export { WalkerEvent, WalkerOptionEnum, walk, walkValues } from './lib/ast/walk.js';
|
|
5
6
|
export { expand } from './lib/ast/expand.js';
|
|
@@ -21,34 +22,47 @@ import { resolve, matchUrl, dirname } from './lib/fs/resolve.js';
|
|
|
21
22
|
import { Readable } from 'node:stream';
|
|
22
23
|
import { createReadStream } from 'node:fs';
|
|
23
24
|
import { readFile, lstat } from 'node:fs/promises';
|
|
25
|
+
import { ResponseType } from './types.js';
|
|
24
26
|
export { FeatureWalkMode } from './lib/ast/features/type.js';
|
|
25
27
|
|
|
26
28
|
/**
|
|
27
29
|
* load file or url as stream
|
|
28
30
|
* @param url
|
|
29
31
|
* @param currentFile
|
|
30
|
-
* @param
|
|
32
|
+
* @param responseType
|
|
31
33
|
* @throws Error file not found
|
|
32
34
|
*
|
|
33
35
|
* @private
|
|
34
36
|
*/
|
|
35
|
-
async function load(url, currentFile = '.',
|
|
37
|
+
async function load(url, currentFile = '.', responseType = false) {
|
|
36
38
|
const resolved = resolve(url, currentFile);
|
|
39
|
+
if (typeof responseType == 'boolean') {
|
|
40
|
+
responseType = responseType ? ResponseType.ReadableStream : ResponseType.Text;
|
|
41
|
+
}
|
|
37
42
|
if (matchUrl.test(resolved.absolute)) {
|
|
38
43
|
return fetch(resolved.absolute).then(async (response) => {
|
|
39
44
|
if (!response.ok) {
|
|
40
45
|
throw new Error(`${response.status} ${response.statusText} ${response.url}`);
|
|
41
46
|
}
|
|
42
|
-
|
|
47
|
+
if (responseType == ResponseType.ArrayBuffer) {
|
|
48
|
+
return response.arrayBuffer();
|
|
49
|
+
}
|
|
50
|
+
return responseType == ResponseType.ReadableStream ? response.body : await response.text();
|
|
43
51
|
});
|
|
44
52
|
}
|
|
45
53
|
try {
|
|
46
|
-
if (
|
|
54
|
+
if (responseType == ResponseType.Text) {
|
|
47
55
|
return readFile(resolved.absolute, 'utf-8');
|
|
48
56
|
}
|
|
57
|
+
if (responseType == ResponseType.ArrayBuffer) {
|
|
58
|
+
return readFile(resolved.absolute).then(buffer => buffer.buffer);
|
|
59
|
+
}
|
|
49
60
|
const stats = await lstat(resolved.absolute);
|
|
50
61
|
if (stats.isFile()) {
|
|
51
|
-
return Readable.toWeb(createReadStream(resolved.absolute, {
|
|
62
|
+
return Readable.toWeb(createReadStream(resolved.absolute, {
|
|
63
|
+
encoding: 'utf-8',
|
|
64
|
+
highWaterMark: 64 * 1024
|
|
65
|
+
}));
|
|
52
66
|
}
|
|
53
67
|
}
|
|
54
68
|
catch (error) {
|
|
@@ -60,6 +74,7 @@ async function load(url, currentFile = '.', asStream = false) {
|
|
|
60
74
|
* render the ast tree
|
|
61
75
|
* @param data
|
|
62
76
|
* @param options
|
|
77
|
+
* @param mapping
|
|
63
78
|
*
|
|
64
79
|
* Example:
|
|
65
80
|
*
|
|
@@ -84,8 +99,8 @@ async function load(url, currentFile = '.', asStream = false) {
|
|
|
84
99
|
* // }
|
|
85
100
|
* ```
|
|
86
101
|
*/
|
|
87
|
-
function render(data, options = {}) {
|
|
88
|
-
return doRender(data, Object.assign(options, { resolve, dirname, cwd: options.cwd ?? process.cwd() }));
|
|
102
|
+
function render(data, options = {}, mapping) {
|
|
103
|
+
return doRender(data, Object.assign(options, { resolve, dirname, cwd: options.cwd ?? process.cwd() }), mapping);
|
|
89
104
|
}
|
|
90
105
|
/**
|
|
91
106
|
* parse css file
|
|
@@ -160,9 +175,18 @@ async function parse(stream, options = {}) {
|
|
|
160
175
|
return doParse(stream instanceof ReadableStream ? tokenizeStream(stream) : tokenize({
|
|
161
176
|
stream,
|
|
162
177
|
buffer: '',
|
|
178
|
+
offset: 0,
|
|
163
179
|
position: { ind: 0, lin: 1, col: 1 },
|
|
164
180
|
currentPosition: { ind: -1, lin: 1, col: 0 }
|
|
165
|
-
}), Object.assign(options, {
|
|
181
|
+
}), Object.assign(options, {
|
|
182
|
+
load,
|
|
183
|
+
resolve,
|
|
184
|
+
dirname,
|
|
185
|
+
cwd: options.cwd ?? process.cwd()
|
|
186
|
+
})).then(result => {
|
|
187
|
+
const { revMapping, ...res } = result;
|
|
188
|
+
return res;
|
|
189
|
+
});
|
|
166
190
|
}
|
|
167
191
|
/**
|
|
168
192
|
* transform css file
|
|
@@ -237,8 +261,21 @@ async function transform(css, options = {}) {
|
|
|
237
261
|
options = { minify: true, removeEmpty: true, removeCharset: true, ...options };
|
|
238
262
|
const startTime = performance.now();
|
|
239
263
|
return parse(css, options).then((parseResult) => {
|
|
264
|
+
let mapping = null;
|
|
265
|
+
let importMapping = null;
|
|
266
|
+
if (typeof options.module == 'number' && (options.module & ModuleScopeEnumOptions.ICSS)) {
|
|
267
|
+
mapping = parseResult.mapping;
|
|
268
|
+
importMapping = parseResult.importMapping;
|
|
269
|
+
}
|
|
270
|
+
else if (typeof options.module == 'object' && typeof options.module.scoped == 'number' && (options.module.scoped & ModuleScopeEnumOptions.ICSS)) {
|
|
271
|
+
mapping = parseResult.mapping;
|
|
272
|
+
importMapping = parseResult.importMapping;
|
|
273
|
+
}
|
|
240
274
|
// ast already expanded by parse
|
|
241
|
-
const rendered = render(parseResult.ast, {
|
|
275
|
+
const rendered = render(parseResult.ast, {
|
|
276
|
+
...options,
|
|
277
|
+
expandNestingRules: false
|
|
278
|
+
}, mapping != null ? { mapping, importMapping } : null);
|
|
242
279
|
return {
|
|
243
280
|
...parseResult,
|
|
244
281
|
...rendered,
|
|
@@ -253,4 +290,4 @@ async function transform(css, options = {}) {
|
|
|
253
290
|
});
|
|
254
291
|
}
|
|
255
292
|
|
|
256
|
-
export { dirname, load, parse, parseFile, render, resolve, transform, transformFile };
|
|
293
|
+
export { ModuleScopeEnumOptions, ResponseType, dirname, load, parse, parseFile, render, resolve, transform, transformFile };
|
package/dist/types.d.ts
ADDED
package/dist/types.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* response type
|
|
3
|
+
*/
|
|
4
|
+
var ResponseType;
|
|
5
|
+
(function (ResponseType) {
|
|
6
|
+
/**
|
|
7
|
+
* return text
|
|
8
|
+
*/
|
|
9
|
+
ResponseType[ResponseType["Text"] = 0] = "Text";
|
|
10
|
+
/**
|
|
11
|
+
* return a readable stream
|
|
12
|
+
*/
|
|
13
|
+
ResponseType[ResponseType["ReadableStream"] = 1] = "ReadableStream";
|
|
14
|
+
/**
|
|
15
|
+
* return an arraybuffer
|
|
16
|
+
*/
|
|
17
|
+
ResponseType[ResponseType["ArrayBuffer"] = 2] = "ArrayBuffer";
|
|
18
|
+
})(ResponseType || (ResponseType = {}));
|
|
19
|
+
|
|
20
|
+
export { ResponseType };
|