@tbela99/css-parser 0.0.1-alpha4 → 0.0.1-rc1

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tbela99/css-parser",
3
3
  "description": "CSS parser for node and the browser",
4
- "version": "0.0.1-alpha4",
4
+ "version": "0.0.1-rc1",
5
5
  "exports": {
6
6
  ".": "./dist/index.js",
7
7
  "./web": "./dist/web/index.js",
@@ -11,8 +11,10 @@
11
11
  "typings": "dist/index.d.ts",
12
12
  "scripts": {
13
13
  "build": "rollup -c",
14
- "debug": "web-test-runner \"test/**/*.web.js\" --manual --open --node-resolve --root-dir=./test/",
15
- "test": "web-test-runner \"test/**/*.web.js\" --node-resolve --root-dir=./test/; mocha --reporter-options='maxDiffSize=181920' \"test/**/*.test.js\""
14
+ "test": "web-test-runner \"test/**/*.web-spec.js\" --node-resolve --root-dir=.; mocha --reporter-options='maxDiffSize=1801920' \"test/**/*.spec.js\"",
15
+ "test:cov": "web-test-runner \"test/**/*.web-spec.js\" --node-resolve --root-dir=. --coverage; c8 --reporter=html --reporter=text --reporter=json-summary mocha --reporter-options='maxDiffSize=1801920' \"test/**/*.spec.js\"",
16
+ "profile": "node --inspect-brk test/inspect.mjs",
17
+ "debug": "web-test-runner \"test/**/*.web.js\" --manual --open --node-resolve --root-dir=."
16
18
  },
17
19
  "repository": {
18
20
  "type": "git",
@@ -21,9 +23,14 @@
21
23
  "keywords": [
22
24
  "parser",
23
25
  "css",
26
+ "css parser",
24
27
  "css-parser",
25
28
  "node",
26
- "browser"
29
+ "ast",
30
+ "browser",
31
+ "css nesting",
32
+ "css compiler",
33
+ "nested css"
27
34
  ],
28
35
  "author": "Thierry Bela",
29
36
  "license": "MIT OR LGPL-3.0",
@@ -43,7 +50,7 @@
43
50
  "@types/node": "^18.15.10",
44
51
  "@web/test-runner": "^0.16.1",
45
52
  "@webref/css": "^6.5.9",
46
- "glob": "^10.3.0",
53
+ "c8": "^8.0.1",
47
54
  "mocha": "^10.2.0",
48
55
  "rollup": "^3.20.1",
49
56
  "rollup-plugin-dts": "^5.3.0",
@@ -1,542 +0,0 @@
1
- import { PropertyList } from './declaration/list.js';
2
- import { eq } from './utils/eq.js';
3
- import { isIdentStart } from './utils/syntax.js';
4
- import { getConfig } from './utils/config.js';
5
- import { render } from '../renderer/render.js';
6
-
7
- const configuration = getConfig();
8
- function deduplicate(ast, options = {}, recursive = false) {
9
- // @ts-ignore
10
- if (('chi' in ast) && ast.chi?.length > 0) {
11
- let i = 0;
12
- let previous;
13
- let node;
14
- let nodeIndex;
15
- // @ts-ignore
16
- for (; i < ast.chi.length; i++) {
17
- // @ts-ignore
18
- if (ast.chi[i].typ == 'Comment') {
19
- continue;
20
- }
21
- // @ts-ignore
22
- node = ast.chi[i];
23
- if (node.typ == 'AtRule' && node.nam == 'font-face') {
24
- continue;
25
- }
26
- if (node.typ == 'AtRule' && node.val == 'all') {
27
- // @ts-ignore
28
- ast.chi?.splice(i, 1, ...node.chi);
29
- i--;
30
- continue;
31
- }
32
- // @ts-ignore
33
- if (node.typ == 'Rule') {
34
- reduceRuleSelector(node);
35
- // @ts-ignore
36
- if (options.nestingRules && node.raw != null && previous?.raw != null && node.raw.length == 1 && previous.raw.length == 1) {
37
- const match = [];
38
- // @ts-ignore
39
- while (node.raw[0].length > 0 && previous.raw[0].length > 0) {
40
- // @ts-ignore
41
- if (node.raw[0][0] != previous.raw[0][0]) {
42
- break;
43
- }
44
- // @ts-ignore
45
- match.push(node.raw[0].shift());
46
- // @ts-ignore
47
- previous.raw[0].shift();
48
- }
49
- if (match.length > 0) {
50
- // @ts-ignore
51
- const wrapper = { ...previous, chi: [] };
52
- // @ts-ignore
53
- if (previous.raw[0].length == 0) {
54
- // @ts-ignore
55
- wrapper.chi.push(...previous.chi);
56
- }
57
- else {
58
- // @ts-ignore
59
- previous.sel = previous.raw.reduce((acc, curr) => {
60
- acc.push(curr.join(''));
61
- return acc;
62
- }, []).join(',');
63
- // @ts-ignore
64
- wrapper.chi.push(previous);
65
- }
66
- // @ts-ignore
67
- if (node.raw[0].length == 0) {
68
- // @ts-ignore
69
- if (previous.raw.length == 0) {
70
- // @ts-ignore
71
- wrapper.chi.push(...node.chi);
72
- }
73
- else {
74
- if (hasOnlyDeclarations(wrapper)) {
75
- wrapper.chi.push(...node.chi);
76
- }
77
- else {
78
- // @ts-ignore
79
- node.raw[0].push('&');
80
- // @ts-ignore
81
- node.sel = node.raw.reduce((acc, curr) => {
82
- acc.push(curr.join(''));
83
- return acc;
84
- }, []).join(',');
85
- // @ts-ignore
86
- wrapper.chi.push(node);
87
- }
88
- }
89
- }
90
- else {
91
- // @ts-ignore
92
- node.sel = node.raw.reduce((acc, curr) => {
93
- acc.push(curr.join(''));
94
- return acc;
95
- }, []).join(',');
96
- // @ts-ignore
97
- wrapper.chi.push(node);
98
- }
99
- Object.defineProperty(wrapper, 'raw', { enumerable: false, writable: true, value: [match] });
100
- // @ts-ignore
101
- ast.chi.splice(i, 1, wrapper);
102
- // @ts-ignore
103
- ast.chi.splice(nodeIndex, 1);
104
- // @ts-ignore
105
- while (i < ast.chi.length) {
106
- // @ts-ignore
107
- const nextNode = ast.chi[i];
108
- // @ts-ignore
109
- if (nextNode.typ != 'Rule' || nextNode.raw == null) {
110
- break;
111
- }
112
- reduceRuleSelector(nextNode);
113
- // @ts-ignore
114
- if (nextNode.raw.length != 1 || !eq(wrapper.raw[0], nextNode.raw[0].slice(0, wrapper.raw[0].length))) {
115
- break;
116
- }
117
- // @ts-ignore
118
- nextNode.raw[0].splice(0, wrapper.raw[0].length);
119
- // @ts-ignore
120
- if (nextNode.raw[0].length == 0 ||
121
- // @ts-ignore
122
- (nextNode.raw.length == 1 && nextNode.raw[0] == '&')) {
123
- if (hasOnlyDeclarations(wrapper)) {
124
- wrapper.chi.push(...nextNode.chi);
125
- // @ts-ignore
126
- ast.chi.splice(i, 1);
127
- continue;
128
- }
129
- else {
130
- // @ts-ignore
131
- nextNode.raw[0].push('&');
132
- }
133
- }
134
- // @ts-ignore
135
- nextNode.sel = nextNode.raw.reduce((acc, curr) => {
136
- acc.push(curr.join(''));
137
- return acc;
138
- }, []).join(',');
139
- wrapper.chi.push(nextNode);
140
- // @ts-ignore
141
- ast.chi.splice(i, 1);
142
- }
143
- deduplicateRule(wrapper);
144
- nodeIndex = --i;
145
- // @ts-ignore
146
- previous = ast.chi[i];
147
- continue;
148
- }
149
- }
150
- }
151
- // @ts-ignore
152
- if (previous != null && 'chi' in previous && ('chi' in node)) {
153
- // @ts-ignore
154
- if (previous.typ == node.typ) {
155
- let shouldMerge = true;
156
- // @ts-ignore
157
- let k = previous.chi.length;
158
- while (k-- > 0) {
159
- // @ts-ignore
160
- if (previous.chi[k].typ == 'Comment') {
161
- continue;
162
- }
163
- // @ts-ignore
164
- shouldMerge = previous.chi[k].typ == 'Declaration';
165
- break;
166
- }
167
- if (shouldMerge) {
168
- // @ts-ignore
169
- if ((node.typ == 'Rule' && node.sel == previous.sel) ||
170
- // @ts-ignore
171
- (node.typ == 'AtRule') && node.val != 'font-face' && node.val == previous.val) {
172
- // @ts-ignore
173
- node.chi.unshift(...previous.chi);
174
- // @ts-ignore
175
- ast.chi.splice(nodeIndex, 1);
176
- // @ts-ignore
177
- if (hasDeclaration(node)) {
178
- deduplicateRule(node);
179
- }
180
- else {
181
- deduplicate(node, options, recursive);
182
- }
183
- i--;
184
- previous = node;
185
- nodeIndex = i;
186
- continue;
187
- }
188
- else if (node.typ == 'Rule' && previous?.typ == 'Rule') {
189
- const intersect = diff(previous, node, options);
190
- if (intersect != null) {
191
- if (intersect.node1.chi.length == 0) {
192
- // @ts-ignore
193
- ast.chi.splice(i--, 1);
194
- // @ts-ignore
195
- node = ast.chi[i];
196
- }
197
- else {
198
- // @ts-ignore
199
- ast.chi.splice(i, 1, intersect.node1);
200
- node = intersect.node1;
201
- }
202
- if (intersect.node2.chi.length == 0) {
203
- // @ts-ignore
204
- ast.chi.splice(nodeIndex, 1, intersect.result);
205
- previous = intersect.result;
206
- }
207
- else {
208
- // @ts-ignore
209
- ast.chi.splice(nodeIndex, 1, intersect.result, intersect.node2);
210
- previous = intersect.result;
211
- // @ts-ignore
212
- i = nodeIndex;
213
- }
214
- }
215
- }
216
- }
217
- }
218
- // @ts-ignore
219
- if (recursive && previous != node) {
220
- // @ts-ignore
221
- if (hasDeclaration(previous)) {
222
- deduplicateRule(previous);
223
- }
224
- else {
225
- deduplicate(previous, options, recursive);
226
- }
227
- }
228
- }
229
- previous = node;
230
- nodeIndex = i;
231
- }
232
- // @ts-ignore
233
- if (recursive && node != null && ('chi' in node)) {
234
- // @ts-ignore
235
- if (node.chi.some(n => n.typ == 'Declaration')) {
236
- deduplicateRule(node);
237
- }
238
- else {
239
- if (!(node.typ == 'AtRule' && node.nam != 'font-face')) {
240
- deduplicate(node, options, recursive);
241
- }
242
- }
243
- }
244
- }
245
- return ast;
246
- }
247
- function hasOnlyDeclarations(node) {
248
- let k = node.chi.length;
249
- while (k--) {
250
- if (node.chi[k].typ == 'Comment') {
251
- continue;
252
- }
253
- return node.chi[k].typ == 'Declaration';
254
- }
255
- return true;
256
- }
257
- function hasDeclaration(node) {
258
- // @ts-ignore
259
- for (let i = 0; i < node.chi?.length; i++) {
260
- // @ts-ignore
261
- if (node.chi[i].typ == 'Comment') {
262
- continue;
263
- }
264
- // @ts-ignore
265
- return node.chi[i].typ == 'Declaration';
266
- }
267
- return true;
268
- }
269
- function deduplicateRule(ast, options = {}) {
270
- // @ts-ignore
271
- if (!('chi' in ast) || ast.chi?.length <= 1) {
272
- return ast;
273
- }
274
- // @ts-ignore
275
- const j = ast.chi.length;
276
- let k = 0;
277
- let map = new Map;
278
- // @ts-ignore
279
- for (; k < j; k++) {
280
- // @ts-ignore
281
- const node = ast.chi[k];
282
- if (node.typ == 'Comment') {
283
- // @ts-ignore
284
- map.set(node, node);
285
- continue;
286
- }
287
- else if (node.typ != 'Declaration') {
288
- break;
289
- }
290
- if (node.nam in configuration.map ||
291
- node.nam in configuration.properties) {
292
- // @ts-ignore
293
- const shorthand = node.nam in configuration.map ? configuration.map[node.nam].shorthand : configuration.properties[node.nam].shorthand;
294
- if (!map.has(shorthand)) {
295
- map.set(shorthand, new PropertyList());
296
- }
297
- map.get(shorthand).add(node);
298
- }
299
- else {
300
- map.set(node.nam, node);
301
- }
302
- }
303
- const children = [];
304
- for (let child of map.values()) {
305
- if (child instanceof PropertyList) {
306
- // @ts-ignore
307
- children.push(...child);
308
- }
309
- else {
310
- // @ts-ignore
311
- children.push(child);
312
- }
313
- }
314
- // @ts-ignore
315
- ast.chi = children.concat(ast.chi?.slice(k));
316
- return ast;
317
- }
318
- function splitRule(buffer) {
319
- const result = [];
320
- let str = '';
321
- for (let i = 0; i < buffer.length; i++) {
322
- let chr = buffer.charAt(i);
323
- if (chr == ',') {
324
- if (str !== '') {
325
- result.push(str);
326
- str = '';
327
- }
328
- continue;
329
- }
330
- str += chr;
331
- if (chr == '\\') {
332
- str += buffer.charAt(++i);
333
- continue;
334
- }
335
- if (chr == '"' || chr == "'") {
336
- let k = i;
337
- while (++k < buffer.length) {
338
- chr = buffer.charAt(k);
339
- str += chr;
340
- if (chr == '//') {
341
- str += buffer.charAt(++k);
342
- continue;
343
- }
344
- if (chr == buffer.charAt(i)) {
345
- break;
346
- }
347
- }
348
- continue;
349
- }
350
- if (chr == '(' || chr == '[') {
351
- const open = chr;
352
- const close = chr == '(' ? ')' : ']';
353
- let inParens = 1;
354
- let k = i;
355
- while (++k < buffer.length) {
356
- chr = buffer.charAt(k);
357
- if (chr == '\\') {
358
- str += buffer.slice(k, k + 2);
359
- k++;
360
- continue;
361
- }
362
- str += chr;
363
- if (chr == open) {
364
- inParens++;
365
- }
366
- else if (chr == close) {
367
- inParens--;
368
- }
369
- if (inParens == 0) {
370
- break;
371
- }
372
- }
373
- i = k;
374
- }
375
- }
376
- if (str !== '') {
377
- result.push(str);
378
- }
379
- return result;
380
- }
381
- function reduceRuleSelector(node) {
382
- // @ts-ignore
383
- if (node.raw != null) {
384
- // @ts-ignore
385
- let optimized = reduceSelector(node.raw);
386
- if (optimized != null) {
387
- Object.defineProperty(node, 'optimized', { enumerable: false, writable: true, value: optimized });
388
- }
389
- if (optimized != null && optimized.match && optimized.reducible) {
390
- const raw = [
391
- [
392
- optimized.optimized[0], ':is('
393
- ].concat(optimized.selector.reduce((acc, curr) => {
394
- if (acc.length > 0) {
395
- acc.push(',');
396
- }
397
- acc.push(...curr);
398
- return acc;
399
- }, [])).concat(')')
400
- ];
401
- const sel = raw[0].join('');
402
- if (sel.length < node.sel.length) {
403
- node.sel = sel;
404
- // node.raw = raw;
405
- Object.defineProperty(node, 'raw', { enumerable: false, writable: true, value: raw });
406
- }
407
- }
408
- }
409
- }
410
- function diff(n1, n2, options = {}) {
411
- let node1 = n1;
412
- let node2 = n2;
413
- let exchanged = false;
414
- if (node1.chi.length > node2.chi.length) {
415
- const t = node1;
416
- node1 = node2;
417
- node2 = t;
418
- exchanged = true;
419
- }
420
- let i = node1.chi.length;
421
- let j = node2.chi.length;
422
- if (i == 0 || j == 0) {
423
- // @ts-ignore
424
- return null;
425
- }
426
- // @ts-ignore
427
- const raw1 = node1.raw;
428
- // @ts-ignore
429
- const optimized1 = node1.optimized;
430
- // @ts-ignore
431
- const raw2 = node2.raw;
432
- // @ts-ignore
433
- const optimized2 = node2.optimized;
434
- node1 = { ...node1, chi: node1.chi.slice() };
435
- node2 = { ...node2, chi: node2.chi.slice() };
436
- if (raw1 != null) {
437
- Object.defineProperty(node1, 'raw', { enumerable: false, writable: true, value: raw1 });
438
- }
439
- if (optimized1 != null) {
440
- Object.defineProperty(node1, 'optimized', { enumerable: false, writable: true, value: optimized1 });
441
- }
442
- if (raw2 != null) {
443
- Object.defineProperty(node2, 'raw', { enumerable: false, writable: true, value: raw2 });
444
- }
445
- if (optimized2 != null) {
446
- Object.defineProperty(node2, 'optimized', { enumerable: false, writable: true, value: optimized2 });
447
- }
448
- const intersect = [];
449
- while (i--) {
450
- if (node1.chi[i].typ == 'Comment') {
451
- continue;
452
- }
453
- j = node2.chi.length;
454
- if (j == 0) {
455
- break;
456
- }
457
- while (j--) {
458
- if (node2.chi[j].typ == 'Comment') {
459
- continue;
460
- }
461
- if (node1.chi[i].nam == node2.chi[j].nam) {
462
- if (eq(node1.chi[i], node2.chi[j])) {
463
- intersect.push(node1.chi[i]);
464
- node1.chi.splice(i, 1);
465
- node2.chi.splice(j, 1);
466
- break;
467
- }
468
- }
469
- }
470
- }
471
- // @ts-ignore
472
- const result = (intersect.length == 0 ? null : {
473
- ...node1,
474
- // @ts-ignore
475
- sel: [...new Set([...(n1?.raw?.reduce(reducer, []) || splitRule(n1.sel)).concat(n2?.raw?.reduce(reducer, []) || splitRule(n2.sel))])].join(),
476
- chi: intersect.reverse()
477
- });
478
- if (result == null || [n1, n2].reduce((acc, curr) => curr.chi.length == 0 ? acc : acc + render(curr, options).code.length, 0) <= [node1, node2, result].reduce((acc, curr) => curr.chi.length == 0 ? acc : acc + render(curr, options).code.length, 0)) {
479
- // @ts-ignore
480
- return null;
481
- }
482
- return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node2 : node2 };
483
- }
484
- function reduceSelector(selector) {
485
- if (selector.length < 2) {
486
- return null;
487
- }
488
- const optimized = [];
489
- const k = selector.reduce((acc, curr) => acc == 0 ? curr.length : (curr.length == 0 ? acc : Math.min(acc, curr.length)), 0);
490
- let i = 0;
491
- let j;
492
- let match;
493
- for (; i < k; i++) {
494
- const item = selector[0][i];
495
- match = true;
496
- for (j = 1; j < selector.length; j++) {
497
- if (item != selector[j][i]) {
498
- match = false;
499
- break;
500
- }
501
- }
502
- if (!match) {
503
- break;
504
- }
505
- optimized.push(item);
506
- }
507
- if (optimized.at(-1) == ' ') {
508
- optimized.pop();
509
- }
510
- let reducible = optimized.length == 1;
511
- if (optimized.length == 0) {
512
- return { match: false, optimized, selector, reducible };
513
- }
514
- return {
515
- match: true,
516
- optimized,
517
- selector: selector.reduce((acc, curr) => {
518
- const slice = curr.slice(optimized.length);
519
- // @ts-ignore
520
- if (slice.length > 0 && slice[0] == ' ') {
521
- slice.shift();
522
- }
523
- if (slice.length == 0) {
524
- slice.push('&');
525
- }
526
- if (reducible) {
527
- const chr = slice[0].charAt(0);
528
- // @ts-ignore
529
- reducible = chr == '.' || chr == ':' || isIdentStart(chr.codePointAt(0));
530
- }
531
- acc.push(slice);
532
- return acc;
533
- }, []),
534
- reducible
535
- };
536
- }
537
- function reducer(acc, curr) {
538
- acc.push(curr.join(''));
539
- return acc;
540
- }
541
-
542
- export { deduplicate, deduplicateRule, hasDeclaration, reduceSelector };