@tbela99/css-parser 0.0.1-rc2 → 0.0.1-rc3
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 +59 -3
- package/dist/index-umd-web.js +66 -41
- package/dist/index.cjs +66 -41
- package/dist/index.d.ts +27 -1
- package/dist/index.js +1 -0
- package/dist/lib/parser/declaration/map.js +43 -29
- package/dist/lib/parser/parse.js +2 -2
- package/dist/lib/parser/utils/type.js +4 -0
- package/dist/lib/renderer/render.js +6 -0
- package/dist/web/index.js +1 -0
- package/package.json +9 -10
- package/.gitattributes +0 -22
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ $ npm install @tbela99/css-parser
|
|
|
15
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
|
- replace @import at-rules with actual css content of the imported rule
|
|
18
|
-
- automatically
|
|
18
|
+
- automatically generate nested css rules
|
|
19
19
|
- works the same way in node and web browser
|
|
20
20
|
|
|
21
21
|
### Performance
|
|
@@ -79,7 +79,7 @@ parse(css, parseOptions = {})
|
|
|
79
79
|
|
|
80
80
|
````javascript
|
|
81
81
|
|
|
82
|
-
const {ast, errors} = await parse(css);
|
|
82
|
+
const {ast, errors, stats} = await parse(css);
|
|
83
83
|
````
|
|
84
84
|
|
|
85
85
|
## Rendering
|
|
@@ -96,7 +96,7 @@ render(ast, RenderOptions = {});
|
|
|
96
96
|
import {render} from '@tbela99/css-parser';
|
|
97
97
|
|
|
98
98
|
// minified
|
|
99
|
-
const {code} = render(ast, {minify: true});
|
|
99
|
+
const {code, stats} = render(ast, {minify: true});
|
|
100
100
|
|
|
101
101
|
console.log(code);
|
|
102
102
|
```
|
|
@@ -160,6 +160,62 @@ Single JavaScript file
|
|
|
160
160
|
<script src="dist/index-umd-web.js"></script>
|
|
161
161
|
```
|
|
162
162
|
|
|
163
|
+
## Example
|
|
164
|
+
|
|
165
|
+
### Automatic CSS Nesting
|
|
166
|
+
|
|
167
|
+
CSS
|
|
168
|
+
|
|
169
|
+
```css
|
|
170
|
+
|
|
171
|
+
table.colortable td {
|
|
172
|
+
text-align:center;
|
|
173
|
+
}
|
|
174
|
+
table.colortable td.c {
|
|
175
|
+
text-transform:uppercase;
|
|
176
|
+
}
|
|
177
|
+
table.colortable td:first-child, table.colortable td:first-child+td {
|
|
178
|
+
border:1px solid black;
|
|
179
|
+
}
|
|
180
|
+
table.colortable th {
|
|
181
|
+
text-align:center;
|
|
182
|
+
background:black;
|
|
183
|
+
color:white;
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Javascript
|
|
188
|
+
```javascript
|
|
189
|
+
import {parse, render} from '@tbela99/css-parser';
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
const options = {minify: true, nestingRules: true};
|
|
193
|
+
|
|
194
|
+
const {code} = await parse(css, options).then(result => render(result.ast, {minify: false}));
|
|
195
|
+
//
|
|
196
|
+
console.debug(code);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Result
|
|
200
|
+
```css
|
|
201
|
+
table.colortable {
|
|
202
|
+
& td {
|
|
203
|
+
text-align: center;
|
|
204
|
+
&.c {
|
|
205
|
+
text-transform: uppercase
|
|
206
|
+
}
|
|
207
|
+
&:first-child,&:first-child+td {
|
|
208
|
+
border: 1px solid #000
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
& th {
|
|
212
|
+
text-align: center;
|
|
213
|
+
background: #000;
|
|
214
|
+
color: #fff
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
163
219
|
## AST
|
|
164
220
|
|
|
165
221
|
### Comment
|
package/dist/index-umd-web.js
CHANGED
|
@@ -1069,6 +1069,21 @@
|
|
|
1069
1069
|
|
|
1070
1070
|
const getConfig = () => config$1;
|
|
1071
1071
|
|
|
1072
|
+
const funcList = ['clamp', 'calc'];
|
|
1073
|
+
function matchType(val, properties) {
|
|
1074
|
+
if (val.typ == 'Iden' && properties.keywords.includes(val.val) ||
|
|
1075
|
+
(properties.types.includes(val.typ))) {
|
|
1076
|
+
return true;
|
|
1077
|
+
}
|
|
1078
|
+
if (val.typ == 'Number' && val.val == '0') {
|
|
1079
|
+
return properties.types.some(type => type == 'Length' || type == 'Angle');
|
|
1080
|
+
}
|
|
1081
|
+
if (val.typ == 'Func' && funcList.includes(val.val)) {
|
|
1082
|
+
return val.chi.every((t => ['Literal', 'Comma', 'Whitespace', 'Start-parens', 'End-parens'].includes(t.typ) || matchType(t, properties)));
|
|
1083
|
+
}
|
|
1084
|
+
return false;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1072
1087
|
// name to color
|
|
1073
1088
|
const COLORS_NAMES = Object.seal({
|
|
1074
1089
|
'aliceblue': '#f0f8ff',
|
|
@@ -1597,6 +1612,8 @@
|
|
|
1597
1612
|
const indent = indents[level];
|
|
1598
1613
|
const indentSub = indents[level + 1];
|
|
1599
1614
|
switch (data.typ) {
|
|
1615
|
+
case 'Declaration':
|
|
1616
|
+
return `${data.nam}:${options.indent}${data.val.reduce((acc, curr) => acc + renderToken(curr), '')}`;
|
|
1600
1617
|
case 'Comment':
|
|
1601
1618
|
return options.removeComments ? '' : data.val;
|
|
1602
1619
|
case 'StyleSheet':
|
|
@@ -1622,6 +1639,10 @@
|
|
|
1622
1639
|
str = options.removeComments ? '' : node.val;
|
|
1623
1640
|
}
|
|
1624
1641
|
else if (node.typ == 'Declaration') {
|
|
1642
|
+
if (node.val.length == 0) {
|
|
1643
|
+
console.error(`invalid declaration`, node);
|
|
1644
|
+
return '';
|
|
1645
|
+
}
|
|
1625
1646
|
str = `${node.nam}:${options.indent}${node.val.reduce(reducer, '').trimEnd()};`;
|
|
1626
1647
|
}
|
|
1627
1648
|
else if (node.typ == 'AtRule' && !('chi' in node)) {
|
|
@@ -2000,17 +2021,6 @@
|
|
|
2000
2021
|
}
|
|
2001
2022
|
}
|
|
2002
2023
|
|
|
2003
|
-
function matchType(val, properties) {
|
|
2004
|
-
if (val.typ == 'Iden' && properties.keywords.includes(val.val) ||
|
|
2005
|
-
(properties.types.includes(val.typ))) {
|
|
2006
|
-
return true;
|
|
2007
|
-
}
|
|
2008
|
-
if (val.typ == 'Number' && val.val == '0') {
|
|
2009
|
-
return properties.types.some(type => type == 'Length' || type == 'Angle');
|
|
2010
|
-
}
|
|
2011
|
-
return false;
|
|
2012
|
-
}
|
|
2013
|
-
|
|
2014
2024
|
const propertiesConfig = getConfig();
|
|
2015
2025
|
class PropertyMap {
|
|
2016
2026
|
config;
|
|
@@ -2025,6 +2035,9 @@
|
|
|
2025
2035
|
this.pattern = config.pattern.split(/\s/);
|
|
2026
2036
|
}
|
|
2027
2037
|
add(declaration) {
|
|
2038
|
+
for (const val of declaration.val) {
|
|
2039
|
+
Object.defineProperty(val, 'propertyName', { enumerable: false, writable: true, value: declaration.nam });
|
|
2040
|
+
}
|
|
2028
2041
|
if (declaration.nam == this.config.shorthand) {
|
|
2029
2042
|
this.declarations = new Map;
|
|
2030
2043
|
this.declarations.set(declaration.nam, declaration);
|
|
@@ -2058,7 +2071,7 @@
|
|
|
2058
2071
|
i--;
|
|
2059
2072
|
continue;
|
|
2060
2073
|
}
|
|
2061
|
-
if (matchType(acc[i], props)) {
|
|
2074
|
+
if (('propertyName' in acc[i] && acc[i].propertyName == property) || matchType(acc[i], props)) {
|
|
2062
2075
|
if ('prefix' in props && props.previous != null && !(props.previous in tokens)) {
|
|
2063
2076
|
return acc;
|
|
2064
2077
|
}
|
|
@@ -2192,10 +2205,12 @@
|
|
|
2192
2205
|
}
|
|
2193
2206
|
else {
|
|
2194
2207
|
let count = 0;
|
|
2208
|
+
let match;
|
|
2195
2209
|
const separator = this.config.separator;
|
|
2196
2210
|
const tokens = {};
|
|
2197
2211
|
// @ts-ignore
|
|
2198
|
-
/* const valid: string[] =*/
|
|
2212
|
+
/* const valid: string[] =*/
|
|
2213
|
+
Object.entries(this.config.properties).reduce((acc, curr) => {
|
|
2199
2214
|
if (!this.declarations.has(curr[0])) {
|
|
2200
2215
|
if (curr[1].required) {
|
|
2201
2216
|
acc.push(curr[0]);
|
|
@@ -2204,33 +2219,39 @@
|
|
|
2204
2219
|
}
|
|
2205
2220
|
let current = 0;
|
|
2206
2221
|
const props = this.config.properties[curr[0]];
|
|
2207
|
-
const
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
tokens[curr[0]].
|
|
2222
|
+
const properties = this.declarations.get(curr[0]);
|
|
2223
|
+
for (const declaration of [(properties instanceof PropertySet ? [...properties][0] : properties)]) {
|
|
2224
|
+
// @ts-ignore
|
|
2225
|
+
for (const val of declaration.val) {
|
|
2226
|
+
if (separator != null && separator.typ == val.typ && eq(separator, val)) {
|
|
2227
|
+
current++;
|
|
2228
|
+
if (tokens[curr[0]].length == current) {
|
|
2229
|
+
tokens[curr[0]].push([]);
|
|
2230
|
+
}
|
|
2231
|
+
continue;
|
|
2214
2232
|
}
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2233
|
+
if (val.typ == 'Whitespace' || val.typ == 'Comment') {
|
|
2234
|
+
continue;
|
|
2235
|
+
}
|
|
2236
|
+
if (props.multiple && props.separator != null && props.separator.typ == val.typ && eq(props.separator, val)) {
|
|
2237
|
+
continue;
|
|
2238
|
+
}
|
|
2239
|
+
match = matchType(val, curr[1]);
|
|
2240
|
+
if (isShorthand) {
|
|
2241
|
+
isShorthand = match;
|
|
2242
|
+
}
|
|
2243
|
+
if (('propertyName' in val && val.propertyName == property) || match) {
|
|
2244
|
+
if (!(curr[0] in tokens)) {
|
|
2245
|
+
tokens[curr[0]] = [[]];
|
|
2246
|
+
}
|
|
2247
|
+
// is default value
|
|
2248
|
+
tokens[curr[0]][current].push(val);
|
|
2249
|
+
// continue;
|
|
2250
|
+
}
|
|
2251
|
+
else {
|
|
2252
|
+
acc.push(curr[0]);
|
|
2253
|
+
break;
|
|
2226
2254
|
}
|
|
2227
|
-
// is default value
|
|
2228
|
-
tokens[curr[0]][current].push(val);
|
|
2229
|
-
// continue;
|
|
2230
|
-
}
|
|
2231
|
-
else {
|
|
2232
|
-
acc.push(curr[0]);
|
|
2233
|
-
break;
|
|
2234
2255
|
}
|
|
2235
2256
|
}
|
|
2236
2257
|
if (count == 0) {
|
|
@@ -2239,7 +2260,10 @@
|
|
|
2239
2260
|
return acc;
|
|
2240
2261
|
}, []);
|
|
2241
2262
|
count++;
|
|
2242
|
-
if (!Object.
|
|
2263
|
+
if (!isShorthand || Object.entries(this.config.properties).some(entry => {
|
|
2264
|
+
// missing required property
|
|
2265
|
+
return entry[1].required && !(entry[0] in tokens);
|
|
2266
|
+
}) || !Object.values(tokens).every(v => v.length == count)) {
|
|
2243
2267
|
// @ts-ignore
|
|
2244
2268
|
iterable = this.declarations.values();
|
|
2245
2269
|
}
|
|
@@ -4106,13 +4130,13 @@
|
|
|
4106
4130
|
};
|
|
4107
4131
|
}
|
|
4108
4132
|
function parseString(src, options = { location: false }) {
|
|
4109
|
-
return [...tokenize(src)].map(t => {
|
|
4133
|
+
return parseTokens([...tokenize(src)].map(t => {
|
|
4110
4134
|
const token = getTokenType(t.token, t.hint);
|
|
4111
4135
|
if (options.location) {
|
|
4112
4136
|
Object.assign(token, { loc: t.position });
|
|
4113
4137
|
}
|
|
4114
4138
|
return token;
|
|
4115
|
-
});
|
|
4139
|
+
}));
|
|
4116
4140
|
}
|
|
4117
4141
|
function getTokenType(val, hint) {
|
|
4118
4142
|
if (val === '' && hint == null) {
|
|
@@ -4623,6 +4647,7 @@
|
|
|
4623
4647
|
exports.isTime = isTime;
|
|
4624
4648
|
exports.isWhiteSpace = isWhiteSpace;
|
|
4625
4649
|
exports.load = load;
|
|
4650
|
+
exports.matchType = matchType;
|
|
4626
4651
|
exports.matchUrl = matchUrl;
|
|
4627
4652
|
exports.minify = minify;
|
|
4628
4653
|
exports.minifyRule = minifyRule;
|
package/dist/index.cjs
CHANGED
|
@@ -1067,6 +1067,21 @@ var config$1 = {
|
|
|
1067
1067
|
|
|
1068
1068
|
const getConfig = () => config$1;
|
|
1069
1069
|
|
|
1070
|
+
const funcList = ['clamp', 'calc'];
|
|
1071
|
+
function matchType(val, properties) {
|
|
1072
|
+
if (val.typ == 'Iden' && properties.keywords.includes(val.val) ||
|
|
1073
|
+
(properties.types.includes(val.typ))) {
|
|
1074
|
+
return true;
|
|
1075
|
+
}
|
|
1076
|
+
if (val.typ == 'Number' && val.val == '0') {
|
|
1077
|
+
return properties.types.some(type => type == 'Length' || type == 'Angle');
|
|
1078
|
+
}
|
|
1079
|
+
if (val.typ == 'Func' && funcList.includes(val.val)) {
|
|
1080
|
+
return val.chi.every((t => ['Literal', 'Comma', 'Whitespace', 'Start-parens', 'End-parens'].includes(t.typ) || matchType(t, properties)));
|
|
1081
|
+
}
|
|
1082
|
+
return false;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1070
1085
|
// name to color
|
|
1071
1086
|
const COLORS_NAMES = Object.seal({
|
|
1072
1087
|
'aliceblue': '#f0f8ff',
|
|
@@ -1595,6 +1610,8 @@ function doRender(data, options, reducer, level = 0, indents = []) {
|
|
|
1595
1610
|
const indent = indents[level];
|
|
1596
1611
|
const indentSub = indents[level + 1];
|
|
1597
1612
|
switch (data.typ) {
|
|
1613
|
+
case 'Declaration':
|
|
1614
|
+
return `${data.nam}:${options.indent}${data.val.reduce((acc, curr) => acc + renderToken(curr), '')}`;
|
|
1598
1615
|
case 'Comment':
|
|
1599
1616
|
return options.removeComments ? '' : data.val;
|
|
1600
1617
|
case 'StyleSheet':
|
|
@@ -1620,6 +1637,10 @@ function doRender(data, options, reducer, level = 0, indents = []) {
|
|
|
1620
1637
|
str = options.removeComments ? '' : node.val;
|
|
1621
1638
|
}
|
|
1622
1639
|
else if (node.typ == 'Declaration') {
|
|
1640
|
+
if (node.val.length == 0) {
|
|
1641
|
+
console.error(`invalid declaration`, node);
|
|
1642
|
+
return '';
|
|
1643
|
+
}
|
|
1623
1644
|
str = `${node.nam}:${options.indent}${node.val.reduce(reducer, '').trimEnd()};`;
|
|
1624
1645
|
}
|
|
1625
1646
|
else if (node.typ == 'AtRule' && !('chi' in node)) {
|
|
@@ -1998,17 +2019,6 @@ class PropertySet {
|
|
|
1998
2019
|
}
|
|
1999
2020
|
}
|
|
2000
2021
|
|
|
2001
|
-
function matchType(val, properties) {
|
|
2002
|
-
if (val.typ == 'Iden' && properties.keywords.includes(val.val) ||
|
|
2003
|
-
(properties.types.includes(val.typ))) {
|
|
2004
|
-
return true;
|
|
2005
|
-
}
|
|
2006
|
-
if (val.typ == 'Number' && val.val == '0') {
|
|
2007
|
-
return properties.types.some(type => type == 'Length' || type == 'Angle');
|
|
2008
|
-
}
|
|
2009
|
-
return false;
|
|
2010
|
-
}
|
|
2011
|
-
|
|
2012
2022
|
const propertiesConfig = getConfig();
|
|
2013
2023
|
class PropertyMap {
|
|
2014
2024
|
config;
|
|
@@ -2023,6 +2033,9 @@ class PropertyMap {
|
|
|
2023
2033
|
this.pattern = config.pattern.split(/\s/);
|
|
2024
2034
|
}
|
|
2025
2035
|
add(declaration) {
|
|
2036
|
+
for (const val of declaration.val) {
|
|
2037
|
+
Object.defineProperty(val, 'propertyName', { enumerable: false, writable: true, value: declaration.nam });
|
|
2038
|
+
}
|
|
2026
2039
|
if (declaration.nam == this.config.shorthand) {
|
|
2027
2040
|
this.declarations = new Map;
|
|
2028
2041
|
this.declarations.set(declaration.nam, declaration);
|
|
@@ -2056,7 +2069,7 @@ class PropertyMap {
|
|
|
2056
2069
|
i--;
|
|
2057
2070
|
continue;
|
|
2058
2071
|
}
|
|
2059
|
-
if (matchType(acc[i], props)) {
|
|
2072
|
+
if (('propertyName' in acc[i] && acc[i].propertyName == property) || matchType(acc[i], props)) {
|
|
2060
2073
|
if ('prefix' in props && props.previous != null && !(props.previous in tokens)) {
|
|
2061
2074
|
return acc;
|
|
2062
2075
|
}
|
|
@@ -2190,10 +2203,12 @@ class PropertyMap {
|
|
|
2190
2203
|
}
|
|
2191
2204
|
else {
|
|
2192
2205
|
let count = 0;
|
|
2206
|
+
let match;
|
|
2193
2207
|
const separator = this.config.separator;
|
|
2194
2208
|
const tokens = {};
|
|
2195
2209
|
// @ts-ignore
|
|
2196
|
-
/* const valid: string[] =*/
|
|
2210
|
+
/* const valid: string[] =*/
|
|
2211
|
+
Object.entries(this.config.properties).reduce((acc, curr) => {
|
|
2197
2212
|
if (!this.declarations.has(curr[0])) {
|
|
2198
2213
|
if (curr[1].required) {
|
|
2199
2214
|
acc.push(curr[0]);
|
|
@@ -2202,33 +2217,39 @@ class PropertyMap {
|
|
|
2202
2217
|
}
|
|
2203
2218
|
let current = 0;
|
|
2204
2219
|
const props = this.config.properties[curr[0]];
|
|
2205
|
-
const
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
tokens[curr[0]].
|
|
2220
|
+
const properties = this.declarations.get(curr[0]);
|
|
2221
|
+
for (const declaration of [(properties instanceof PropertySet ? [...properties][0] : properties)]) {
|
|
2222
|
+
// @ts-ignore
|
|
2223
|
+
for (const val of declaration.val) {
|
|
2224
|
+
if (separator != null && separator.typ == val.typ && eq(separator, val)) {
|
|
2225
|
+
current++;
|
|
2226
|
+
if (tokens[curr[0]].length == current) {
|
|
2227
|
+
tokens[curr[0]].push([]);
|
|
2228
|
+
}
|
|
2229
|
+
continue;
|
|
2212
2230
|
}
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2231
|
+
if (val.typ == 'Whitespace' || val.typ == 'Comment') {
|
|
2232
|
+
continue;
|
|
2233
|
+
}
|
|
2234
|
+
if (props.multiple && props.separator != null && props.separator.typ == val.typ && eq(props.separator, val)) {
|
|
2235
|
+
continue;
|
|
2236
|
+
}
|
|
2237
|
+
match = matchType(val, curr[1]);
|
|
2238
|
+
if (isShorthand) {
|
|
2239
|
+
isShorthand = match;
|
|
2240
|
+
}
|
|
2241
|
+
if (('propertyName' in val && val.propertyName == property) || match) {
|
|
2242
|
+
if (!(curr[0] in tokens)) {
|
|
2243
|
+
tokens[curr[0]] = [[]];
|
|
2244
|
+
}
|
|
2245
|
+
// is default value
|
|
2246
|
+
tokens[curr[0]][current].push(val);
|
|
2247
|
+
// continue;
|
|
2248
|
+
}
|
|
2249
|
+
else {
|
|
2250
|
+
acc.push(curr[0]);
|
|
2251
|
+
break;
|
|
2224
2252
|
}
|
|
2225
|
-
// is default value
|
|
2226
|
-
tokens[curr[0]][current].push(val);
|
|
2227
|
-
// continue;
|
|
2228
|
-
}
|
|
2229
|
-
else {
|
|
2230
|
-
acc.push(curr[0]);
|
|
2231
|
-
break;
|
|
2232
2253
|
}
|
|
2233
2254
|
}
|
|
2234
2255
|
if (count == 0) {
|
|
@@ -2237,7 +2258,10 @@ class PropertyMap {
|
|
|
2237
2258
|
return acc;
|
|
2238
2259
|
}, []);
|
|
2239
2260
|
count++;
|
|
2240
|
-
if (!Object.
|
|
2261
|
+
if (!isShorthand || Object.entries(this.config.properties).some(entry => {
|
|
2262
|
+
// missing required property
|
|
2263
|
+
return entry[1].required && !(entry[0] in tokens);
|
|
2264
|
+
}) || !Object.values(tokens).every(v => v.length == count)) {
|
|
2241
2265
|
// @ts-ignore
|
|
2242
2266
|
iterable = this.declarations.values();
|
|
2243
2267
|
}
|
|
@@ -4104,13 +4128,13 @@ async function parse$1(iterator, opt = {}) {
|
|
|
4104
4128
|
};
|
|
4105
4129
|
}
|
|
4106
4130
|
function parseString(src, options = { location: false }) {
|
|
4107
|
-
return [...tokenize(src)].map(t => {
|
|
4131
|
+
return parseTokens([...tokenize(src)].map(t => {
|
|
4108
4132
|
const token = getTokenType(t.token, t.hint);
|
|
4109
4133
|
if (options.location) {
|
|
4110
4134
|
Object.assign(token, { loc: t.position });
|
|
4111
4135
|
}
|
|
4112
4136
|
return token;
|
|
4113
|
-
});
|
|
4137
|
+
}));
|
|
4114
4138
|
}
|
|
4115
4139
|
function getTokenType(val, hint) {
|
|
4116
4140
|
if (val === '' && hint == null) {
|
|
@@ -4607,6 +4631,7 @@ exports.isResolution = isResolution;
|
|
|
4607
4631
|
exports.isTime = isTime;
|
|
4608
4632
|
exports.isWhiteSpace = isWhiteSpace;
|
|
4609
4633
|
exports.load = load;
|
|
4634
|
+
exports.matchType = matchType;
|
|
4610
4635
|
exports.matchUrl = matchUrl;
|
|
4611
4636
|
exports.minify = minify;
|
|
4612
4637
|
exports.minifyRule = minifyRule;
|
package/dist/index.d.ts
CHANGED
|
@@ -173,6 +173,30 @@ interface AttrToken {
|
|
|
173
173
|
}
|
|
174
174
|
declare type Token = LiteralToken | IdentToken | CommaToken | ColonToken | SemiColonToken | NumberToken | AtRuleToken | PercentageToken | FunctionURLToken | FunctionToken | DimensionToken | LengthToken | AngleToken | StringToken | TimeToken | FrequencyToken | ResolutionToken | UnclosedStringToken | HashToken | BadStringToken | BlockStartToken | BlockEndToken | AttrStartToken | AttrEndToken | ParensStartToken | ParensEndToken | CDOCommentToken | BadCDOCommentToken | CommentToken | BadCommentToken | WhitespaceToken | IncludesToken | DashMatchToken | LessThanToken | GreaterThanToken | PseudoClassToken | PseudoClassFunctionToken | DelimToken | BadUrlToken | UrlToken | ImportantToken | ColorToken | AttrToken | EOFToken;
|
|
175
175
|
|
|
176
|
+
interface PropertyMapType {
|
|
177
|
+
default: string[];
|
|
178
|
+
types: string[];
|
|
179
|
+
keywords: string[];
|
|
180
|
+
required?: boolean;
|
|
181
|
+
multiple?: boolean;
|
|
182
|
+
prefix?: {
|
|
183
|
+
typ: 'Literal';
|
|
184
|
+
val: string;
|
|
185
|
+
};
|
|
186
|
+
previous?: string;
|
|
187
|
+
separator?: {
|
|
188
|
+
typ: 'Comma';
|
|
189
|
+
};
|
|
190
|
+
constraints?: {
|
|
191
|
+
[key: string]: {
|
|
192
|
+
[key: string]: any;
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
mapping?: {
|
|
196
|
+
[key: string]: any;
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
176
200
|
interface PropertiesConfig {
|
|
177
201
|
properties: PropertiesConfigProperties;
|
|
178
202
|
map: Map;
|
|
@@ -629,6 +653,8 @@ declare function isWhiteSpace(codepoint: number): boolean;
|
|
|
629
653
|
|
|
630
654
|
declare const getConfig: () => PropertiesConfig;
|
|
631
655
|
|
|
656
|
+
declare function matchType(val: Token, properties: PropertyMapType): boolean;
|
|
657
|
+
|
|
632
658
|
declare function render(data: AstNode, opt?: RenderOptions): RenderResult;
|
|
633
659
|
declare function renderToken(token: Token, options?: RenderOptions): string;
|
|
634
660
|
|
|
@@ -661,4 +687,4 @@ declare function resolve(url: string, currentDirectory: string, cwd?: string): {
|
|
|
661
687
|
declare function parse(iterator: string, opt?: ParserOptions): Promise<ParseResult>;
|
|
662
688
|
declare function transform(css: string, options?: TransformOptions): Promise<TransformResult>;
|
|
663
689
|
|
|
664
|
-
export { combinators, dirname, getConfig, hasDeclaration, isAngle, isAtKeyword, isDigit, isDimension, isFrequency, isFunction, isHash, isHexColor, isHexDigit, isIdent, isIdentCodepoint, isIdentStart, isLength, isNewLine, isNumber, isPercentage, isPseudo, isResolution, isTime, isWhiteSpace, load, matchUrl, minify, minifyRule, parse, parseDimension, parseString, reduceSelector, render, renderToken, resolve, tokenize, transform, urlTokenMatcher, walk };
|
|
690
|
+
export { combinators, dirname, getConfig, hasDeclaration, isAngle, isAtKeyword, isDigit, isDimension, isFrequency, isFunction, isHash, isHexColor, isHexDigit, isIdent, isIdentCodepoint, isIdentStart, isLength, isNewLine, isNumber, isPercentage, isPseudo, isResolution, isTime, isWhiteSpace, load, matchType, matchUrl, minify, minifyRule, parse, parseDimension, parseString, reduceSelector, render, renderToken, resolve, tokenize, transform, urlTokenMatcher, walk };
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export { parseString, urlTokenMatcher } from './lib/parser/parse.js';
|
|
|
3
3
|
export { tokenize } from './lib/parser/tokenize.js';
|
|
4
4
|
export { isAngle, isAtKeyword, isDigit, isDimension, isFrequency, isFunction, isHash, isHexColor, isHexDigit, isIdent, isIdentCodepoint, isIdentStart, isLength, isNewLine, isNumber, isPercentage, isPseudo, isResolution, isTime, isWhiteSpace, parseDimension } from './lib/parser/utils/syntax.js';
|
|
5
5
|
export { getConfig } from './lib/parser/utils/config.js';
|
|
6
|
+
export { matchType } from './lib/parser/utils/type.js';
|
|
6
7
|
export { render, renderToken } from './lib/renderer/render.js';
|
|
7
8
|
export { combinators, hasDeclaration, minify, minifyRule, reduceSelector } from './lib/ast/minify.js';
|
|
8
9
|
export { walk } from './lib/ast/walk.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { eq } from '../utils/eq.js';
|
|
2
2
|
import { getConfig } from '../utils/config.js';
|
|
3
|
-
import { renderToken } from '../../renderer/render.js';
|
|
4
3
|
import { matchType } from '../utils/type.js';
|
|
4
|
+
import { renderToken } from '../../renderer/render.js';
|
|
5
5
|
import { parseString } from '../parse.js';
|
|
6
6
|
import { PropertySet } from './set.js';
|
|
7
7
|
|
|
@@ -19,6 +19,9 @@ class PropertyMap {
|
|
|
19
19
|
this.pattern = config.pattern.split(/\s/);
|
|
20
20
|
}
|
|
21
21
|
add(declaration) {
|
|
22
|
+
for (const val of declaration.val) {
|
|
23
|
+
Object.defineProperty(val, 'propertyName', { enumerable: false, writable: true, value: declaration.nam });
|
|
24
|
+
}
|
|
22
25
|
if (declaration.nam == this.config.shorthand) {
|
|
23
26
|
this.declarations = new Map;
|
|
24
27
|
this.declarations.set(declaration.nam, declaration);
|
|
@@ -52,7 +55,7 @@ class PropertyMap {
|
|
|
52
55
|
i--;
|
|
53
56
|
continue;
|
|
54
57
|
}
|
|
55
|
-
if (matchType(acc[i], props)) {
|
|
58
|
+
if (('propertyName' in acc[i] && acc[i].propertyName == property) || matchType(acc[i], props)) {
|
|
56
59
|
if ('prefix' in props && props.previous != null && !(props.previous in tokens)) {
|
|
57
60
|
return acc;
|
|
58
61
|
}
|
|
@@ -186,10 +189,12 @@ class PropertyMap {
|
|
|
186
189
|
}
|
|
187
190
|
else {
|
|
188
191
|
let count = 0;
|
|
192
|
+
let match;
|
|
189
193
|
const separator = this.config.separator;
|
|
190
194
|
const tokens = {};
|
|
191
195
|
// @ts-ignore
|
|
192
|
-
/* const valid: string[] =*/
|
|
196
|
+
/* const valid: string[] =*/
|
|
197
|
+
Object.entries(this.config.properties).reduce((acc, curr) => {
|
|
193
198
|
if (!this.declarations.has(curr[0])) {
|
|
194
199
|
if (curr[1].required) {
|
|
195
200
|
acc.push(curr[0]);
|
|
@@ -198,33 +203,39 @@ class PropertyMap {
|
|
|
198
203
|
}
|
|
199
204
|
let current = 0;
|
|
200
205
|
const props = this.config.properties[curr[0]];
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
tokens[curr[0]].
|
|
206
|
+
const properties = this.declarations.get(curr[0]);
|
|
207
|
+
for (const declaration of [(properties instanceof PropertySet ? [...properties][0] : properties)]) {
|
|
208
|
+
// @ts-ignore
|
|
209
|
+
for (const val of declaration.val) {
|
|
210
|
+
if (separator != null && separator.typ == val.typ && eq(separator, val)) {
|
|
211
|
+
current++;
|
|
212
|
+
if (tokens[curr[0]].length == current) {
|
|
213
|
+
tokens[curr[0]].push([]);
|
|
214
|
+
}
|
|
215
|
+
continue;
|
|
208
216
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
217
|
+
if (val.typ == 'Whitespace' || val.typ == 'Comment') {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (props.multiple && props.separator != null && props.separator.typ == val.typ && eq(props.separator, val)) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
match = matchType(val, curr[1]);
|
|
224
|
+
if (isShorthand) {
|
|
225
|
+
isShorthand = match;
|
|
226
|
+
}
|
|
227
|
+
if (('propertyName' in val && val.propertyName == property) || match) {
|
|
228
|
+
if (!(curr[0] in tokens)) {
|
|
229
|
+
tokens[curr[0]] = [[]];
|
|
230
|
+
}
|
|
231
|
+
// is default value
|
|
232
|
+
tokens[curr[0]][current].push(val);
|
|
233
|
+
// continue;
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
acc.push(curr[0]);
|
|
237
|
+
break;
|
|
220
238
|
}
|
|
221
|
-
// is default value
|
|
222
|
-
tokens[curr[0]][current].push(val);
|
|
223
|
-
// continue;
|
|
224
|
-
}
|
|
225
|
-
else {
|
|
226
|
-
acc.push(curr[0]);
|
|
227
|
-
break;
|
|
228
239
|
}
|
|
229
240
|
}
|
|
230
241
|
if (count == 0) {
|
|
@@ -233,7 +244,10 @@ class PropertyMap {
|
|
|
233
244
|
return acc;
|
|
234
245
|
}, []);
|
|
235
246
|
count++;
|
|
236
|
-
if (!Object.
|
|
247
|
+
if (!isShorthand || Object.entries(this.config.properties).some(entry => {
|
|
248
|
+
// missing required property
|
|
249
|
+
return entry[1].required && !(entry[0] in tokens);
|
|
250
|
+
}) || !Object.values(tokens).every(v => v.length == count)) {
|
|
237
251
|
// @ts-ignore
|
|
238
252
|
iterable = this.declarations.values();
|
|
239
253
|
}
|
package/dist/lib/parser/parse.js
CHANGED
|
@@ -389,13 +389,13 @@ async function parse(iterator, opt = {}) {
|
|
|
389
389
|
};
|
|
390
390
|
}
|
|
391
391
|
function parseString(src, options = { location: false }) {
|
|
392
|
-
return [...tokenize(src)].map(t => {
|
|
392
|
+
return parseTokens([...tokenize(src)].map(t => {
|
|
393
393
|
const token = getTokenType(t.token, t.hint);
|
|
394
394
|
if (options.location) {
|
|
395
395
|
Object.assign(token, { loc: t.position });
|
|
396
396
|
}
|
|
397
397
|
return token;
|
|
398
|
-
});
|
|
398
|
+
}));
|
|
399
399
|
}
|
|
400
400
|
function getTokenType(val, hint) {
|
|
401
401
|
if (val === '' && hint == null) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const funcList = ['clamp', 'calc'];
|
|
1
2
|
function matchType(val, properties) {
|
|
2
3
|
if (val.typ == 'Iden' && properties.keywords.includes(val.val) ||
|
|
3
4
|
(properties.types.includes(val.typ))) {
|
|
@@ -6,6 +7,9 @@ function matchType(val, properties) {
|
|
|
6
7
|
if (val.typ == 'Number' && val.val == '0') {
|
|
7
8
|
return properties.types.some(type => type == 'Length' || type == 'Angle');
|
|
8
9
|
}
|
|
10
|
+
if (val.typ == 'Func' && funcList.includes(val.val)) {
|
|
11
|
+
return val.chi.every((t => ['Literal', 'Comma', 'Whitespace', 'Start-parens', 'End-parens'].includes(t.typ) || matchType(t, properties)));
|
|
12
|
+
}
|
|
9
13
|
return false;
|
|
10
14
|
}
|
|
11
15
|
|
|
@@ -35,6 +35,8 @@ function doRender(data, options, reducer, level = 0, indents = []) {
|
|
|
35
35
|
const indent = indents[level];
|
|
36
36
|
const indentSub = indents[level + 1];
|
|
37
37
|
switch (data.typ) {
|
|
38
|
+
case 'Declaration':
|
|
39
|
+
return `${data.nam}:${options.indent}${data.val.reduce((acc, curr) => acc + renderToken(curr), '')}`;
|
|
38
40
|
case 'Comment':
|
|
39
41
|
return options.removeComments ? '' : data.val;
|
|
40
42
|
case 'StyleSheet':
|
|
@@ -60,6 +62,10 @@ function doRender(data, options, reducer, level = 0, indents = []) {
|
|
|
60
62
|
str = options.removeComments ? '' : node.val;
|
|
61
63
|
}
|
|
62
64
|
else if (node.typ == 'Declaration') {
|
|
65
|
+
if (node.val.length == 0) {
|
|
66
|
+
console.error(`invalid declaration`, node);
|
|
67
|
+
return '';
|
|
68
|
+
}
|
|
63
69
|
str = `${node.nam}:${options.indent}${node.val.reduce(reducer, '').trimEnd()};`;
|
|
64
70
|
}
|
|
65
71
|
else if (node.typ == 'AtRule' && !('chi' in node)) {
|
package/dist/web/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export { parseString, urlTokenMatcher } from '../lib/parser/parse.js';
|
|
|
3
3
|
export { tokenize } from '../lib/parser/tokenize.js';
|
|
4
4
|
export { isAngle, isAtKeyword, isDigit, isDimension, isFrequency, isFunction, isHash, isHexColor, isHexDigit, isIdent, isIdentCodepoint, isIdentStart, isLength, isNewLine, isNumber, isPercentage, isPseudo, isResolution, isTime, isWhiteSpace, parseDimension } from '../lib/parser/utils/syntax.js';
|
|
5
5
|
export { getConfig } from '../lib/parser/utils/config.js';
|
|
6
|
+
export { matchType } from '../lib/parser/utils/type.js';
|
|
6
7
|
export { render, renderToken } from '../lib/renderer/render.js';
|
|
7
8
|
import { transform as transform$1 } from '../lib/transform.js';
|
|
8
9
|
export { combinators, hasDeclaration, minify, minifyRule, reduceSelector } from '../lib/ast/minify.js';
|
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-
|
|
4
|
+
"version": "0.0.1-rc3",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dist/index.js",
|
|
7
7
|
"./umd": "./dist/index-umd-web.js",
|
|
@@ -41,20 +41,19 @@
|
|
|
41
41
|
"homepage": "https://github.com/tbela99/css-parser#readme",
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@esm-bundle/chai": "^4.3.4-fix.0",
|
|
44
|
-
"@rollup/plugin-commonjs": "^25.0.
|
|
44
|
+
"@rollup/plugin-commonjs": "^25.0.4",
|
|
45
45
|
"@rollup/plugin-json": "^6.0.0",
|
|
46
|
-
"@rollup/plugin-node-resolve": "^15.0
|
|
46
|
+
"@rollup/plugin-node-resolve": "^15.1.0",
|
|
47
47
|
"@rollup/plugin-terser": "^0.4.3",
|
|
48
|
-
"@rollup/plugin-typescript": "^11.
|
|
48
|
+
"@rollup/plugin-typescript": "^11.1.2",
|
|
49
49
|
"@types/chai": "^4.3.5",
|
|
50
50
|
"@types/mocha": "^10.0.1",
|
|
51
|
-
"@types/node": "^
|
|
52
|
-
"@web/test-runner": "^0.
|
|
53
|
-
"@webref/css": "^6.5.9",
|
|
51
|
+
"@types/node": "^20.4.10",
|
|
52
|
+
"@web/test-runner": "^0.17.0",
|
|
54
53
|
"c8": "^8.0.1",
|
|
55
54
|
"mocha": "^10.2.0",
|
|
56
|
-
"rollup": "^3.
|
|
57
|
-
"rollup-plugin-dts": "^5.3.
|
|
58
|
-
"tslib": "^2.
|
|
55
|
+
"rollup": "^3.28.0",
|
|
56
|
+
"rollup-plugin-dts": "^5.3.1",
|
|
57
|
+
"tslib": "^2.6.1"
|
|
59
58
|
}
|
|
60
59
|
}
|
package/.gitattributes
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/test/ export-ignore
|
|
2
|
-
/docs/ export-ignore
|
|
3
|
-
/benchmark
|
|
4
|
-
/tools/ export-ignore
|
|
5
|
-
/package-lock.json export-ignore
|
|
6
|
-
/.gitignore export-ignore
|
|
7
|
-
/.gitattributes export-ignore
|
|
8
|
-
/coverage/ export-ignore
|
|
9
|
-
/rollup.config.mjs export-ignore
|
|
10
|
-
/tsconfig.json export-ignore
|
|
11
|
-
# exclude all files in test/ from stats
|
|
12
|
-
/test/** linguist-vendored
|
|
13
|
-
/docs/** linguist-vendored
|
|
14
|
-
/tools/** linguist-vendored
|
|
15
|
-
/dist/** linguist-vendored
|
|
16
|
-
/coverage/** linguist-vendored
|
|
17
|
-
#
|
|
18
|
-
# do not replace lf by crlf
|
|
19
|
-
*.css text
|
|
20
|
-
*.json text
|
|
21
|
-
text eol=lf
|
|
22
|
-
|