@tbela99/css-parser 0.0.1-rc4 → 0.0.1-rc6

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.
@@ -1,3 +1,6 @@
1
+ import { colorsFunc } from '../../renderer/render.js';
2
+ import { COLORS_NAMES } from '../../renderer/utils/color.js';
3
+
1
4
  // https://www.w3.org/TR/CSS21/syndata.html#syntax
2
5
  // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-ident-token
3
6
  // '\\'
@@ -23,6 +26,25 @@ function isTime(dimension) {
23
26
  function isFrequency(dimension) {
24
27
  return 'unit' in dimension && ['hz', 'khz'].includes(dimension.unit.toLowerCase());
25
28
  }
29
+ function isColor(token) {
30
+ if (token.typ == 'Color') {
31
+ return true;
32
+ }
33
+ if (token.typ == 'Iden') {
34
+ // named color
35
+ return token.val.toLowerCase() in COLORS_NAMES;
36
+ }
37
+ if (token.typ == 'Func' && token.chi.length > 0 && colorsFunc.includes(token.val)) {
38
+ // @ts-ignore
39
+ for (const v of token.chi) {
40
+ if (!['Number', 'Perc', 'Comma', 'Whitespace'].includes(v.typ)) {
41
+ return false;
42
+ }
43
+ }
44
+ return true;
45
+ }
46
+ return false;
47
+ }
26
48
  function isLetter(codepoint) {
27
49
  // lowercase
28
50
  return (codepoint >= 0x61 && codepoint <= 0x7a) ||
@@ -74,20 +96,22 @@ function isIdent(name) {
74
96
  }
75
97
  return true;
76
98
  }
99
+ function isNonPrintable(codepoint) {
100
+ // null -> backspace
101
+ return (codepoint >= 0 && codepoint <= 0x8) ||
102
+ // tab
103
+ codepoint == 0xb ||
104
+ // delete
105
+ codepoint == 0x7f ||
106
+ (codepoint >= 0xe && codepoint <= 0x1f);
107
+ }
77
108
  function isPseudo(name) {
78
- if (name.charAt(0) != ':') {
79
- return false;
80
- }
81
- if (name.endsWith('(')) {
82
- return isIdent(name.charAt(1) == ':' ? name.slice(2, -1) : name.slice(1, -1));
83
- }
84
- return isIdent(name.charAt(1) == ':' ? name.slice(2) : name.slice(1));
109
+ return name.charAt(0) == ':' &&
110
+ ((name.endsWith('(') && isIdent(name.charAt(1) == ':' ? name.slice(2, -1) : name.slice(1, -1))) ||
111
+ isIdent(name.charAt(1) == ':' ? name.slice(2) : name.slice(1)));
85
112
  }
86
113
  function isHash(name) {
87
- if (name.charAt(0) != '#') {
88
- return false;
89
- }
90
- return isIdent(name.charAt(1));
114
+ return name.charAt(0) == '#' && isIdent(name.charAt(1));
91
115
  }
92
116
  function isNumber(name) {
93
117
  if (name.length == 0) {
@@ -224,22 +248,6 @@ function isHexColor(name) {
224
248
  }
225
249
  return true;
226
250
  }
227
- function isHexDigit(name) {
228
- if (name.length || name.length > 6) {
229
- return false;
230
- }
231
- for (let chr of name) {
232
- let codepoint = chr.charCodeAt(0);
233
- if (!isDigit(codepoint) &&
234
- // A F
235
- !(codepoint >= 0x41 && codepoint <= 0x46) &&
236
- // a f
237
- !(codepoint >= 0x61 && codepoint <= 0x66)) {
238
- return false;
239
- }
240
- }
241
- return true;
242
- }
243
251
  function isFunction(name) {
244
252
  return name.endsWith('(') && isIdent(name.slice(0, -1));
245
253
  }
@@ -256,4 +264,4 @@ function isWhiteSpace(codepoint) {
256
264
  codepoint == 0xa || codepoint == 0xc || codepoint == 0xd;
257
265
  }
258
266
 
259
- export { isAngle, isAtKeyword, isDigit, isDimension, isFrequency, isFunction, isHash, isHexColor, isHexDigit, isIdent, isIdentCodepoint, isIdentStart, isLength, isNewLine, isNumber, isPercentage, isPseudo, isResolution, isTime, isWhiteSpace, parseDimension };
267
+ export { isAngle, isAtKeyword, isColor, isDigit, isDimension, isFrequency, isFunction, isHash, isHexColor, isIdent, isIdentCodepoint, isIdentStart, isLength, isNewLine, isNonPrintable, isNumber, isPercentage, isPseudo, isResolution, isTime, isWhiteSpace, parseDimension };
@@ -13,4 +13,4 @@ function matchType(val, properties) {
13
13
  return false;
14
14
  }
15
15
 
16
- export { matchType };
16
+ export { funcList, matchType };
@@ -1,5 +1,7 @@
1
1
  import { getAngle, COLORS_NAMES, rgb2Hex, hsl2Hex, hwb2hex, cmyk2hex, NAMES_COLORS } from './utils/color.js';
2
+ import { expand } from '../ast/expand.js';
2
3
 
4
+ const colorsFunc = ['rgb', 'rgba', 'hsl', 'hsla', 'hwb', 'device-cmyk'];
3
5
  function reduceNumber(val) {
4
6
  val = (+val).toString();
5
7
  if (val === '0') {
@@ -19,6 +21,7 @@ function reduceNumber(val) {
19
21
  }
20
22
  function render(data, opt = {}) {
21
23
  const startTime = performance.now();
24
+ const errors = [];
22
25
  const options = Object.assign(opt.minify ?? true ? {
23
26
  indent: '',
24
27
  newLine: '',
@@ -28,23 +31,23 @@ function render(data, opt = {}) {
28
31
  newLine: '\n',
29
32
  compress: false,
30
33
  removeComments: false,
31
- }, { colorConvert: true, preserveLicense: false }, opt);
34
+ }, { colorConvert: true, expandNestingRules: false, preserveLicense: false }, opt);
32
35
  return {
33
- code: doRender(data, options, function reducer(acc, curr) {
36
+ code: doRender(options.expandNestingRules ? expand(data) : data, options, errors, function reducer(acc, curr) {
34
37
  if (curr.typ == 'Comment' && options.removeComments) {
35
38
  if (!options.preserveLicense || !curr.val.startsWith('/*!')) {
36
39
  return acc;
37
40
  }
38
41
  return acc + curr.val;
39
42
  }
40
- return acc + renderToken(curr, options, reducer);
41
- }, 0), stats: {
43
+ return acc + renderToken(curr, options, reducer, errors);
44
+ }, 0), errors, stats: {
42
45
  total: `${(performance.now() - startTime).toFixed(2)}ms`
43
46
  }
44
47
  };
45
48
  }
46
49
  // @ts-ignore
47
- function doRender(data, options, reducer, level = 0, indents = []) {
50
+ function doRender(data, options, errors, reducer, level = 0, indents = []) {
48
51
  if (indents.length < level + 1) {
49
52
  indents.push(options.indent.repeat(level));
50
53
  }
@@ -57,10 +60,11 @@ function doRender(data, options, reducer, level = 0, indents = []) {
57
60
  case 'Declaration':
58
61
  return `${data.nam}:${options.indent}${data.val.reduce(reducer, '')}`;
59
62
  case 'Comment':
63
+ case 'CDOCOMM':
60
64
  return !options.removeComments || (options.preserveLicense && data.val.startsWith('/*!')) ? data.val : '';
61
65
  case 'StyleSheet':
62
66
  return data.chi.reduce((css, node) => {
63
- const str = doRender(node, options, reducer, level, indents);
67
+ const str = doRender(node, options, errors, reducer, level, indents);
64
68
  if (str === '') {
65
69
  return css;
66
70
  }
@@ -82,7 +86,8 @@ function doRender(data, options, reducer, level = 0, indents = []) {
82
86
  }
83
87
  else if (node.typ == 'Declaration') {
84
88
  if (node.val.length == 0) {
85
- console.error(`invalid declaration`, node);
89
+ // @ts-ignore
90
+ errors.push({ action: 'ignore', message: `render: invalid declaration ${JSON.stringify(node)}`, location: node.loc });
86
91
  return '';
87
92
  }
88
93
  str = `${node.nam}:${options.indent}${node.val.reduce(reducer, '').trimEnd()};`;
@@ -91,7 +96,7 @@ function doRender(data, options, reducer, level = 0, indents = []) {
91
96
  str = `${data.val === '' ? '' : options.indent || ' '}${data.val};`;
92
97
  }
93
98
  else {
94
- str = doRender(node, options, reducer, level + 1, indents);
99
+ str = doRender(node, options, errors, reducer, level + 1, indents);
95
100
  }
96
101
  if (css === '') {
97
102
  return str;
@@ -111,7 +116,7 @@ function doRender(data, options, reducer, level = 0, indents = []) {
111
116
  }
112
117
  return '';
113
118
  }
114
- function renderToken(token, options = {}, reducer) {
119
+ function renderToken(token, options = {}, reducer, errors) {
115
120
  if (reducer == null) {
116
121
  reducer = function (acc, curr) {
117
122
  if (curr.typ == 'Comment' && options.removeComments) {
@@ -120,7 +125,7 @@ function renderToken(token, options = {}, reducer) {
120
125
  }
121
126
  return acc + curr.val;
122
127
  }
123
- return acc + renderToken(curr, options, reducer);
128
+ return acc + renderToken(curr, options, reducer, errors);
124
129
  };
125
130
  }
126
131
  switch (token.typ) {
@@ -257,14 +262,14 @@ function renderToken(token, options = {}, reducer) {
257
262
  }
258
263
  }
259
264
  if (val === '0') {
260
- if (unit == 'Time') {
265
+ if (token.typ == 'Time') {
261
266
  return '0s';
262
267
  }
263
- if (unit == 'Frequency') {
268
+ if (token.typ == 'Frequency') {
264
269
  return '0Hz';
265
270
  }
266
271
  // @ts-ignore
267
- if (unit == 'Resolution') {
272
+ if (token.typ == 'Resolution') {
268
273
  return '0x';
269
274
  }
270
275
  return '0';
@@ -273,18 +278,7 @@ function renderToken(token, options = {}, reducer) {
273
278
  case 'Perc':
274
279
  return token.val + '%';
275
280
  case 'Number':
276
- const num = (+token.val).toString();
277
- if (token.val.length < num.length) {
278
- return token.val;
279
- }
280
- if (num.charAt(0) === '0' && num.length > 1) {
281
- return num.slice(1);
282
- }
283
- const slice = num.slice(0, 2);
284
- if (slice == '-0') {
285
- return '-' + num.slice(2);
286
- }
287
- return num;
281
+ return reduceNumber(token.val);
288
282
  case 'Comment':
289
283
  if (options.removeComments && (!options.preserveLicense || !token.val.startsWith('/*!'))) {
290
284
  return '';
@@ -299,9 +293,8 @@ function renderToken(token, options = {}, reducer) {
299
293
  case 'Delim':
300
294
  return /* options.minify && 'Pseudo-class' == token.typ && '::' == token.val.slice(0, 2) ? token.val.slice(1) : */ token.val;
301
295
  }
302
- console.error(`unexpected token ${JSON.stringify(token, null, 1)}`);
303
- // throw new Error(`unexpected token ${JSON.stringify(token, null, 1)}`);
296
+ errors?.push({ action: 'ignore', message: `render: unexpected token ${JSON.stringify(token, null, 1)}` });
304
297
  return '';
305
298
  }
306
299
 
307
- export { render, renderToken };
300
+ export { colorsFunc, render, renderToken };
@@ -1,5 +1,6 @@
1
1
  import { parse } from './parser/parse.js';
2
2
  import { render } from './renderer/render.js';
3
+ import './renderer/utils/color.js';
3
4
 
4
5
  async function transform(css, options = {}) {
5
6
  options = { minify: true, removeEmpty: true, ...options };
@@ -7,7 +8,10 @@ async function transform(css, options = {}) {
7
8
  return parse(css, options).then((parseResult) => {
8
9
  const rendered = render(parseResult.ast, options);
9
10
  return {
10
- ...parseResult, ...rendered, stats: {
11
+ ...parseResult,
12
+ ...rendered,
13
+ errors: parseResult.errors.concat(rendered.errors),
14
+ stats: {
11
15
  bytesOut: rendered.code.length,
12
16
  ...parseResult.stats,
13
17
  render: rendered.stats.total,
@@ -1,21 +1,22 @@
1
+ export { combinators, hasDeclaration, minify, minifyRule, reduceSelector, splitRule } from '../lib/ast/minify.js';
2
+ export { walk, walkValues } from '../lib/ast/walk.js';
3
+ export { expand } from '../lib/ast/expand.js';
4
+ export { colorsFunc, render, renderToken } from '../lib/renderer/render.js';
1
5
  import { parse as parse$1 } from '../lib/parser/parse.js';
2
6
  export { parseString, urlTokenMatcher } from '../lib/parser/parse.js';
3
7
  export { tokenize } from '../lib/parser/tokenize.js';
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';
8
+ export { isAngle, isAtKeyword, isColor, isDigit, isDimension, isFrequency, isFunction, isHash, isHexColor, isIdent, isIdentCodepoint, isIdentStart, isLength, isNewLine, isNonPrintable, isNumber, isPercentage, isPseudo, isResolution, isTime, isWhiteSpace, parseDimension } from '../lib/parser/utils/syntax.js';
5
9
  export { getConfig } from '../lib/parser/utils/config.js';
6
- export { matchType } from '../lib/parser/utils/type.js';
7
- export { render, renderToken } from '../lib/renderer/render.js';
10
+ export { funcList, matchType } from '../lib/parser/utils/type.js';
8
11
  import { transform as transform$1 } from '../lib/transform.js';
9
- export { combinators, hasDeclaration, minify, minifyRule, reduceSelector } from '../lib/ast/minify.js';
10
- export { walk } from '../lib/ast/walk.js';
11
12
  import { load } from './load.js';
12
13
  import { resolve } from '../lib/fs/resolve.js';
13
14
  export { dirname, matchUrl } from '../lib/fs/resolve.js';
14
15
 
15
- function parse(iterator, opt = {}) {
16
+ async function parse(iterator, opt = {}) {
16
17
  return parse$1(iterator, Object.assign(opt, { load, resolve, cwd: opt.cwd ?? process.cwd() }));
17
18
  }
18
- function transform(css, options = {}) {
19
+ async function transform(css, options = {}) {
19
20
  return transform$1(css, Object.assign(options, { load, resolve, cwd: options.cwd ?? process.cwd() }));
20
21
  }
21
22
 
package/dist/web/index.js CHANGED
@@ -1,25 +1,26 @@
1
+ export { combinators, hasDeclaration, minify, minifyRule, reduceSelector, splitRule } from '../lib/ast/minify.js';
2
+ export { walk, walkValues } from '../lib/ast/walk.js';
3
+ export { expand } from '../lib/ast/expand.js';
4
+ export { colorsFunc, render, renderToken } from '../lib/renderer/render.js';
1
5
  import { parse as parse$1 } from '../lib/parser/parse.js';
2
6
  export { parseString, urlTokenMatcher } from '../lib/parser/parse.js';
3
7
  export { tokenize } from '../lib/parser/tokenize.js';
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';
8
+ export { isAngle, isAtKeyword, isColor, isDigit, isDimension, isFrequency, isFunction, isHash, isHexColor, isIdent, isIdentCodepoint, isIdentStart, isLength, isNewLine, isNonPrintable, isNumber, isPercentage, isPseudo, isResolution, isTime, isWhiteSpace, parseDimension } from '../lib/parser/utils/syntax.js';
5
9
  export { getConfig } from '../lib/parser/utils/config.js';
6
- export { matchType } from '../lib/parser/utils/type.js';
7
- export { render, renderToken } from '../lib/renderer/render.js';
10
+ export { funcList, matchType } from '../lib/parser/utils/type.js';
8
11
  import { transform as transform$1 } from '../lib/transform.js';
9
- export { combinators, hasDeclaration, minify, minifyRule, reduceSelector } from '../lib/ast/minify.js';
10
- export { walk } from '../lib/ast/walk.js';
11
12
  import { load } from './load.js';
12
13
  import { resolve, dirname } from '../lib/fs/resolve.js';
13
14
  export { matchUrl } from '../lib/fs/resolve.js';
14
15
 
15
- function parse(iterator, opt = {}) {
16
+ async function parse(iterator, opt = {}) {
16
17
  return parse$1(iterator, Object.assign(opt, {
17
18
  load,
18
19
  resolve,
19
20
  cwd: opt.cwd ?? self.location.pathname.endsWith('/') ? self.location.pathname : dirname(self.location.pathname)
20
21
  }));
21
22
  }
22
- function transform(css, options = {}) {
23
+ async function transform(css, options = {}) {
23
24
  return transform$1(css, Object.assign(options, {
24
25
  load,
25
26
  resolve,
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-rc4",
4
+ "version": "0.0.1-rc6",
5
5
  "exports": {
6
6
  ".": "./dist/node/index.js",
7
7
  "./umd": "./dist/index-umd-web.js",