@tbela99/css-parser 0.0.1-alpha3

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.
@@ -0,0 +1,5 @@
1
+ import config from '../../../config.json.js';
2
+
3
+ const getConfig = () => config;
4
+
5
+ export { getConfig };
@@ -0,0 +1,13 @@
1
+ function eq(a, b) {
2
+ if ((typeof a != 'object') || typeof b != 'object') {
3
+ return a === b;
4
+ }
5
+ const k1 = Object.keys(a);
6
+ const k2 = Object.keys(b);
7
+ return k1.length == k2.length &&
8
+ k1.every((key) => {
9
+ return eq(a[key], b[key]);
10
+ });
11
+ }
12
+
13
+ export { eq };
@@ -0,0 +1,247 @@
1
+ // https://www.w3.org/TR/CSS21/syndata.html#syntax
2
+ // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-ident-token
3
+ // '\\'
4
+ const REVERSE_SOLIDUS = 0x5c;
5
+ function isLength(dimension) {
6
+ return 'unit' in dimension && [
7
+ 'q', 'cap', 'ch', 'cm', 'cqb', 'cqh', 'cqi', 'cqmax', 'cqmin', 'cqw', 'dvb',
8
+ 'dvh', 'dvi', 'dvmax', 'dvmin', 'dvw', 'em', 'ex', 'ic', 'in', 'lh', 'lvb',
9
+ 'lvh', 'lvi', 'lvmax', 'lvw', 'mm', 'pc', 'pt', 'px', 'rem', 'rlh', 'svb',
10
+ 'svh', 'svi', 'svmin', 'svw', 'vb', 'vh', 'vi', 'vmax', 'vmin', 'vw'
11
+ ].includes(dimension.unit.toLowerCase());
12
+ }
13
+ function isResolution(dimension) {
14
+ return 'unit' in dimension && ['dpi', 'dpcm', 'dppx', 'x'].includes(dimension.unit.toLowerCase());
15
+ }
16
+ function isAngle(dimension) {
17
+ return 'unit' in dimension && ['rad', 'turn', 'deg', 'grad'].includes(dimension.unit.toLowerCase());
18
+ }
19
+ function isTime(dimension) {
20
+ return 'unit' in dimension && ['ms', 's'].includes(dimension.unit.toLowerCase());
21
+ }
22
+ function isFrequency(dimension) {
23
+ return 'unit' in dimension && ['hz', 'khz'].includes(dimension.unit.toLowerCase());
24
+ }
25
+ function isLetter(codepoint) {
26
+ // lowercase
27
+ return (codepoint >= 0x61 && codepoint <= 0x7a) ||
28
+ // uppercase
29
+ (codepoint >= 0x41 && codepoint <= 0x5a);
30
+ }
31
+ function isNonAscii(codepoint) {
32
+ return codepoint >= 0x80;
33
+ }
34
+ function isIdentStart(codepoint) {
35
+ // _
36
+ return codepoint == 0x5f || isLetter(codepoint) || isNonAscii(codepoint);
37
+ }
38
+ function isDigit(codepoint) {
39
+ return codepoint >= 0x30 && codepoint <= 0x39;
40
+ }
41
+ function isIdentCodepoint(codepoint) {
42
+ // -
43
+ return codepoint == 0x2d || isDigit(codepoint) || isIdentStart(codepoint);
44
+ }
45
+ function isIdent(name) {
46
+ const j = name.length - 1;
47
+ let i = 0;
48
+ let codepoint = name.charCodeAt(0);
49
+ // -
50
+ if (codepoint == 0x2d) {
51
+ const nextCodepoint = name.charCodeAt(1);
52
+ if (nextCodepoint == null) {
53
+ return false;
54
+ }
55
+ // -
56
+ if (nextCodepoint == 0x2d) {
57
+ return true;
58
+ }
59
+ if (nextCodepoint == REVERSE_SOLIDUS) {
60
+ return name.length > 2 && !isNewLine(name.charCodeAt(2));
61
+ }
62
+ return true;
63
+ }
64
+ if (!isIdentStart(codepoint)) {
65
+ return false;
66
+ }
67
+ while (i < j) {
68
+ i += codepoint < 0x80 ? 1 : String.fromCodePoint(codepoint).length;
69
+ codepoint = name.charCodeAt(i);
70
+ if (!isIdentCodepoint(codepoint)) {
71
+ return false;
72
+ }
73
+ }
74
+ return true;
75
+ }
76
+ function isPseudo(name) {
77
+ if (name.charAt(0) != ':') {
78
+ return false;
79
+ }
80
+ if (name.endsWith('(')) {
81
+ return isIdent(name.charAt(1) == ':' ? name.slice(2, -1) : name.slice(1, -1));
82
+ }
83
+ return isIdent(name.charAt(1) == ':' ? name.slice(2) : name.slice(1));
84
+ }
85
+ function isHash(name) {
86
+ if (name.charAt(0) != '#') {
87
+ return false;
88
+ }
89
+ if (isIdent(name.charAt(1))) {
90
+ return true;
91
+ }
92
+ return true;
93
+ }
94
+ function isNumber(name) {
95
+ if (name.length == 0) {
96
+ return false;
97
+ }
98
+ let codepoint = name.charCodeAt(0);
99
+ let i = 0;
100
+ const j = name.length;
101
+ // '+' '-'
102
+ if ([0x2b, 0x2d].includes(codepoint)) {
103
+ i++;
104
+ }
105
+ // consume digits
106
+ while (i < j) {
107
+ codepoint = name.charCodeAt(i);
108
+ if (isDigit(codepoint)) {
109
+ i++;
110
+ continue;
111
+ }
112
+ // '.' 'E' 'e'
113
+ if (codepoint == 0x2e || codepoint == 0x45 || codepoint == 0x65) {
114
+ break;
115
+ }
116
+ return false;
117
+ }
118
+ // '.'
119
+ if (codepoint == 0x2e) {
120
+ if (!isDigit(name.charCodeAt(++i))) {
121
+ return false;
122
+ }
123
+ }
124
+ while (i < j) {
125
+ codepoint = name.charCodeAt(i);
126
+ if (isDigit(codepoint)) {
127
+ i++;
128
+ continue;
129
+ }
130
+ // 'E' 'e'
131
+ if (codepoint == 0x45 || codepoint == 0x65) {
132
+ i++;
133
+ break;
134
+ }
135
+ return false;
136
+ }
137
+ // 'E' 'e'
138
+ if (codepoint == 0x45 || codepoint == 0x65) {
139
+ if (i == j) {
140
+ return false;
141
+ }
142
+ codepoint = name.charCodeAt(i + 1);
143
+ // '+' '-'
144
+ if ([0x2b, 0x2d].includes(codepoint)) {
145
+ i++;
146
+ }
147
+ codepoint = name.charCodeAt(i + 1);
148
+ if (!isDigit(codepoint)) {
149
+ return false;
150
+ }
151
+ }
152
+ while (++i < j) {
153
+ codepoint = name.charCodeAt(i);
154
+ if (!isDigit(codepoint)) {
155
+ return false;
156
+ }
157
+ }
158
+ return true;
159
+ }
160
+ function isDimension(name) {
161
+ let index = 0;
162
+ while (index++ < name.length) {
163
+ if (isDigit(name.charCodeAt(name.length - index))) {
164
+ index--;
165
+ break;
166
+ }
167
+ if (index == 3) {
168
+ break;
169
+ }
170
+ }
171
+ if (index == 0 || index > 3) {
172
+ return false;
173
+ }
174
+ const number = name.slice(0, -index);
175
+ return number.length > 0 && isIdentStart(name.charCodeAt(name.length - index)) && isNumber(number);
176
+ }
177
+ function isPercentage(name) {
178
+ return name.endsWith('%') && isNumber(name.slice(0, -1));
179
+ }
180
+ function parseDimension(name) {
181
+ let index = 0;
182
+ while (index++ < name.length) {
183
+ if (isDigit(name.charCodeAt(name.length - index))) {
184
+ index--;
185
+ break;
186
+ }
187
+ if (index == 3) {
188
+ break;
189
+ }
190
+ }
191
+ const dimension = { typ: 'Dimension', val: name.slice(0, -index), unit: name.slice(-index) };
192
+ if (isAngle(dimension)) {
193
+ // @ts-ignore
194
+ dimension.typ = 'Angle';
195
+ }
196
+ else if (isLength(dimension)) {
197
+ // @ts-ignore
198
+ dimension.typ = 'Length';
199
+ }
200
+ else if (isTime(dimension)) {
201
+ // @ts-ignore
202
+ dimension.typ = 'Time';
203
+ }
204
+ else if (isResolution(dimension)) {
205
+ // @ts-ignore
206
+ dimension.typ = 'Resolution';
207
+ if (dimension.unit == 'dppx') {
208
+ dimension.unit = 'x';
209
+ }
210
+ }
211
+ else if (isFrequency(dimension)) {
212
+ // @ts-ignore
213
+ dimension.typ = 'Frequency';
214
+ }
215
+ return dimension;
216
+ }
217
+ function isHexColor(name) {
218
+ if (name.charAt(0) != '#' || ![4, 5, 7, 9].includes(name.length)) {
219
+ return false;
220
+ }
221
+ for (let chr of name.slice(1)) {
222
+ let codepoint = chr.charCodeAt(0);
223
+ if (!isDigit(codepoint) &&
224
+ // A-F
225
+ !(codepoint >= 0x41 && codepoint <= 0x46) &&
226
+ // a-f
227
+ !(codepoint >= 0x61 && codepoint <= 0x66)) {
228
+ return false;
229
+ }
230
+ }
231
+ return true;
232
+ }
233
+ function isFunction(name) {
234
+ return name.endsWith('(') && isIdent(name.slice(0, -1));
235
+ }
236
+ function isAtKeyword(name) {
237
+ return name.charCodeAt(0) == 0x40 && isIdent(name.slice(1));
238
+ }
239
+ function isNewLine(codepoint) {
240
+ // \n \r \f
241
+ return codepoint == 0xa || codepoint == 0xc || codepoint == 0xd;
242
+ }
243
+ function isWhiteSpace(codepoint) {
244
+ return codepoint == 0x9 || codepoint == 0x20 || isNewLine(codepoint);
245
+ }
246
+
247
+ export { isAngle, isAtKeyword, isDigit, isDimension, isFrequency, isFunction, isHash, isHexColor, isIdent, isIdentCodepoint, isIdentStart, isLength, isNewLine, isNumber, isPercentage, isPseudo, isResolution, isTime, isWhiteSpace, parseDimension };
@@ -0,0 +1,12 @@
1
+ function matchType(val, properties) {
2
+ if (val.typ == 'Iden' && properties.keywords.includes(val.val) ||
3
+ (properties.types.includes(val.typ))) {
4
+ return true;
5
+ }
6
+ if (val.typ == 'Number' && val.val == '0') {
7
+ return properties.types.some(type => type == 'Length' || type == 'Angle');
8
+ }
9
+ return false;
10
+ }
11
+
12
+ export { matchType };
@@ -0,0 +1,242 @@
1
+ import { rgb2Hex, hsl2Hex, hwb2hex, cmyk2hex, NAMES_COLORS } from './utils/color.js';
2
+
3
+ const indents = [];
4
+ function render(data, opt = {}) {
5
+ const options = Object.assign(opt.compress ? {
6
+ indent: '',
7
+ newLine: '',
8
+ removeComments: true
9
+ } : {
10
+ indent: ' ',
11
+ newLine: '\n',
12
+ compress: false,
13
+ removeComments: false,
14
+ }, { colorConvert: true, preserveLicense: false }, opt);
15
+ function reducer(acc, curr, index, original) {
16
+ if (curr.typ == 'Comment' && options.removeComments) {
17
+ if (!options.preserveLicense || !curr.val.startsWith('/*!')) {
18
+ return acc;
19
+ }
20
+ }
21
+ if (options.compress && curr.typ == 'Whitespace') {
22
+ if (original[index + 1]?.typ == 'Start-parens' ||
23
+ (index > 0 && (original[index - 1].typ == 'Pseudo-class-func' ||
24
+ original[index - 1].typ == 'End-parens' ||
25
+ original[index - 1].typ == 'UrlFunc' ||
26
+ original[index - 1].typ == 'Func' ||
27
+ (original[index - 1].typ == 'Color' &&
28
+ original[index - 1].kin != 'hex' &&
29
+ original[index - 1].kin != 'lit')))) {
30
+ return acc;
31
+ }
32
+ }
33
+ return acc + renderToken(curr, options);
34
+ }
35
+ return { code: doRender(data, options, reducer) };
36
+ }
37
+ // @ts-ignore
38
+ function doRender(data, options, reducer, level = 0) {
39
+ if (indents.length < level + 1) {
40
+ indents.push(options.indent.repeat(level));
41
+ }
42
+ if (indents.length < level + 2) {
43
+ indents.push(options.indent.repeat(level + 1));
44
+ }
45
+ const indent = indents[level];
46
+ const indentSub = indents[level + 1];
47
+ switch (data.typ) {
48
+ case 'Comment':
49
+ return options.removeComments ? '' : data.val;
50
+ case 'StyleSheet':
51
+ return data.chi.reduce((css, node) => {
52
+ const str = doRender(node, options, reducer, level);
53
+ if (str === '') {
54
+ return css;
55
+ }
56
+ if (css === '') {
57
+ return str;
58
+ }
59
+ return `${css}${options.newLine}${str}`;
60
+ }, '');
61
+ case 'AtRule':
62
+ case 'Rule':
63
+ if (data.typ == 'AtRule' && !('chi' in data)) {
64
+ return `${indent}@${data.nam} ${data.val};`;
65
+ }
66
+ // @ts-ignore
67
+ let children = data.chi.reduce((css, node) => {
68
+ let str;
69
+ if (node.typ == 'Comment') {
70
+ str = options.removeComments ? '' : node.val;
71
+ }
72
+ else if (node.typ == 'Declaration') {
73
+ str = `${node.nam}:${options.indent}${node.val.reduce(reducer, '').trimEnd()};`;
74
+ }
75
+ else if (node.typ == 'AtRule' && !('chi' in node)) {
76
+ str = `@${node.nam} ${node.val};`;
77
+ }
78
+ else {
79
+ str = doRender(node, options, reducer, level + 1);
80
+ }
81
+ if (css === '') {
82
+ return str;
83
+ }
84
+ if (str === '') {
85
+ return css;
86
+ }
87
+ if (str !== '')
88
+ return `${css}${options.newLine}${indentSub}${str}`;
89
+ }, '');
90
+ if (children.endsWith(';')) {
91
+ children = children.slice(0, -1);
92
+ }
93
+ if (data.typ == 'AtRule') {
94
+ return `@${data.nam}${data.val ? ' ' + data.val + options.indent : ''}{${options.newLine}` + (children === '' ? '' : indentSub + children + options.newLine) + indent + `}`;
95
+ }
96
+ return data.sel + `${options.indent}{${options.newLine}` + (children === '' ? '' : indentSub + children + options.newLine) + indent + `}`;
97
+ }
98
+ return '';
99
+ }
100
+ function renderToken(token, options = {}) {
101
+ switch (token.typ) {
102
+ case 'Color':
103
+ if (options.compress || options.colorConvert) {
104
+ let value = token.kin == 'hex' ? token.val.toLowerCase() : '';
105
+ if (token.val == 'rgb' || token.val == 'rgba') {
106
+ value = rgb2Hex(token);
107
+ }
108
+ else if (token.val == 'hsl' || token.val == 'hsla') {
109
+ value = hsl2Hex(token);
110
+ }
111
+ else if (token.val == 'hwb') {
112
+ value = hwb2hex(token);
113
+ }
114
+ else if (token.val == 'device-cmyk') {
115
+ value = cmyk2hex(token);
116
+ }
117
+ const named_color = NAMES_COLORS[value];
118
+ if (value !== '') {
119
+ if (value.length == 7) {
120
+ if (value[1] == value[2] &&
121
+ value[3] == value[4] &&
122
+ value[5] == value[6]) {
123
+ value = `#${value[1]}${value[3]}${value[5]}`;
124
+ }
125
+ }
126
+ else if (value.length == 9) {
127
+ if (value[1] == value[2] &&
128
+ value[3] == value[4] &&
129
+ value[5] == value[6] &&
130
+ value[7] == value[8]) {
131
+ value = `#${value[1]}${value[3]}${value[5]}${value[7]}`;
132
+ }
133
+ }
134
+ return named_color != null && named_color.length <= value.length ? named_color : value;
135
+ }
136
+ }
137
+ if (token.kin == 'hex' || token.kin == 'lit') {
138
+ return token.val;
139
+ }
140
+ case 'Func':
141
+ case 'UrlFunc':
142
+ case 'Pseudo-class-func':
143
+ // @ts-ignore
144
+ return (options.compress && 'Pseudo-class-func' == token.typ && token.val.slice(0, 2) == '::' ? token.val.slice(1) : token.val) + '(' + token.chi.reduce((acc, curr) => {
145
+ if (options.removeComments && curr.typ == 'Comment') {
146
+ if (!options.preserveLicense || !curr.val.startsWith('/*!')) {
147
+ return acc;
148
+ }
149
+ }
150
+ return acc + renderToken(curr, options);
151
+ }, '') + ')';
152
+ case 'Includes':
153
+ return '~=';
154
+ case 'Dash-match':
155
+ return '|=';
156
+ case 'Lt':
157
+ return '<';
158
+ case 'Gt':
159
+ return '>';
160
+ case 'Start-parens':
161
+ return '(';
162
+ case 'End-parens':
163
+ return ')';
164
+ case 'Attr-start':
165
+ return '[';
166
+ case 'Attr-end':
167
+ return ']';
168
+ case 'Whitespace':
169
+ return ' ';
170
+ case 'Colon':
171
+ return ':';
172
+ case 'Semi-colon':
173
+ return ';';
174
+ case 'Comma':
175
+ return ',';
176
+ case 'Important':
177
+ return '!important';
178
+ case 'Attr':
179
+ return '[' + token.chi.reduce((acc, curr) => acc + renderToken(curr, options), '') + ']';
180
+ case 'Time':
181
+ case 'Frequency':
182
+ case 'Angle':
183
+ case 'Length':
184
+ case 'Dimension':
185
+ const val = (+token.val).toString();
186
+ if (val === '0') {
187
+ if (token.typ == 'Time') {
188
+ return '0s';
189
+ }
190
+ if (token.typ == 'Frequency') {
191
+ return '0Hz';
192
+ }
193
+ // @ts-ignore
194
+ if (token.typ == 'Resolution') {
195
+ return '0x';
196
+ }
197
+ return '0';
198
+ }
199
+ const chr = val.charAt(0);
200
+ if (chr == '-') {
201
+ const slice = val.slice(0, 2);
202
+ if (slice == '-0') {
203
+ return (val.length == 2 ? '0' : '-' + val.slice(2)) + token.unit;
204
+ }
205
+ }
206
+ else if (chr == '0') {
207
+ return val.slice(1) + token.unit;
208
+ }
209
+ return val + token.unit;
210
+ case 'Perc':
211
+ return token.val + '%';
212
+ case 'Number':
213
+ const num = (+token.val).toString();
214
+ if (token.val.length < num.length) {
215
+ return token.val;
216
+ }
217
+ if (num.charAt(0) === '0' && num.length > 1) {
218
+ return num.slice(1);
219
+ }
220
+ const slice = num.slice(0, 2);
221
+ if (slice == '-0') {
222
+ return '-' + num.slice(2);
223
+ }
224
+ return num;
225
+ case 'Comment':
226
+ if (options.removeComments) {
227
+ return '';
228
+ }
229
+ case 'Url-token':
230
+ case 'At-rule':
231
+ case 'Hash':
232
+ case 'Pseudo-class':
233
+ case 'Literal':
234
+ case 'String':
235
+ case 'Iden':
236
+ case 'Delim':
237
+ return options.compress && 'Pseudo-class' == token.typ && '::' == token.val.slice(0, 2) ? token.val.slice(1) : token.val;
238
+ }
239
+ throw new Error(`unexpected token ${JSON.stringify(token, null, 1)}`);
240
+ }
241
+
242
+ export { render, renderToken };