@tbela99/css-parser 0.0.1-rc1 → 0.0.1-rc2
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 +9 -9
- package/dist/index-umd-web.js +68 -47
- package/dist/index.cjs +68 -47
- package/dist/index.d.ts +10 -1
- package/dist/lib/ast/minify.js +21 -5
- package/dist/lib/parser/parse.js +12 -9
- package/dist/lib/parser/tokenize.js +13 -11
- package/dist/lib/parser/utils/syntax.js +1 -4
- package/dist/lib/renderer/render.js +10 -5
- package/dist/lib/transform.js +11 -13
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
[](https://github.com/tbela99/css-parser/actions)
|
|
1
|
+
[](https://www.npmjs.com/package/@tbela99/css-parser) [](https://github.com/tbela99/css-parser/actions)
|
|
2
2
|
|
|
3
3
|
# css-parser
|
|
4
4
|
|
|
@@ -12,15 +12,15 @@ $ npm install @tbela99/css-parser
|
|
|
12
12
|
|
|
13
13
|
### Features
|
|
14
14
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
15
|
+
- fault tolerant parser, will try to fix invalid tokens according to the CSS syntax module 3 recommendations.
|
|
16
|
+
- efficient minification, see [benchmark](https://tbela99.github.io/css-parser/benchmark/index.html)
|
|
17
|
+
- replace @import at-rules with actual css content of the imported rule
|
|
18
|
+
- automatically create nested css rules
|
|
19
|
+
- works the same way in node and web browser
|
|
20
20
|
|
|
21
21
|
### Performance
|
|
22
22
|
|
|
23
|
-
-
|
|
23
|
+
- flatten @import
|
|
24
24
|
|
|
25
25
|
## Transform
|
|
26
26
|
|
|
@@ -50,7 +50,7 @@ Include ParseOptions and RenderOptions
|
|
|
50
50
|
|
|
51
51
|
- src: string, optional. css file location to be used with sourcemap.
|
|
52
52
|
- minify: boolean, optional. default to _true_. optimize ast.
|
|
53
|
-
- nestingRules: boolean, optional. automatically
|
|
53
|
+
- nestingRules: boolean, optional. automatically generated nested rules.
|
|
54
54
|
- removeEmpty: boolean, remove empty nodes from the ast.
|
|
55
55
|
- location: boolean, optional. includes node location in the ast, required for sourcemap generation.
|
|
56
56
|
- cwd: string, optional. the current working directory. when specified url() are resolved using this value
|
|
@@ -188,4 +188,4 @@ Single JavaScript file
|
|
|
188
188
|
### AtRuleStyleSheet
|
|
189
189
|
|
|
190
190
|
- typ: string 'Stylesheet'
|
|
191
|
-
- chi: array of children
|
|
191
|
+
- chi: array of children
|
package/dist/index-umd-web.js
CHANGED
|
@@ -93,10 +93,7 @@
|
|
|
93
93
|
if (name.charAt(0) != '#') {
|
|
94
94
|
return false;
|
|
95
95
|
}
|
|
96
|
-
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
return true;
|
|
96
|
+
return isIdent(name.charAt(1));
|
|
100
97
|
}
|
|
101
98
|
function isNumber(name) {
|
|
102
99
|
if (name.length == 0) {
|
|
@@ -1566,6 +1563,7 @@
|
|
|
1566
1563
|
}
|
|
1567
1564
|
|
|
1568
1565
|
function render(data, opt = {}) {
|
|
1566
|
+
const startTime = performance.now();
|
|
1569
1567
|
const options = Object.assign(opt.minify ?? true ? {
|
|
1570
1568
|
indent: '',
|
|
1571
1569
|
newLine: '',
|
|
@@ -1584,7 +1582,9 @@
|
|
|
1584
1582
|
}
|
|
1585
1583
|
return acc + renderToken(curr, options);
|
|
1586
1584
|
}
|
|
1587
|
-
return { code: doRender(data, options, reducer, 0)
|
|
1585
|
+
return { code: doRender(data, options, reducer, 0), stats: {
|
|
1586
|
+
total: `${(performance.now() - startTime).toFixed(2)}ms`
|
|
1587
|
+
} };
|
|
1588
1588
|
}
|
|
1589
1589
|
// @ts-ignore
|
|
1590
1590
|
function doRender(data, options, reducer, level = 0, indents = []) {
|
|
@@ -1613,7 +1613,7 @@
|
|
|
1613
1613
|
case 'AtRule':
|
|
1614
1614
|
case 'Rule':
|
|
1615
1615
|
if (data.typ == 'AtRule' && !('chi' in data)) {
|
|
1616
|
-
return `${indent}@${data.nam} ${data.val};`;
|
|
1616
|
+
return `${indent}@${data.nam}${data.val === '' ? '' : options.indent || ' '}${data.val};`;
|
|
1617
1617
|
}
|
|
1618
1618
|
// @ts-ignore
|
|
1619
1619
|
let children = data.chi.reduce((css, node) => {
|
|
@@ -1625,7 +1625,7 @@
|
|
|
1625
1625
|
str = `${node.nam}:${options.indent}${node.val.reduce(reducer, '').trimEnd()};`;
|
|
1626
1626
|
}
|
|
1627
1627
|
else if (node.typ == 'AtRule' && !('chi' in node)) {
|
|
1628
|
-
str =
|
|
1628
|
+
str = `${data.val === '' ? '' : options.indent || ' '}${data.val};`;
|
|
1629
1629
|
}
|
|
1630
1630
|
else {
|
|
1631
1631
|
str = doRender(node, options, reducer, level + 1, indents);
|
|
@@ -1642,7 +1642,7 @@
|
|
|
1642
1642
|
children = children.slice(0, -1);
|
|
1643
1643
|
}
|
|
1644
1644
|
if (data.typ == 'AtRule') {
|
|
1645
|
-
return `@${data.nam}${data.val
|
|
1645
|
+
return `@${data.nam}${data.val === '' ? '' : options.indent || ' '}${data.val}${options.indent}{${options.newLine}` + (children === '' ? '' : indentSub + children + options.newLine) + indent + `}`;
|
|
1646
1646
|
}
|
|
1647
1647
|
return data.sel + `${options.indent}{${options.newLine}` + (children === '' ? '' : indentSub + children + options.newLine) + indent + `}`;
|
|
1648
1648
|
}
|
|
@@ -1792,7 +1792,9 @@
|
|
|
1792
1792
|
case 'Delim':
|
|
1793
1793
|
return /* options.minify && 'Pseudo-class' == token.typ && '::' == token.val.slice(0, 2) ? token.val.slice(1) : */ token.val;
|
|
1794
1794
|
}
|
|
1795
|
-
|
|
1795
|
+
console.error(`unexpected token ${JSON.stringify(token, null, 1)}`);
|
|
1796
|
+
// throw new Error(`unexpected token ${JSON.stringify(token, null, 1)}`);
|
|
1797
|
+
return '';
|
|
1796
1798
|
}
|
|
1797
1799
|
|
|
1798
1800
|
function eq(a, b) {
|
|
@@ -2581,7 +2583,7 @@
|
|
|
2581
2583
|
// @ts-ignore
|
|
2582
2584
|
return null;
|
|
2583
2585
|
}
|
|
2584
|
-
return { result, node1: exchanged ? node2 : node1, node2: exchanged ?
|
|
2586
|
+
return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node1 : node2 };
|
|
2585
2587
|
}
|
|
2586
2588
|
function matchSelectors(selector1, selector2, parentType) {
|
|
2587
2589
|
let match = [[]];
|
|
@@ -2769,11 +2771,27 @@
|
|
|
2769
2771
|
if (node.typ == 'AtRule' && node.nam == 'font-face') {
|
|
2770
2772
|
continue;
|
|
2771
2773
|
}
|
|
2772
|
-
if (node.typ == 'AtRule'
|
|
2774
|
+
if (node.typ == 'AtRule') {
|
|
2775
|
+
if (node.nam == 'media' && node.val == 'all') {
|
|
2776
|
+
// @ts-ignore
|
|
2777
|
+
ast.chi?.splice(i, 1, ...node.chi);
|
|
2778
|
+
i--;
|
|
2779
|
+
continue;
|
|
2780
|
+
}
|
|
2781
|
+
// console.debug({previous, node});
|
|
2773
2782
|
// @ts-ignore
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2783
|
+
if (previous?.typ == 'AtRule' &&
|
|
2784
|
+
previous.nam == node.nam &&
|
|
2785
|
+
previous.val == node.val) {
|
|
2786
|
+
if ('chi' in node) {
|
|
2787
|
+
// @ts-ignore
|
|
2788
|
+
previous.chi.push(...node.chi);
|
|
2789
|
+
}
|
|
2790
|
+
// else {
|
|
2791
|
+
ast?.chi?.splice(i--, 1);
|
|
2792
|
+
continue;
|
|
2793
|
+
// }
|
|
2794
|
+
}
|
|
2777
2795
|
}
|
|
2778
2796
|
// @ts-ignore
|
|
2779
2797
|
if (node.typ == 'Rule') {
|
|
@@ -3288,10 +3306,11 @@
|
|
|
3288
3306
|
}
|
|
3289
3307
|
buffer += quoteStr;
|
|
3290
3308
|
while (value = peek()) {
|
|
3291
|
-
if (ind >= iterator.length) {
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3309
|
+
// if (ind >= iterator.length) {
|
|
3310
|
+
//
|
|
3311
|
+
// yield pushToken(buffer, hasNewLine ? 'Bad-string' : 'Unclosed-string');
|
|
3312
|
+
// break;
|
|
3313
|
+
// }
|
|
3295
3314
|
if (value == '\\') {
|
|
3296
3315
|
const sequence = peek(6);
|
|
3297
3316
|
let escapeSequence = '';
|
|
@@ -3314,7 +3333,7 @@
|
|
|
3314
3333
|
// not hex or new line
|
|
3315
3334
|
// @ts-ignore
|
|
3316
3335
|
if (i == 1 && !isNewLine(codepoint)) {
|
|
3317
|
-
buffer += sequence[i];
|
|
3336
|
+
buffer += value + sequence[i];
|
|
3318
3337
|
next(2);
|
|
3319
3338
|
continue;
|
|
3320
3339
|
}
|
|
@@ -3334,11 +3353,12 @@
|
|
|
3334
3353
|
continue;
|
|
3335
3354
|
}
|
|
3336
3355
|
// buffer += value;
|
|
3337
|
-
if (ind >= iterator.length) {
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3356
|
+
// if (ind >= iterator.length) {
|
|
3357
|
+
//
|
|
3358
|
+
// // drop '\\' at the end
|
|
3359
|
+
// yield pushToken(buffer);
|
|
3360
|
+
// break;
|
|
3361
|
+
// }
|
|
3342
3362
|
buffer += next(2);
|
|
3343
3363
|
continue;
|
|
3344
3364
|
}
|
|
@@ -3507,7 +3527,7 @@
|
|
|
3507
3527
|
buffer = '';
|
|
3508
3528
|
break;
|
|
3509
3529
|
}
|
|
3510
|
-
buffer += value;
|
|
3530
|
+
buffer += prev() + value;
|
|
3511
3531
|
break;
|
|
3512
3532
|
case '"':
|
|
3513
3533
|
case "'":
|
|
@@ -3709,6 +3729,7 @@
|
|
|
3709
3729
|
* @param opt
|
|
3710
3730
|
*/
|
|
3711
3731
|
async function parse$1(iterator, opt = {}) {
|
|
3732
|
+
const startTime = performance.now();
|
|
3712
3733
|
const errors = [];
|
|
3713
3734
|
const options = {
|
|
3714
3735
|
src: '',
|
|
@@ -3847,7 +3868,7 @@
|
|
|
3847
3868
|
src: options.resolve(url, options.src).absolute
|
|
3848
3869
|
}));
|
|
3849
3870
|
});
|
|
3850
|
-
bytesIn += root.bytesIn;
|
|
3871
|
+
bytesIn += root.stats.bytesIn;
|
|
3851
3872
|
if (root.ast.chi.length > 0) {
|
|
3852
3873
|
context.chi.push(...root.ast.chi);
|
|
3853
3874
|
}
|
|
@@ -3893,13 +3914,6 @@
|
|
|
3893
3914
|
// rule
|
|
3894
3915
|
if (delim.typ == 'Block-start') {
|
|
3895
3916
|
const position = map.get(tokens[0]);
|
|
3896
|
-
// if (context.typ == 'Rule') {
|
|
3897
|
-
//
|
|
3898
|
-
// if (tokens[0]?.typ == 'Iden') {
|
|
3899
|
-
// errors.push({action: 'drop', message: 'invalid nesting rule', location: {src, ...position}});
|
|
3900
|
-
// return null;
|
|
3901
|
-
// }
|
|
3902
|
-
// }
|
|
3903
3917
|
const uniq = new Map;
|
|
3904
3918
|
parseTokens(tokens, { minify: options.minify }).reduce((acc, curr, index, array) => {
|
|
3905
3919
|
if (curr.typ == 'Whitespace') {
|
|
@@ -4075,12 +4089,21 @@
|
|
|
4075
4089
|
if (tokens.length > 0) {
|
|
4076
4090
|
await parseNode(tokens);
|
|
4077
4091
|
}
|
|
4092
|
+
const endParseTime = performance.now();
|
|
4078
4093
|
if (options.minify) {
|
|
4079
4094
|
if (ast.chi.length > 0) {
|
|
4080
4095
|
minify(ast, options, true);
|
|
4081
4096
|
}
|
|
4082
4097
|
}
|
|
4083
|
-
|
|
4098
|
+
const endTime = performance.now();
|
|
4099
|
+
return {
|
|
4100
|
+
ast, errors, stats: {
|
|
4101
|
+
bytesIn,
|
|
4102
|
+
parse: `${(endParseTime - startTime).toFixed(2)}ms`,
|
|
4103
|
+
minify: `${(endTime - endParseTime).toFixed(2)}ms`,
|
|
4104
|
+
total: `${(endTime - startTime).toFixed(2)}ms`
|
|
4105
|
+
}
|
|
4106
|
+
};
|
|
4084
4107
|
}
|
|
4085
4108
|
function parseString(src, options = { location: false }) {
|
|
4086
4109
|
return [...tokenize(src)].map(t => {
|
|
@@ -4431,19 +4454,17 @@
|
|
|
4431
4454
|
async function transform$1(css, options = {}) {
|
|
4432
4455
|
options = { minify: true, removeEmpty: true, ...options };
|
|
4433
4456
|
const startTime = performance.now();
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
}
|
|
4446
|
-
};
|
|
4457
|
+
return parse$1(css, options).then((parseResult) => {
|
|
4458
|
+
const rendered = render(parseResult.ast, options);
|
|
4459
|
+
return {
|
|
4460
|
+
...parseResult, ...rendered, stats: {
|
|
4461
|
+
bytesOut: rendered.code.length,
|
|
4462
|
+
...parseResult.stats,
|
|
4463
|
+
render: rendered.stats.total,
|
|
4464
|
+
total: `${(performance.now() - startTime).toFixed(2)}ms`
|
|
4465
|
+
}
|
|
4466
|
+
};
|
|
4467
|
+
});
|
|
4447
4468
|
}
|
|
4448
4469
|
|
|
4449
4470
|
const matchUrl = /^(https?:)?\/\//;
|
package/dist/index.cjs
CHANGED
|
@@ -91,10 +91,7 @@ function isHash(name) {
|
|
|
91
91
|
if (name.charAt(0) != '#') {
|
|
92
92
|
return false;
|
|
93
93
|
}
|
|
94
|
-
|
|
95
|
-
return true;
|
|
96
|
-
}
|
|
97
|
-
return true;
|
|
94
|
+
return isIdent(name.charAt(1));
|
|
98
95
|
}
|
|
99
96
|
function isNumber(name) {
|
|
100
97
|
if (name.length == 0) {
|
|
@@ -1564,6 +1561,7 @@ function hsl2rgb(h, s, l, a = null) {
|
|
|
1564
1561
|
}
|
|
1565
1562
|
|
|
1566
1563
|
function render(data, opt = {}) {
|
|
1564
|
+
const startTime = performance.now();
|
|
1567
1565
|
const options = Object.assign(opt.minify ?? true ? {
|
|
1568
1566
|
indent: '',
|
|
1569
1567
|
newLine: '',
|
|
@@ -1582,7 +1580,9 @@ function render(data, opt = {}) {
|
|
|
1582
1580
|
}
|
|
1583
1581
|
return acc + renderToken(curr, options);
|
|
1584
1582
|
}
|
|
1585
|
-
return { code: doRender(data, options, reducer, 0)
|
|
1583
|
+
return { code: doRender(data, options, reducer, 0), stats: {
|
|
1584
|
+
total: `${(performance.now() - startTime).toFixed(2)}ms`
|
|
1585
|
+
} };
|
|
1586
1586
|
}
|
|
1587
1587
|
// @ts-ignore
|
|
1588
1588
|
function doRender(data, options, reducer, level = 0, indents = []) {
|
|
@@ -1611,7 +1611,7 @@ function doRender(data, options, reducer, level = 0, indents = []) {
|
|
|
1611
1611
|
case 'AtRule':
|
|
1612
1612
|
case 'Rule':
|
|
1613
1613
|
if (data.typ == 'AtRule' && !('chi' in data)) {
|
|
1614
|
-
return `${indent}@${data.nam} ${data.val};`;
|
|
1614
|
+
return `${indent}@${data.nam}${data.val === '' ? '' : options.indent || ' '}${data.val};`;
|
|
1615
1615
|
}
|
|
1616
1616
|
// @ts-ignore
|
|
1617
1617
|
let children = data.chi.reduce((css, node) => {
|
|
@@ -1623,7 +1623,7 @@ function doRender(data, options, reducer, level = 0, indents = []) {
|
|
|
1623
1623
|
str = `${node.nam}:${options.indent}${node.val.reduce(reducer, '').trimEnd()};`;
|
|
1624
1624
|
}
|
|
1625
1625
|
else if (node.typ == 'AtRule' && !('chi' in node)) {
|
|
1626
|
-
str =
|
|
1626
|
+
str = `${data.val === '' ? '' : options.indent || ' '}${data.val};`;
|
|
1627
1627
|
}
|
|
1628
1628
|
else {
|
|
1629
1629
|
str = doRender(node, options, reducer, level + 1, indents);
|
|
@@ -1640,7 +1640,7 @@ function doRender(data, options, reducer, level = 0, indents = []) {
|
|
|
1640
1640
|
children = children.slice(0, -1);
|
|
1641
1641
|
}
|
|
1642
1642
|
if (data.typ == 'AtRule') {
|
|
1643
|
-
return `@${data.nam}${data.val
|
|
1643
|
+
return `@${data.nam}${data.val === '' ? '' : options.indent || ' '}${data.val}${options.indent}{${options.newLine}` + (children === '' ? '' : indentSub + children + options.newLine) + indent + `}`;
|
|
1644
1644
|
}
|
|
1645
1645
|
return data.sel + `${options.indent}{${options.newLine}` + (children === '' ? '' : indentSub + children + options.newLine) + indent + `}`;
|
|
1646
1646
|
}
|
|
@@ -1790,7 +1790,9 @@ function renderToken(token, options = {}) {
|
|
|
1790
1790
|
case 'Delim':
|
|
1791
1791
|
return /* options.minify && 'Pseudo-class' == token.typ && '::' == token.val.slice(0, 2) ? token.val.slice(1) : */ token.val;
|
|
1792
1792
|
}
|
|
1793
|
-
|
|
1793
|
+
console.error(`unexpected token ${JSON.stringify(token, null, 1)}`);
|
|
1794
|
+
// throw new Error(`unexpected token ${JSON.stringify(token, null, 1)}`);
|
|
1795
|
+
return '';
|
|
1794
1796
|
}
|
|
1795
1797
|
|
|
1796
1798
|
function eq(a, b) {
|
|
@@ -2579,7 +2581,7 @@ function minify(ast, options = {}, recursive = false) {
|
|
|
2579
2581
|
// @ts-ignore
|
|
2580
2582
|
return null;
|
|
2581
2583
|
}
|
|
2582
|
-
return { result, node1: exchanged ? node2 : node1, node2: exchanged ?
|
|
2584
|
+
return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node1 : node2 };
|
|
2583
2585
|
}
|
|
2584
2586
|
function matchSelectors(selector1, selector2, parentType) {
|
|
2585
2587
|
let match = [[]];
|
|
@@ -2767,11 +2769,27 @@ function minify(ast, options = {}, recursive = false) {
|
|
|
2767
2769
|
if (node.typ == 'AtRule' && node.nam == 'font-face') {
|
|
2768
2770
|
continue;
|
|
2769
2771
|
}
|
|
2770
|
-
if (node.typ == 'AtRule'
|
|
2772
|
+
if (node.typ == 'AtRule') {
|
|
2773
|
+
if (node.nam == 'media' && node.val == 'all') {
|
|
2774
|
+
// @ts-ignore
|
|
2775
|
+
ast.chi?.splice(i, 1, ...node.chi);
|
|
2776
|
+
i--;
|
|
2777
|
+
continue;
|
|
2778
|
+
}
|
|
2779
|
+
// console.debug({previous, node});
|
|
2771
2780
|
// @ts-ignore
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2781
|
+
if (previous?.typ == 'AtRule' &&
|
|
2782
|
+
previous.nam == node.nam &&
|
|
2783
|
+
previous.val == node.val) {
|
|
2784
|
+
if ('chi' in node) {
|
|
2785
|
+
// @ts-ignore
|
|
2786
|
+
previous.chi.push(...node.chi);
|
|
2787
|
+
}
|
|
2788
|
+
// else {
|
|
2789
|
+
ast?.chi?.splice(i--, 1);
|
|
2790
|
+
continue;
|
|
2791
|
+
// }
|
|
2792
|
+
}
|
|
2775
2793
|
}
|
|
2776
2794
|
// @ts-ignore
|
|
2777
2795
|
if (node.typ == 'Rule') {
|
|
@@ -3286,10 +3304,11 @@ function* tokenize(iterator) {
|
|
|
3286
3304
|
}
|
|
3287
3305
|
buffer += quoteStr;
|
|
3288
3306
|
while (value = peek()) {
|
|
3289
|
-
if (ind >= iterator.length) {
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3307
|
+
// if (ind >= iterator.length) {
|
|
3308
|
+
//
|
|
3309
|
+
// yield pushToken(buffer, hasNewLine ? 'Bad-string' : 'Unclosed-string');
|
|
3310
|
+
// break;
|
|
3311
|
+
// }
|
|
3293
3312
|
if (value == '\\') {
|
|
3294
3313
|
const sequence = peek(6);
|
|
3295
3314
|
let escapeSequence = '';
|
|
@@ -3312,7 +3331,7 @@ function* tokenize(iterator) {
|
|
|
3312
3331
|
// not hex or new line
|
|
3313
3332
|
// @ts-ignore
|
|
3314
3333
|
if (i == 1 && !isNewLine(codepoint)) {
|
|
3315
|
-
buffer += sequence[i];
|
|
3334
|
+
buffer += value + sequence[i];
|
|
3316
3335
|
next(2);
|
|
3317
3336
|
continue;
|
|
3318
3337
|
}
|
|
@@ -3332,11 +3351,12 @@ function* tokenize(iterator) {
|
|
|
3332
3351
|
continue;
|
|
3333
3352
|
}
|
|
3334
3353
|
// buffer += value;
|
|
3335
|
-
if (ind >= iterator.length) {
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3354
|
+
// if (ind >= iterator.length) {
|
|
3355
|
+
//
|
|
3356
|
+
// // drop '\\' at the end
|
|
3357
|
+
// yield pushToken(buffer);
|
|
3358
|
+
// break;
|
|
3359
|
+
// }
|
|
3340
3360
|
buffer += next(2);
|
|
3341
3361
|
continue;
|
|
3342
3362
|
}
|
|
@@ -3505,7 +3525,7 @@ function* tokenize(iterator) {
|
|
|
3505
3525
|
buffer = '';
|
|
3506
3526
|
break;
|
|
3507
3527
|
}
|
|
3508
|
-
buffer += value;
|
|
3528
|
+
buffer += prev() + value;
|
|
3509
3529
|
break;
|
|
3510
3530
|
case '"':
|
|
3511
3531
|
case "'":
|
|
@@ -3707,6 +3727,7 @@ const funcLike = ['Start-parens', 'Func', 'UrlFunc', 'Pseudo-class-func'];
|
|
|
3707
3727
|
* @param opt
|
|
3708
3728
|
*/
|
|
3709
3729
|
async function parse$1(iterator, opt = {}) {
|
|
3730
|
+
const startTime = performance.now();
|
|
3710
3731
|
const errors = [];
|
|
3711
3732
|
const options = {
|
|
3712
3733
|
src: '',
|
|
@@ -3845,7 +3866,7 @@ async function parse$1(iterator, opt = {}) {
|
|
|
3845
3866
|
src: options.resolve(url, options.src).absolute
|
|
3846
3867
|
}));
|
|
3847
3868
|
});
|
|
3848
|
-
bytesIn += root.bytesIn;
|
|
3869
|
+
bytesIn += root.stats.bytesIn;
|
|
3849
3870
|
if (root.ast.chi.length > 0) {
|
|
3850
3871
|
context.chi.push(...root.ast.chi);
|
|
3851
3872
|
}
|
|
@@ -3891,13 +3912,6 @@ async function parse$1(iterator, opt = {}) {
|
|
|
3891
3912
|
// rule
|
|
3892
3913
|
if (delim.typ == 'Block-start') {
|
|
3893
3914
|
const position = map.get(tokens[0]);
|
|
3894
|
-
// if (context.typ == 'Rule') {
|
|
3895
|
-
//
|
|
3896
|
-
// if (tokens[0]?.typ == 'Iden') {
|
|
3897
|
-
// errors.push({action: 'drop', message: 'invalid nesting rule', location: {src, ...position}});
|
|
3898
|
-
// return null;
|
|
3899
|
-
// }
|
|
3900
|
-
// }
|
|
3901
3915
|
const uniq = new Map;
|
|
3902
3916
|
parseTokens(tokens, { minify: options.minify }).reduce((acc, curr, index, array) => {
|
|
3903
3917
|
if (curr.typ == 'Whitespace') {
|
|
@@ -4073,12 +4087,21 @@ async function parse$1(iterator, opt = {}) {
|
|
|
4073
4087
|
if (tokens.length > 0) {
|
|
4074
4088
|
await parseNode(tokens);
|
|
4075
4089
|
}
|
|
4090
|
+
const endParseTime = performance.now();
|
|
4076
4091
|
if (options.minify) {
|
|
4077
4092
|
if (ast.chi.length > 0) {
|
|
4078
4093
|
minify(ast, options, true);
|
|
4079
4094
|
}
|
|
4080
4095
|
}
|
|
4081
|
-
|
|
4096
|
+
const endTime = performance.now();
|
|
4097
|
+
return {
|
|
4098
|
+
ast, errors, stats: {
|
|
4099
|
+
bytesIn,
|
|
4100
|
+
parse: `${(endParseTime - startTime).toFixed(2)}ms`,
|
|
4101
|
+
minify: `${(endTime - endParseTime).toFixed(2)}ms`,
|
|
4102
|
+
total: `${(endTime - startTime).toFixed(2)}ms`
|
|
4103
|
+
}
|
|
4104
|
+
};
|
|
4082
4105
|
}
|
|
4083
4106
|
function parseString(src, options = { location: false }) {
|
|
4084
4107
|
return [...tokenize(src)].map(t => {
|
|
@@ -4429,19 +4452,17 @@ function parseTokens(tokens, options = {}) {
|
|
|
4429
4452
|
async function transform$1(css, options = {}) {
|
|
4430
4453
|
options = { minify: true, removeEmpty: true, ...options };
|
|
4431
4454
|
const startTime = performance.now();
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
}
|
|
4444
|
-
};
|
|
4455
|
+
return parse$1(css, options).then((parseResult) => {
|
|
4456
|
+
const rendered = render(parseResult.ast, options);
|
|
4457
|
+
return {
|
|
4458
|
+
...parseResult, ...rendered, stats: {
|
|
4459
|
+
bytesOut: rendered.code.length,
|
|
4460
|
+
...parseResult.stats,
|
|
4461
|
+
render: rendered.stats.total,
|
|
4462
|
+
total: `${(performance.now() - startTime).toFixed(2)}ms`
|
|
4463
|
+
}
|
|
4464
|
+
};
|
|
4465
|
+
});
|
|
4445
4466
|
}
|
|
4446
4467
|
|
|
4447
4468
|
const matchUrl = /^(https?:)?\/\//;
|
package/dist/index.d.ts
CHANGED
|
@@ -487,11 +487,19 @@ interface TransformOptions extends ParserOptions, RenderOptions {
|
|
|
487
487
|
interface ParseResult {
|
|
488
488
|
ast: AstRuleStyleSheet;
|
|
489
489
|
errors: ErrorDescription[];
|
|
490
|
-
|
|
490
|
+
stats: {
|
|
491
|
+
bytesIn: number;
|
|
492
|
+
parse: string;
|
|
493
|
+
minify: string;
|
|
494
|
+
total: string;
|
|
495
|
+
}
|
|
491
496
|
}
|
|
492
497
|
|
|
493
498
|
interface RenderResult {
|
|
494
499
|
code: string ;
|
|
500
|
+
stats: {
|
|
501
|
+
total: string;
|
|
502
|
+
}
|
|
495
503
|
}
|
|
496
504
|
|
|
497
505
|
interface TransformResult extends ParseResult, RenderResult {
|
|
@@ -500,6 +508,7 @@ interface TransformResult extends ParseResult, RenderResult {
|
|
|
500
508
|
bytesIn: number;
|
|
501
509
|
bytesOut: number;
|
|
502
510
|
parse: string;
|
|
511
|
+
minify: string;
|
|
503
512
|
render: string;
|
|
504
513
|
total: string;
|
|
505
514
|
}
|
package/dist/lib/ast/minify.js
CHANGED
|
@@ -135,7 +135,7 @@ function minify(ast, options = {}, recursive = false) {
|
|
|
135
135
|
// @ts-ignore
|
|
136
136
|
return null;
|
|
137
137
|
}
|
|
138
|
-
return { result, node1: exchanged ? node2 : node1, node2: exchanged ?
|
|
138
|
+
return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node1 : node2 };
|
|
139
139
|
}
|
|
140
140
|
function matchSelectors(selector1, selector2, parentType) {
|
|
141
141
|
let match = [[]];
|
|
@@ -323,11 +323,27 @@ function minify(ast, options = {}, recursive = false) {
|
|
|
323
323
|
if (node.typ == 'AtRule' && node.nam == 'font-face') {
|
|
324
324
|
continue;
|
|
325
325
|
}
|
|
326
|
-
if (node.typ == 'AtRule'
|
|
326
|
+
if (node.typ == 'AtRule') {
|
|
327
|
+
if (node.nam == 'media' && node.val == 'all') {
|
|
328
|
+
// @ts-ignore
|
|
329
|
+
ast.chi?.splice(i, 1, ...node.chi);
|
|
330
|
+
i--;
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
// console.debug({previous, node});
|
|
327
334
|
// @ts-ignore
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
335
|
+
if (previous?.typ == 'AtRule' &&
|
|
336
|
+
previous.nam == node.nam &&
|
|
337
|
+
previous.val == node.val) {
|
|
338
|
+
if ('chi' in node) {
|
|
339
|
+
// @ts-ignore
|
|
340
|
+
previous.chi.push(...node.chi);
|
|
341
|
+
}
|
|
342
|
+
// else {
|
|
343
|
+
ast?.chi?.splice(i--, 1);
|
|
344
|
+
continue;
|
|
345
|
+
// }
|
|
346
|
+
}
|
|
331
347
|
}
|
|
332
348
|
// @ts-ignore
|
|
333
349
|
if (node.typ == 'Rule') {
|
package/dist/lib/parser/parse.js
CHANGED
|
@@ -12,6 +12,7 @@ const funcLike = ['Start-parens', 'Func', 'UrlFunc', 'Pseudo-class-func'];
|
|
|
12
12
|
* @param opt
|
|
13
13
|
*/
|
|
14
14
|
async function parse(iterator, opt = {}) {
|
|
15
|
+
const startTime = performance.now();
|
|
15
16
|
const errors = [];
|
|
16
17
|
const options = {
|
|
17
18
|
src: '',
|
|
@@ -150,7 +151,7 @@ async function parse(iterator, opt = {}) {
|
|
|
150
151
|
src: options.resolve(url, options.src).absolute
|
|
151
152
|
}));
|
|
152
153
|
});
|
|
153
|
-
bytesIn += root.bytesIn;
|
|
154
|
+
bytesIn += root.stats.bytesIn;
|
|
154
155
|
if (root.ast.chi.length > 0) {
|
|
155
156
|
context.chi.push(...root.ast.chi);
|
|
156
157
|
}
|
|
@@ -196,13 +197,6 @@ async function parse(iterator, opt = {}) {
|
|
|
196
197
|
// rule
|
|
197
198
|
if (delim.typ == 'Block-start') {
|
|
198
199
|
const position = map.get(tokens[0]);
|
|
199
|
-
// if (context.typ == 'Rule') {
|
|
200
|
-
//
|
|
201
|
-
// if (tokens[0]?.typ == 'Iden') {
|
|
202
|
-
// errors.push({action: 'drop', message: 'invalid nesting rule', location: {src, ...position}});
|
|
203
|
-
// return null;
|
|
204
|
-
// }
|
|
205
|
-
// }
|
|
206
200
|
const uniq = new Map;
|
|
207
201
|
parseTokens(tokens, { minify: options.minify }).reduce((acc, curr, index, array) => {
|
|
208
202
|
if (curr.typ == 'Whitespace') {
|
|
@@ -378,12 +372,21 @@ async function parse(iterator, opt = {}) {
|
|
|
378
372
|
if (tokens.length > 0) {
|
|
379
373
|
await parseNode(tokens);
|
|
380
374
|
}
|
|
375
|
+
const endParseTime = performance.now();
|
|
381
376
|
if (options.minify) {
|
|
382
377
|
if (ast.chi.length > 0) {
|
|
383
378
|
minify(ast, options, true);
|
|
384
379
|
}
|
|
385
380
|
}
|
|
386
|
-
|
|
381
|
+
const endTime = performance.now();
|
|
382
|
+
return {
|
|
383
|
+
ast, errors, stats: {
|
|
384
|
+
bytesIn,
|
|
385
|
+
parse: `${(endParseTime - startTime).toFixed(2)}ms`,
|
|
386
|
+
minify: `${(endTime - endParseTime).toFixed(2)}ms`,
|
|
387
|
+
total: `${(endTime - startTime).toFixed(2)}ms`
|
|
388
|
+
}
|
|
389
|
+
};
|
|
387
390
|
}
|
|
388
391
|
function parseString(src, options = { location: false }) {
|
|
389
392
|
return [...tokenize(src)].map(t => {
|
|
@@ -36,10 +36,11 @@ function* tokenize(iterator) {
|
|
|
36
36
|
}
|
|
37
37
|
buffer += quoteStr;
|
|
38
38
|
while (value = peek()) {
|
|
39
|
-
if (ind >= iterator.length) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
// if (ind >= iterator.length) {
|
|
40
|
+
//
|
|
41
|
+
// yield pushToken(buffer, hasNewLine ? 'Bad-string' : 'Unclosed-string');
|
|
42
|
+
// break;
|
|
43
|
+
// }
|
|
43
44
|
if (value == '\\') {
|
|
44
45
|
const sequence = peek(6);
|
|
45
46
|
let escapeSequence = '';
|
|
@@ -62,7 +63,7 @@ function* tokenize(iterator) {
|
|
|
62
63
|
// not hex or new line
|
|
63
64
|
// @ts-ignore
|
|
64
65
|
if (i == 1 && !isNewLine(codepoint)) {
|
|
65
|
-
buffer += sequence[i];
|
|
66
|
+
buffer += value + sequence[i];
|
|
66
67
|
next(2);
|
|
67
68
|
continue;
|
|
68
69
|
}
|
|
@@ -82,11 +83,12 @@ function* tokenize(iterator) {
|
|
|
82
83
|
continue;
|
|
83
84
|
}
|
|
84
85
|
// buffer += value;
|
|
85
|
-
if (ind >= iterator.length) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
// if (ind >= iterator.length) {
|
|
87
|
+
//
|
|
88
|
+
// // drop '\\' at the end
|
|
89
|
+
// yield pushToken(buffer);
|
|
90
|
+
// break;
|
|
91
|
+
// }
|
|
90
92
|
buffer += next(2);
|
|
91
93
|
continue;
|
|
92
94
|
}
|
|
@@ -255,7 +257,7 @@ function* tokenize(iterator) {
|
|
|
255
257
|
buffer = '';
|
|
256
258
|
break;
|
|
257
259
|
}
|
|
258
|
-
buffer += value;
|
|
260
|
+
buffer += prev() + value;
|
|
259
261
|
break;
|
|
260
262
|
case '"':
|
|
261
263
|
case "'":
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { COLORS_NAMES, rgb2Hex, hsl2Hex, hwb2hex, cmyk2hex, NAMES_COLORS } from './utils/color.js';
|
|
2
2
|
|
|
3
3
|
function render(data, opt = {}) {
|
|
4
|
+
const startTime = performance.now();
|
|
4
5
|
const options = Object.assign(opt.minify ?? true ? {
|
|
5
6
|
indent: '',
|
|
6
7
|
newLine: '',
|
|
@@ -19,7 +20,9 @@ function render(data, opt = {}) {
|
|
|
19
20
|
}
|
|
20
21
|
return acc + renderToken(curr, options);
|
|
21
22
|
}
|
|
22
|
-
return { code: doRender(data, options, reducer, 0)
|
|
23
|
+
return { code: doRender(data, options, reducer, 0), stats: {
|
|
24
|
+
total: `${(performance.now() - startTime).toFixed(2)}ms`
|
|
25
|
+
} };
|
|
23
26
|
}
|
|
24
27
|
// @ts-ignore
|
|
25
28
|
function doRender(data, options, reducer, level = 0, indents = []) {
|
|
@@ -48,7 +51,7 @@ function doRender(data, options, reducer, level = 0, indents = []) {
|
|
|
48
51
|
case 'AtRule':
|
|
49
52
|
case 'Rule':
|
|
50
53
|
if (data.typ == 'AtRule' && !('chi' in data)) {
|
|
51
|
-
return `${indent}@${data.nam} ${data.val};`;
|
|
54
|
+
return `${indent}@${data.nam}${data.val === '' ? '' : options.indent || ' '}${data.val};`;
|
|
52
55
|
}
|
|
53
56
|
// @ts-ignore
|
|
54
57
|
let children = data.chi.reduce((css, node) => {
|
|
@@ -60,7 +63,7 @@ function doRender(data, options, reducer, level = 0, indents = []) {
|
|
|
60
63
|
str = `${node.nam}:${options.indent}${node.val.reduce(reducer, '').trimEnd()};`;
|
|
61
64
|
}
|
|
62
65
|
else if (node.typ == 'AtRule' && !('chi' in node)) {
|
|
63
|
-
str =
|
|
66
|
+
str = `${data.val === '' ? '' : options.indent || ' '}${data.val};`;
|
|
64
67
|
}
|
|
65
68
|
else {
|
|
66
69
|
str = doRender(node, options, reducer, level + 1, indents);
|
|
@@ -77,7 +80,7 @@ function doRender(data, options, reducer, level = 0, indents = []) {
|
|
|
77
80
|
children = children.slice(0, -1);
|
|
78
81
|
}
|
|
79
82
|
if (data.typ == 'AtRule') {
|
|
80
|
-
return `@${data.nam}${data.val
|
|
83
|
+
return `@${data.nam}${data.val === '' ? '' : options.indent || ' '}${data.val}${options.indent}{${options.newLine}` + (children === '' ? '' : indentSub + children + options.newLine) + indent + `}`;
|
|
81
84
|
}
|
|
82
85
|
return data.sel + `${options.indent}{${options.newLine}` + (children === '' ? '' : indentSub + children + options.newLine) + indent + `}`;
|
|
83
86
|
}
|
|
@@ -227,7 +230,9 @@ function renderToken(token, options = {}) {
|
|
|
227
230
|
case 'Delim':
|
|
228
231
|
return /* options.minify && 'Pseudo-class' == token.typ && '::' == token.val.slice(0, 2) ? token.val.slice(1) : */ token.val;
|
|
229
232
|
}
|
|
230
|
-
|
|
233
|
+
console.error(`unexpected token ${JSON.stringify(token, null, 1)}`);
|
|
234
|
+
// throw new Error(`unexpected token ${JSON.stringify(token, null, 1)}`);
|
|
235
|
+
return '';
|
|
231
236
|
}
|
|
232
237
|
|
|
233
238
|
export { render, renderToken };
|
package/dist/lib/transform.js
CHANGED
|
@@ -4,19 +4,17 @@ import { render } from './renderer/render.js';
|
|
|
4
4
|
async function transform(css, options = {}) {
|
|
5
5
|
options = { minify: true, removeEmpty: true, ...options };
|
|
6
6
|
const startTime = performance.now();
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
};
|
|
7
|
+
return parse(css, options).then((parseResult) => {
|
|
8
|
+
const rendered = render(parseResult.ast, options);
|
|
9
|
+
return {
|
|
10
|
+
...parseResult, ...rendered, stats: {
|
|
11
|
+
bytesOut: rendered.code.length,
|
|
12
|
+
...parseResult.stats,
|
|
13
|
+
render: rendered.stats.total,
|
|
14
|
+
total: `${(performance.now() - startTime).toFixed(2)}ms`
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
});
|
|
20
18
|
}
|
|
21
19
|
|
|
22
20
|
export { transform };
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tbela99/css-parser",
|
|
3
3
|
"description": "CSS parser for node and the browser",
|
|
4
|
-
"version": "0.0.1-
|
|
4
|
+
"version": "0.0.1-rc2",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dist/index.js",
|
|
7
|
+
"./umd": "./dist/index-umd-web.js",
|
|
7
8
|
"./web": "./dist/web/index.js",
|
|
8
9
|
"./cjs": "./dist/index.cjs"
|
|
9
10
|
},
|