@tbela99/css-parser 0.0.1 → 0.2.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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # css-parser
4
4
 
5
- CSS parser for node and the browser
5
+ CSS parser and minifier for node and the browser
6
6
 
7
7
  ## Installation
8
8
 
@@ -12,11 +12,15 @@ $ npm install @tbela99/css-parser
12
12
 
13
13
  ## Features
14
14
 
15
- - fault tolerant parser, will try to fix invalid tokens according to the CSS syntax module 3 recommendations.
15
+ - fault-tolerant parser, will try to fix invalid tokens according to the CSS syntax module 3 recommendations.
16
16
  - efficient minification, see [benchmark](https://tbela99.github.io/css-parser/benchmark/index.html)
17
17
  - automatically generate nested css rules
18
+ - generate sourcemap
18
19
  - compute css shorthands. see the list below
19
- - expand nested css
20
+ - compute calc() expression
21
+ - nested css expansion
22
+ - remove duplicate properties
23
+ - flatten @import rules
20
24
  - works the same way in node and web browser
21
25
 
22
26
  ## Transform
@@ -25,9 +29,9 @@ Parse and render css in a single pass.
25
29
 
26
30
  ### Usage
27
31
 
28
- ```javascript
32
+ ```typescript
29
33
 
30
- transform(css, transformOptions = {})
34
+ transform(css, transformOptions: TransformOptions = {}): TransformResult
31
35
  ```
32
36
 
33
37
  ### Example
@@ -36,7 +40,7 @@ transform(css, transformOptions = {})
36
40
 
37
41
  import {transform} from '@tbela99/css-parser';
38
42
 
39
- const {ast, code, errors, stats} = await transform(css, {minify: true, resolveImport: true, cwd: 'files/css'});
43
+ const {ast, code, map, errors, stats} = await transform(css, {minify: true, resolveImport: true, cwd: 'files/css'});
40
44
  ```
41
45
 
42
46
  ### TransformOptions
@@ -45,23 +49,37 @@ Include ParseOptions and RenderOptions
45
49
 
46
50
  #### ParseOptions
47
51
 
48
- - src: string, optional. css file location to be used with sourcemap.
49
52
  - minify: boolean, optional. default to _true_. optimize ast.
53
+ - src: string, optional. original css file location to be used with sourcemap.
54
+ - sourcemap: boolean, optional. preserve node location data.
50
55
  - nestingRules: boolean, optional. automatically generated nested rules.
51
- - removeEmpty: boolean, remove empty nodes from the ast.
52
- - location: boolean, optional. includes node location in the ast, required for sourcemap generation.
56
+ - expandNestingRules: boolean, optional. convert nesting rules into separate rules. will automatically set nestingRules to false.
57
+ - removeCharset: boolean, optional. remove @charset.
58
+ - removeEmpty: boolean, optional. remove empty rule lists from the ast.
59
+ - resolveUrls: boolean, optional. resolve css 'url()' according to the parameters 'src' and 'cwd'
60
+ - resolveImport: boolean, optional. replace @import rule by the content of its referenced stylesheet.
53
61
  - cwd: string, optional. the current working directory. when specified url() are resolved using this value
54
- - resolveImport: boolean, optional. replace @import node by the content of the referenced stylesheet.
55
- - resolveUrls: boolean, optional. resolve css url() according to the parameters 'src' and 'cwd'
62
+ - removeDuplicateDeclarations: boolean, optional. remove duplicate declarations.
63
+ - computeShorthand: boolean, optional. compute shorthand properties.
64
+ - inlineCssVariables: boolean, optional. replace css variables with their current value.
65
+ - computeCalcExpression: boolean, optional. evaluate calc() expression
66
+ - inlineCssVariables: boolean, optional. replace some css variables with their actual value. they must be declared once in the :root {} rule.
67
+ - visitor: VisitorNodeMap, optional. node visitor used to transform the ast.
68
+ - signal: AbortSignal, optional. abort parsing.
56
69
 
57
70
  #### RenderOptions
71
+
58
72
  - minify: boolean, optional. default to _true_. minify css output.
73
+ - expandNestingRules: boolean, optional. expand nesting rules.
74
+ - sourcemap: boolean, optional. generate sourcemap
75
+ - preserveLicense: boolean, force preserving comments starting with '/\*!' when minify is enabled.
76
+ - sourcemap: boolean, optional. generate sourcemap.
59
77
  - indent: string, optional. css indention string. uses space character by default.
60
- - newLine: string, new line character.
78
+ - newLine: string, optional. new line character.
61
79
  - removeComments: boolean, remove comments in generated css.
62
- - preserveLicense: boolean, force preserving comments starting with '/\*!' when minify is enabled.
63
80
  - colorConvert: boolean, convert colors to hex.
64
-
81
+ - output: string, optional. file where to store css. url() are resolved according to the specified value. no file is created though.
82
+ - cwd: string, optional. value used as current working directory. when output is not provided, urls are resolved according to this value.
65
83
 
66
84
  ## Parsing
67
85
 
@@ -163,34 +181,27 @@ Single JavaScript file
163
181
 
164
182
  CSS
165
183
 
166
- ```css
184
+ ```javascript
185
+ const {parse, render} = require("@tbela99/css-parser/cjs");
167
186
 
187
+ const css = `
168
188
  table.colortable td {
169
- text-align:center;
189
+ text-align:center;
170
190
  }
171
191
  table.colortable td.c {
172
- text-transform:uppercase;
192
+ text-transform:uppercase;
173
193
  }
174
194
  table.colortable td:first-child, table.colortable td:first-child+td {
175
- border:1px solid black;
195
+ border:1px solid black;
176
196
  }
177
197
  table.colortable th {
178
- text-align:center;
179
- background:black;
180
- color:white;
198
+ text-align:center;
199
+ background:black;
200
+ color:white;
181
201
  }
182
- ```
183
-
184
- Javascript
185
- ```javascript
186
- import {parse, render} from '@tbela99/css-parser';
202
+ `;
187
203
 
188
-
189
- const options = {minify: true, nestingRules: true};
190
-
191
- const {code} = await parse(css, options).then(result => render(result.ast, {minify: false}));
192
- //
193
- console.debug(code);
204
+ const result = await parse(css, {nestingRules:true}).then(result => render(result.ast, {minify:false}).code);
194
205
  ```
195
206
 
196
207
  Result
@@ -270,6 +281,69 @@ table.colortable th {
270
281
  }
271
282
  ```
272
283
 
284
+ ### Example 3
285
+
286
+ ### Calc() resolution
287
+
288
+ ```javascript
289
+
290
+ import {parse, render} from '@tbela99/css-parser';
291
+
292
+ const css = `
293
+
294
+ .foo-bar {
295
+ width: calc(100px * 2);
296
+ height: calc(((75.37% - 63.5px) - 900px) + (2 * 100px));
297
+ max-width: calc(3.5rem + calc(var(--bs-border-width) * 2));
298
+ }
299
+ `;
300
+
301
+ const prettyPrint = await parse(css).then(result => render(result.ast, {minify: false}).code);
302
+
303
+ ```
304
+ result
305
+
306
+ ```css
307
+ .foo-bar {
308
+ width: 200px;
309
+ height: calc(75.37% - 763.5px);
310
+ max-width: calc(3.5rem + var(--bs-border-width)*2)
311
+ }
312
+ ```
313
+
314
+ ### Example 4
315
+
316
+ ### CSS variable inlining
317
+
318
+ ```javascript
319
+
320
+ import {parse, render} from '@tbela99/css-parser';
321
+
322
+ const css = `
323
+
324
+ :root {
325
+
326
+ --preferred-width: 20px;
327
+ }
328
+ .foo-bar {
329
+
330
+ width: calc(calc(var(--preferred-width) + 1px) / 3 + 5px);
331
+ height: calc(100% / 4);}
332
+ `
333
+
334
+ const prettyPrint = await parse(css, {inlineCssVariables: true}).then(result => render(result.ast, {minify: false}).code);
335
+
336
+ ```
337
+ result
338
+
339
+ ```css
340
+ .foo-bar {
341
+ width: 12px;
342
+ height: 25%
343
+ }
344
+
345
+ ```
346
+
273
347
  ## AST
274
348
 
275
349
  ### Comment
@@ -300,8 +374,14 @@ table.colortable th {
300
374
  - typ: string 'Stylesheet'
301
375
  - chi: array of children
302
376
 
377
+ ## Sourcemap
378
+
379
+ - [x] sourcemap generation
380
+
303
381
  ## Minification
304
382
 
383
+ - [x] reduce calc()
384
+ - [x] inline css variables
305
385
  - [x] merge identical rules
306
386
  - [x] merge adjacent rules
307
387
  - [x] minify colors
@@ -311,12 +391,12 @@ table.colortable th {
311
391
  - [x] conditionally unwrap :is()
312
392
  - [x] automatic css nesting
313
393
  - [x] automatically wrap selectors using :is()
314
- - [x] multi-level shorthand properties (border - [border-width, border-color, border-style, etc.]) https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties
315
394
  - [x] avoid reparsing (declarations, selectors, at-rule)
316
395
  - [x] node and browser versions
317
396
  - [x] decode and replace utf-8 escape sequence
318
397
 
319
- ## Computed shorthands
398
+ ## Computed shorthands properties
399
+ - [x] animation
320
400
  - [x] background
321
401
  - [x] border
322
402
  - [x] border-bottom
@@ -329,12 +409,216 @@ table.colortable th {
329
409
  - [x] border-width
330
410
  - [x] font
331
411
  - [x] inset
412
+ - [x] list-style
332
413
  - [x] margin
333
414
  - [x] outline
334
415
  - [x] overflow
335
416
  - [x] padding
336
417
  - [x] text-decoration
418
+ - [x] text-emphasis
419
+ - [x] transition
337
420
 
338
421
  ## Performance
339
422
 
340
423
  - [x] flatten @import
424
+
425
+ ## Node Transformation
426
+
427
+ Ast can be transformed using node visitors
428
+
429
+ ### Exemple 1: Declaration
430
+
431
+ ```typescript
432
+
433
+ import {AstDeclaration, ParserOptions} from "../src/@types";
434
+
435
+ const options: ParserOptions = {
436
+
437
+ visitor: {
438
+
439
+ Declaration: (node: AstDeclaration) => {
440
+
441
+ if (node.nam == '-webkit-transform') {
442
+
443
+ node.nam = 'transform'
444
+ }
445
+ }
446
+ }
447
+ }
448
+
449
+ const css = `
450
+
451
+ .foo {
452
+ -webkit-transform: scale(calc(100 * 2/ 15));
453
+ }
454
+ `;
455
+
456
+ console.debug(await transform(css, options));
457
+
458
+ // .foo{transform:scale(calc(40/3))}
459
+ ```
460
+
461
+ ### Exemple 2: Declaration
462
+
463
+ ```typescript
464
+
465
+ import {AstDeclaration, LengthToken, ParserOptions} from "../src/@types";
466
+ import {EnumToken, NodeType} from "../src/lib";
467
+ import {transform} from "../src/node";
468
+
469
+ const options: ParserOptions = {
470
+
471
+ visitor: {
472
+
473
+ Declaration: {
474
+
475
+ // called only for height declaration
476
+ height: (node: AstDeclaration): AstDeclaration[] => {
477
+
478
+
479
+ return [
480
+ node,
481
+ {
482
+
483
+ typ: NodeType.DeclarationNodeType,
484
+ nam: 'width',
485
+ val: [
486
+ <LengthToken>{
487
+ typ: EnumToken.Length,
488
+ val: '3',
489
+ unit: 'px'
490
+ }
491
+ ]
492
+ }
493
+ ];
494
+ }
495
+ }
496
+ }
497
+ };
498
+
499
+ const css = `
500
+
501
+ .foo {
502
+ height: calc(100px * 2/ 15);
503
+ }
504
+ `;
505
+
506
+ console.debug(await transform(css, options));
507
+
508
+ // .foo{height:calc(40px/3);width:3px}
509
+
510
+ ```
511
+
512
+ ### Exemple 3: At-Rule
513
+
514
+ ```typescript
515
+
516
+ import {AstAtRule, ParserOptions} from "../src/@types";
517
+ import {transform} from "../src/node";
518
+
519
+
520
+ const options: ParserOptions = {
521
+
522
+ visitor: {
523
+
524
+ AtRule: (node: AstAtRule): AstAtRule => {
525
+
526
+ if (node.nam == 'media') {
527
+
528
+ return {...node, val: 'all'}
529
+ }
530
+ }
531
+ }
532
+ };
533
+
534
+ const css = `
535
+
536
+ @media screen {
537
+
538
+ .foo {
539
+
540
+ height: calc(100px * 2/ 15);
541
+ }
542
+ }
543
+ `;
544
+
545
+ console.debug(await transform(css, options));
546
+
547
+ // .foo{height:calc(40px/3)}
548
+
549
+ ```
550
+
551
+ ### Exemple 4: At-Rule
552
+
553
+ ```typescript
554
+
555
+ import {AstAtRule, ParserOptions} from "../src/@types";
556
+ import {transform} from "../src/node";
557
+
558
+ const options: ParserOptions = {
559
+
560
+ visitor: {
561
+
562
+ AtRule: {
563
+
564
+ media: (node: AstAtRule): AstAtRule => {
565
+
566
+ return {...node, val: 'all'}
567
+ }
568
+ }
569
+ }
570
+ };
571
+
572
+ const css = `
573
+
574
+ @media screen {
575
+
576
+ .foo {
577
+
578
+ height: calc(100px * 2/ 15);
579
+ }
580
+ }
581
+ `;
582
+
583
+ console.debug(await transform(css, options));
584
+
585
+ // .foo{height:calc(40px/3)}
586
+
587
+ ```
588
+
589
+ ### Exemple 5: Rule
590
+
591
+ ```typescript
592
+
593
+ import {AstAtRule, ParserOptions} from "../src/@types";
594
+ import {transform} from "../src/node";
595
+
596
+ const options: ParserOptions = {
597
+
598
+ visitor: {
599
+
600
+
601
+ Rule (node: AstRule): AstRule {
602
+
603
+ return {...node, sel: '.foo,.bar,.fubar'};
604
+ }
605
+ }
606
+ };
607
+
608
+ const css = `
609
+
610
+ .foo {
611
+
612
+ height: calc(100px * 2/ 15);
613
+ }
614
+ `;
615
+
616
+ console.debug(await transform(css, options));
617
+
618
+ // .foo,.bar,.fubar{height:calc(40px/3)}
619
+
620
+ ```
621
+
622
+ ---
623
+
624
+ Thanks to [Jetbrains](https://jetbrains.com) for sponsoring this project with a free license