@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,4 +1,4 @@
1
- import { isPseudo, isAtKeyword, isFunction, isNumber, isDimension, parseDimension, isPercentage, isIdent, isHexColor, isHash, isIdentStart } from './utils/syntax.js';
1
+ import { isPseudo, isAtKeyword, isFunction, isNumber, isDimension, parseDimension, isPercentage, isIdent, isHexColor, isHash, isIdentStart, isColor } from './utils/syntax.js';
2
2
  import { renderToken } from '../renderer/render.js';
3
3
  import { COLORS_NAMES } from '../renderer/utils/color.js';
4
4
  import { minify, combinators } from '../ast/minify.js';
@@ -52,19 +52,19 @@ async function parse(iterator, opt = {}) {
52
52
  let tokens = results.map(mapToken);
53
53
  let i;
54
54
  let loc;
55
- // if ((<Token>tokens.at(-1))?.typ == 'EOF') {
56
- //
57
- // tokens.pop();
58
- // }
59
55
  for (i = 0; i < tokens.length; i++) {
60
- if (tokens[i].typ == 'Comment') {
61
- // @ts-ignore
62
- context.chi.push(tokens[i]);
56
+ if (tokens[i].typ == 'Comment' || tokens[i].typ == 'CDOCOMM') {
63
57
  const position = map.get(tokens[i]);
58
+ if (tokens[i].typ == 'CDOCOMM' && context.typ != 'StyleSheet') {
59
+ errors.push({ action: 'drop', message: `CDOCOMM not allowed here ${JSON.stringify(tokens[i], null, 1)}`, location: { src, ...position } });
60
+ continue;
61
+ }
64
62
  loc = {
65
63
  sta: position,
66
64
  src
67
65
  };
66
+ // @ts-ignore
67
+ context.chi.push(tokens[i]);
68
68
  if (options.sourcemap) {
69
69
  tokens[i].loc = loc;
70
70
  }
@@ -95,7 +95,7 @@ async function parse(iterator, opt = {}) {
95
95
  const atRule = tokens.shift();
96
96
  const position = map.get(atRule);
97
97
  if (atRule.val == 'charset' && position.ind > 0) {
98
- errors.push({ action: 'drop', message: 'invalid @charset', location: { src, ...position } });
98
+ errors.push({ action: 'drop', message: 'parse: invalid @charset', location: { src, ...position } });
99
99
  return null;
100
100
  }
101
101
  // @ts-ignore
@@ -125,12 +125,12 @@ async function parse(iterator, opt = {}) {
125
125
  }
126
126
  // @ts-ignore
127
127
  if (tokens[0]?.typ != 'String' && tokens[0]?.typ != 'UrlFunc') {
128
- errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
128
+ errors.push({ action: 'drop', message: 'parse: invalid @import', location: { src, ...position } });
129
129
  return null;
130
130
  }
131
131
  // @ts-ignore
132
132
  if (tokens[0].typ == 'UrlFunc' && tokens[1]?.typ != 'Url-token' && tokens[1]?.typ != 'String') {
133
- errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
133
+ errors.push({ action: 'drop', message: 'parse: invalid @import', location: { src, ...position } });
134
134
  return null;
135
135
  }
136
136
  }
@@ -166,7 +166,8 @@ async function parse(iterator, opt = {}) {
166
166
  return null;
167
167
  }
168
168
  catch (error) {
169
- console.error(error);
169
+ // @ts-ignore
170
+ errors.push({ action: 'ignore', message: 'parse: ' + error.message, error });
170
171
  }
171
172
  }
172
173
  }
@@ -183,7 +184,7 @@ async function parse(iterator, opt = {}) {
183
184
  nam: renderToken(atRule, { removeComments: true }),
184
185
  val: raw.join('')
185
186
  };
186
- Object.defineProperty(node, 'raw', { enumerable: false, writable: false, value: raw });
187
+ Object.defineProperty(node, 'raw', { enumerable: false, configurable: true, writable: true, value: raw });
187
188
  if (delim.typ == 'Block-start') {
188
189
  node.chi = [];
189
190
  }
@@ -231,7 +232,7 @@ async function parse(iterator, opt = {}) {
231
232
  chi: []
232
233
  };
233
234
  let raw = [...uniq.values()];
234
- Object.defineProperty(node, 'raw', { enumerable: false, writable: true, value: raw });
235
+ Object.defineProperty(node, 'raw', { enumerable: false, configurable: true, writable: true, value: raw });
235
236
  loc = {
236
237
  sta: position,
237
238
  src
@@ -273,7 +274,7 @@ async function parse(iterator, opt = {}) {
273
274
  if (name[i].typ != 'Whitespace' && name[i].typ != 'Comment') {
274
275
  errors.push({
275
276
  action: 'drop',
276
- message: 'invalid declaration',
277
+ message: 'parse: invalid declaration',
277
278
  location: { src, ...position }
278
279
  });
279
280
  return null;
@@ -283,7 +284,7 @@ async function parse(iterator, opt = {}) {
283
284
  if (value == null) {
284
285
  errors.push({
285
286
  action: 'drop',
286
- message: 'invalid declaration',
287
+ message: 'parse: invalid declaration',
287
288
  location: { src, ...position }
288
289
  });
289
290
  return null;
@@ -291,7 +292,7 @@ async function parse(iterator, opt = {}) {
291
292
  if (value.length == 0) {
292
293
  errors.push({
293
294
  action: 'drop',
294
- message: 'invalid declaration',
295
+ message: 'parse: invalid declaration',
295
296
  location: { src, ...position }
296
297
  });
297
298
  return null;
@@ -309,7 +310,7 @@ async function parse(iterator, opt = {}) {
309
310
  if (node.val.length == 0) {
310
311
  errors.push({
311
312
  action: 'drop',
312
- message: 'invalid declaration',
313
+ message: 'parse: invalid declaration',
313
314
  location: { src, ...position }
314
315
  });
315
316
  return null;
@@ -327,13 +328,14 @@ async function parse(iterator, opt = {}) {
327
328
  }
328
329
  const iter = tokenize(iterator);
329
330
  let item;
330
- while (true) {
331
- item = iter.next().value;
332
- if (item == null) {
333
- break;
331
+ while (item = iter.next().value) {
332
+ bytesIn = item.bytesIn;
333
+ // parse error
334
+ if (item.hint != null && item.hint.startsWith('Bad-')) {
335
+ // bad token
336
+ continue;
334
337
  }
335
338
  tokens.push(item);
336
- bytesIn = item.bytesIn;
337
339
  if (item.token == ';' || item.token == '{') {
338
340
  let node = await parseNode(tokens);
339
341
  if (node != null) {
@@ -377,15 +379,28 @@ async function parse(iterator, opt = {}) {
377
379
  if (tokens.length > 0) {
378
380
  await parseNode(tokens);
379
381
  }
382
+ while (stack.length > 0 && context != ast) {
383
+ const previousNode = stack.pop();
384
+ // @ts-ignore
385
+ context = stack[stack.length - 1] || ast;
386
+ // @ts-ignore
387
+ if (options.removeEmpty && previousNode != null && previousNode.chi.length == 0 && context.chi[context.chi.length - 1] == previousNode) {
388
+ context.chi.pop();
389
+ continue;
390
+ }
391
+ break;
392
+ }
380
393
  const endParseTime = performance.now();
381
394
  if (options.minify) {
382
395
  if (ast.chi.length > 0) {
383
- minify(ast, options, true);
396
+ minify(ast, options, true, errors);
384
397
  }
385
398
  }
386
399
  const endTime = performance.now();
387
400
  return {
388
- ast, errors, stats: {
401
+ ast,
402
+ errors,
403
+ stats: {
389
404
  bytesIn,
390
405
  parse: `${(endParseTime - startTime).toFixed(2)}ms`,
391
406
  minify: `${(endTime - endParseTime).toFixed(2)}ms`,
@@ -641,41 +656,33 @@ function parseTokens(tokens, options = {}) {
641
656
  // @ts-ignore
642
657
  t.chi.pop();
643
658
  }
644
- let isColor = true;
645
659
  // @ts-ignore
646
- if (options.parseColor && ['rgb', 'rgba', 'hsl', 'hsla', 'hwb', 'device-cmyk'].includes(t.val)) {
660
+ if (options.parseColor && t.typ == 'Func' && isColor(t)) {
661
+ // if (isColor) {
647
662
  // @ts-ignore
648
- for (const v of t.chi) {
649
- if (v.typ == 'Func' && v.val == 'var') {
650
- isColor = false;
651
- break;
652
- }
653
- }
654
- if (isColor) {
655
- // @ts-ignore
656
- t.typ = 'Color';
657
- // @ts-ignore
658
- t.kin = t.val;
663
+ t.typ = 'Color';
664
+ // @ts-ignore
665
+ t.kin = t.val;
666
+ // @ts-ignore
667
+ let m = t.chi.length;
668
+ while (m-- > 0) {
659
669
  // @ts-ignore
660
- let m = t.chi.length;
661
- while (m-- > 0) {
670
+ if (['Literal'].concat(trimWhiteSpace).includes(t.chi[m].typ)) {
662
671
  // @ts-ignore
663
- if (['Literal'].concat(trimWhiteSpace).includes(t.chi[m].typ)) {
672
+ if (t.chi[m + 1]?.typ == 'Whitespace') {
664
673
  // @ts-ignore
665
- if (t.chi[m + 1]?.typ == 'Whitespace') {
666
- // @ts-ignore
667
- t.chi.splice(m + 1, 1);
668
- }
674
+ t.chi.splice(m + 1, 1);
675
+ }
676
+ // @ts-ignore
677
+ if (t.chi[m - 1]?.typ == 'Whitespace') {
669
678
  // @ts-ignore
670
- if (t.chi[m - 1]?.typ == 'Whitespace') {
671
- // @ts-ignore
672
- t.chi.splice(m - 1, 1);
673
- m--;
674
- }
679
+ t.chi.splice(m - 1, 1);
680
+ m--;
675
681
  }
676
682
  }
677
- continue;
678
683
  }
684
+ continue;
685
+ // }
679
686
  }
680
687
  if (t.typ == 'UrlFunc') {
681
688
  // @ts-ignore
@@ -700,7 +707,7 @@ function parseTokens(tokens, options = {}) {
700
707
  // @ts-ignore
701
708
  if (t.chi.length > 0) {
702
709
  // @ts-ignore
703
- parseTokens(t.chi, t.typ);
710
+ parseTokens(t.chi, options);
704
711
  if (t.typ == 'Pseudo-class-func' && t.val == ':is' && options.minify) {
705
712
  //
706
713
  const count = t.chi.filter(t => t.typ != 'Comment').length;
@@ -719,7 +726,7 @@ function parseTokens(tokens, options = {}) {
719
726
  if (t.typ == 'Iden') {
720
727
  // named color
721
728
  const value = t.val.toLowerCase();
722
- if (COLORS_NAMES[value] != null) {
729
+ if (value in COLORS_NAMES) {
723
730
  Object.assign(t, {
724
731
  typ: 'Color',
725
732
  val: COLORS_NAMES[value].length < value.length ? COLORS_NAMES[value] : value,
@@ -1,4 +1,4 @@
1
- import { isWhiteSpace, isDigit, isNewLine } from './utils/syntax.js';
1
+ import { isWhiteSpace, isNewLine, isDigit, isNonPrintable } from './utils/syntax.js';
2
2
 
3
3
  function* tokenize(iterator) {
4
4
  let ind = -1;
@@ -55,23 +55,7 @@ function* tokenize(iterator) {
55
55
  }
56
56
  break;
57
57
  }
58
- // @ts-ignore
59
- if (isNewLine(codepoint)) {
60
- if (i == 1) {
61
- buffer += value + escapeSequence.slice(0, i);
62
- next(i + 1);
63
- continue;
64
- }
65
- // else {
66
- yield pushToken(buffer + value + escapeSequence.slice(0, i), 'Bad-string');
67
- buffer = '';
68
- // }
69
- next(i + 1);
70
- break;
71
- }
72
- // not hex or new line
73
- // @ts-ignore
74
- else if (i == 1) {
58
+ if (i == 1) {
75
59
  buffer += value + sequence[i];
76
60
  next(2);
77
61
  continue;
@@ -88,7 +72,7 @@ function* tokenize(iterator) {
88
72
  else {
89
73
  buffer += String.fromCodePoint(codepoint);
90
74
  }
91
- next(escapeSequence.length + 1);
75
+ next(escapeSequence.length + 1 + (isWhiteSpace(peek()?.charCodeAt(0)) ? 1 : 0));
92
76
  continue;
93
77
  }
94
78
  buffer += next(2);
@@ -154,31 +138,18 @@ function* tokenize(iterator) {
154
138
  return char;
155
139
  }
156
140
  while (value = next()) {
157
- if (ind >= iterator.length) {
158
- if (buffer.length > 0) {
159
- yield pushToken(buffer);
160
- buffer = '';
161
- }
162
- break;
163
- }
164
141
  if (isWhiteSpace(value.charCodeAt(0))) {
165
142
  if (buffer.length > 0) {
166
143
  yield pushToken(buffer);
167
144
  buffer = '';
168
145
  }
169
146
  while (value = next()) {
170
- if (ind >= iterator.length) {
171
- break;
172
- }
173
147
  if (!isWhiteSpace(value.charCodeAt(0))) {
174
148
  break;
175
149
  }
176
150
  }
177
151
  yield pushToken('', 'Whitespace');
178
152
  buffer = '';
179
- if (ind >= iterator.length) {
180
- break;
181
- }
182
153
  }
183
154
  switch (value) {
184
155
  case '/':
@@ -192,34 +163,12 @@ function* tokenize(iterator) {
192
163
  }
193
164
  buffer += value;
194
165
  if (peek() == '*') {
195
- buffer += '*';
196
- // i++;
197
- next();
166
+ buffer += next();
198
167
  while (value = next()) {
199
- if (ind >= iterator.length) {
200
- yield pushToken(buffer, 'Bad-comment');
201
- break;
202
- }
203
- if (value == '\\') {
204
- buffer += value;
205
- value = next();
206
- if (ind >= iterator.length) {
207
- yield pushToken(buffer, 'Bad-comment');
208
- break;
209
- }
210
- buffer += value;
211
- continue;
212
- }
213
168
  if (value == '*') {
214
169
  buffer += value;
215
- value = next();
216
- if (ind >= iterator.length) {
217
- yield pushToken(buffer, 'Bad-comment');
218
- break;
219
- }
220
- buffer += value;
221
- if (value == '/') {
222
- yield pushToken(buffer, 'Comment');
170
+ if (peek() == '/') {
171
+ yield pushToken(buffer + next(), 'Comment');
223
172
  buffer = '';
224
173
  break;
225
174
  }
@@ -228,6 +177,8 @@ function* tokenize(iterator) {
228
177
  buffer += value;
229
178
  }
230
179
  }
180
+ yield pushToken(buffer, 'Bad-comment');
181
+ buffer = '';
231
182
  }
232
183
  break;
233
184
  case '<':
@@ -241,32 +192,26 @@ function* tokenize(iterator) {
241
192
  break;
242
193
  }
243
194
  buffer += value;
244
- value = next();
245
- if (ind >= iterator.length) {
246
- break;
247
- }
248
195
  if (peek(3) == '!--') {
196
+ buffer += next(3);
249
197
  while (value = next()) {
250
- if (ind >= iterator.length) {
251
- break;
252
- }
253
198
  buffer += value;
254
- if (value == '>' && prev(2) == '--') {
255
- yield pushToken(buffer, 'CDOCOMM');
256
- buffer = '';
199
+ if (value == '-' && peek(2) == '->') {
257
200
  break;
258
201
  }
259
202
  }
260
- }
261
- if (ind >= iterator.length) {
262
- yield pushToken(buffer, 'BADCDO');
203
+ if (value === '') {
204
+ yield pushToken(buffer, 'Bad-cdo');
205
+ }
206
+ else {
207
+ yield pushToken(buffer + next(2), 'CDOCOMM');
208
+ }
263
209
  buffer = '';
264
210
  }
265
211
  break;
266
212
  case '\\':
267
- value = next();
268
213
  // EOF
269
- if (ind + 1 >= iterator.length) {
214
+ if (!(value = next())) {
270
215
  // end of stream ignore \\
271
216
  yield pushToken(buffer);
272
217
  buffer = '';
@@ -285,8 +230,7 @@ function* tokenize(iterator) {
285
230
  buffer = '';
286
231
  }
287
232
  buffer += value;
288
- value = next();
289
- if (ind >= iterator.length) {
233
+ if (!(value = next())) {
290
234
  yield pushToken(buffer);
291
235
  buffer = '';
292
236
  break;
@@ -364,73 +308,150 @@ function* tokenize(iterator) {
364
308
  if (buffer == 'url(') {
365
309
  yield pushToken(buffer);
366
310
  buffer = '';
367
- // consume either string or url token
368
- let whitespace = '';
369
- value = peek();
370
- while (isWhiteSpace(value.charCodeAt(0))) {
371
- whitespace += value;
372
- }
373
- if (whitespace.length > 0) {
374
- next(whitespace.length);
375
- }
311
+ consumeWhiteSpace();
376
312
  value = peek();
313
+ let cp;
314
+ let whitespace = '';
315
+ let hasWhiteSpace = false;
316
+ let errorState = false;
377
317
  if (value == '"' || value == "'") {
378
- yield* consumeString(next());
318
+ const quote = value;
319
+ let inquote = true;
320
+ let hasNewLine = false;
321
+ buffer = next();
322
+ while (value = next()) {
323
+ cp = value.charCodeAt(0);
324
+ // consume an invalid string
325
+ if (inquote) {
326
+ buffer += value;
327
+ if (isNewLine(cp)) {
328
+ hasNewLine = true;
329
+ while (value = next()) {
330
+ buffer += value;
331
+ if (value == ';') {
332
+ inquote = false;
333
+ break;
334
+ }
335
+ }
336
+ if (value === '') {
337
+ yield pushToken(buffer, 'Bad-string');
338
+ buffer = '';
339
+ break;
340
+ }
341
+ cp = value.charCodeAt(0);
342
+ }
343
+ // '\\'
344
+ if (cp == 0x5c) {
345
+ buffer += next();
346
+ }
347
+ else if (value == quote) {
348
+ inquote = false;
349
+ }
350
+ continue;
351
+ }
352
+ if (!inquote) {
353
+ if (isWhiteSpace(cp)) {
354
+ whitespace += value;
355
+ while (value = peek()) {
356
+ hasWhiteSpace = true;
357
+ if (isWhiteSpace(value?.charCodeAt(0))) {
358
+ whitespace += next();
359
+ continue;
360
+ }
361
+ break;
362
+ }
363
+ if (!(value = next())) {
364
+ yield pushToken(buffer, hasNewLine ? 'Bad-url-token' : 'Url-token');
365
+ buffer = '';
366
+ break;
367
+ }
368
+ }
369
+ cp = value.charCodeAt(0);
370
+ // ')'
371
+ if (cp == 0x29) {
372
+ yield pushToken(buffer, hasNewLine ? 'Bad-string' : 'String');
373
+ yield pushToken('', 'End-parens');
374
+ buffer = '';
375
+ break;
376
+ }
377
+ while (value = next()) {
378
+ cp = value.charCodeAt(0);
379
+ if (cp == 0x5c) {
380
+ buffer += value + next();
381
+ continue;
382
+ }
383
+ if (cp == 0x29) {
384
+ yield pushToken(buffer, 'Bad-string');
385
+ yield pushToken('', 'End-parens');
386
+ buffer = '';
387
+ break;
388
+ }
389
+ buffer += value;
390
+ }
391
+ if (hasNewLine) {
392
+ yield pushToken(buffer, 'Bad-string');
393
+ buffer = '';
394
+ }
395
+ break;
396
+ }
397
+ buffer += value;
398
+ }
379
399
  break;
380
400
  }
381
401
  else {
382
402
  buffer = '';
383
- do {
384
- let cp = value.charCodeAt(0);
385
- // EOF -
386
- if (cp == null) {
387
- yield pushToken('', 'Bad-url-token');
388
- break;
389
- }
403
+ while (value = next()) {
404
+ cp = value.charCodeAt(0);
390
405
  // ')'
391
- if (cp == 0x29 || cp == null) {
392
- if (buffer.length == 0) {
393
- yield pushToken(buffer, 'Bad-url-token');
394
- }
395
- else {
396
- yield pushToken(buffer, 'Url-token');
397
- }
398
- if (cp != null) {
399
- yield pushToken(next());
400
- }
406
+ if (cp == 0x29) {
407
+ yield pushToken(buffer, 'Url-token');
408
+ yield pushToken('', 'End-parens');
409
+ buffer = '';
401
410
  break;
402
411
  }
403
412
  if (isWhiteSpace(cp)) {
404
- whitespace = next();
405
- while (true) {
406
- value = peek();
413
+ hasWhiteSpace = true;
414
+ whitespace = value;
415
+ while (isWhiteSpace(peek()?.charCodeAt(0))) {
416
+ whitespace += next();
417
+ }
418
+ continue;
419
+ }
420
+ if (isNonPrintable(cp) ||
421
+ // '"'
422
+ cp == 0x22 ||
423
+ // "'"
424
+ cp == 0x27 ||
425
+ // \('
426
+ cp == 0x28 ||
427
+ hasWhiteSpace) {
428
+ errorState = true;
429
+ }
430
+ if (errorState) {
431
+ buffer += whitespace + value;
432
+ while (value = peek()) {
407
433
  cp = value.charCodeAt(0);
408
- if (isWhiteSpace(cp)) {
409
- whitespace += value;
434
+ if (cp == 0x5c) {
435
+ buffer += next(2);
410
436
  continue;
411
437
  }
412
- break;
413
- }
414
- if (cp == null || cp == 0x29) {
415
- continue;
416
- }
417
- // bad url token
418
- buffer += next(whitespace.length);
419
- do {
420
- value = peek();
421
- cp = value.charCodeAt(0);
422
- if (cp == null || cp == 0x29) {
438
+ // ')'
439
+ if (cp == 0x29) {
423
440
  break;
424
441
  }
425
442
  buffer += next();
426
- } while (true);
443
+ }
427
444
  yield pushToken(buffer, 'Bad-url-token');
428
- continue;
445
+ buffer = '';
446
+ break;
429
447
  }
430
- buffer += next();
431
- value = peek();
432
- } while (true);
448
+ buffer += value;
449
+ }
450
+ }
451
+ if (buffer !== '') {
452
+ yield pushToken(buffer, 'Url-token');
433
453
  buffer = '';
454
+ break;
434
455
  }
435
456
  break;
436
457
  }
@@ -453,8 +474,7 @@ function* tokenize(iterator) {
453
474
  yield pushToken(buffer);
454
475
  buffer = '';
455
476
  }
456
- const important = peek(9);
457
- if (important == 'important') {
477
+ if (peek(9) == 'important') {
458
478
  yield pushToken('', 'Important');
459
479
  next(9);
460
480
  buffer = '';