@tbela99/css-parser 0.0.1-rc5 → 0.0.1-rc7

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,14 +1,19 @@
1
- function* walk(node) {
2
- // @ts-ignore
3
- yield* doWalk(node, null, null);
4
- }
5
- function* doWalk(node, parent, root) {
1
+ function* walk(node, parent, root) {
6
2
  yield { node, parent, root };
7
3
  if ('chi' in node) {
8
4
  for (const child of node.chi) {
9
- yield* doWalk(child, node, (root ?? node));
5
+ yield* walk(child, node, (root ?? node));
6
+ }
7
+ }
8
+ }
9
+ function* walkValues(values, parent) {
10
+ for (const value of values) {
11
+ // @ts-ignore
12
+ yield { value, parent };
13
+ if ('chi' in value) {
14
+ yield* walkValues(value.chi, value);
10
15
  }
11
16
  }
12
17
  }
13
18
 
14
- export { walk };
19
+ export { walk, walkValues };
@@ -1,8 +1,8 @@
1
1
  import { PropertySet } from './set.js';
2
2
  import '../../renderer/utils/color.js';
3
- import { getConfig } from '../utils/config.js';
4
3
  import { PropertyMap } from './map.js';
5
4
  import { parseString } from '../parse.js';
5
+ import { getConfig } from '../utils/config.js';
6
6
 
7
7
  const config = getConfig();
8
8
  class PropertyList {
@@ -340,6 +340,14 @@ class PropertyMap {
340
340
  acc.push(...curr);
341
341
  return acc;
342
342
  }, []);
343
+ if (this.config.mapping != null) {
344
+ const val = values.reduce((acc, curr) => acc + renderToken(curr, { removeComments: true }), '');
345
+ if (val in this.config.mapping) {
346
+ values.length = 0;
347
+ // @ts-ignore
348
+ values.push({ typ: ['"', "'"].includes(val.charAt(0)) ? 'String' : 'Iden', val: this.config.mapping[val] });
349
+ }
350
+ }
343
351
  iterable = [{
344
352
  typ: 'Declaration',
345
353
  nam: this.config.shorthand,
@@ -53,14 +53,18 @@ async function parse(iterator, opt = {}) {
53
53
  let i;
54
54
  let loc;
55
55
  for (i = 0; i < tokens.length; i++) {
56
- if (tokens[i].typ == 'Comment') {
57
- // @ts-ignore
58
- context.chi.push(tokens[i]);
56
+ if (tokens[i].typ == 'Comment' || tokens[i].typ == 'CDOCOMM') {
59
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
+ }
60
62
  loc = {
61
63
  sta: position,
62
64
  src
63
65
  };
66
+ // @ts-ignore
67
+ context.chi.push(tokens[i]);
64
68
  if (options.sourcemap) {
65
69
  tokens[i].loc = loc;
66
70
  }
@@ -91,7 +95,7 @@ async function parse(iterator, opt = {}) {
91
95
  const atRule = tokens.shift();
92
96
  const position = map.get(atRule);
93
97
  if (atRule.val == 'charset' && position.ind > 0) {
94
- errors.push({ action: 'drop', message: 'invalid @charset', location: { src, ...position } });
98
+ errors.push({ action: 'drop', message: 'parse: invalid @charset', location: { src, ...position } });
95
99
  return null;
96
100
  }
97
101
  // @ts-ignore
@@ -121,12 +125,12 @@ async function parse(iterator, opt = {}) {
121
125
  }
122
126
  // @ts-ignore
123
127
  if (tokens[0]?.typ != 'String' && tokens[0]?.typ != 'UrlFunc') {
124
- errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
128
+ errors.push({ action: 'drop', message: 'parse: invalid @import', location: { src, ...position } });
125
129
  return null;
126
130
  }
127
131
  // @ts-ignore
128
132
  if (tokens[0].typ == 'UrlFunc' && tokens[1]?.typ != 'Url-token' && tokens[1]?.typ != 'String') {
129
- errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
133
+ errors.push({ action: 'drop', message: 'parse: invalid @import', location: { src, ...position } });
130
134
  return null;
131
135
  }
132
136
  }
@@ -162,7 +166,8 @@ async function parse(iterator, opt = {}) {
162
166
  return null;
163
167
  }
164
168
  catch (error) {
165
- console.error(error);
169
+ // @ts-ignore
170
+ errors.push({ action: 'ignore', message: 'parse: ' + error.message, error });
166
171
  }
167
172
  }
168
173
  }
@@ -179,7 +184,7 @@ async function parse(iterator, opt = {}) {
179
184
  nam: renderToken(atRule, { removeComments: true }),
180
185
  val: raw.join('')
181
186
  };
182
- Object.defineProperty(node, 'raw', { enumerable: false, writable: false, value: raw });
187
+ Object.defineProperty(node, 'raw', { enumerable: false, configurable: true, writable: true, value: raw });
183
188
  if (delim.typ == 'Block-start') {
184
189
  node.chi = [];
185
190
  }
@@ -199,7 +204,7 @@ async function parse(iterator, opt = {}) {
199
204
  if (delim.typ == 'Block-start') {
200
205
  const position = map.get(tokens[0]);
201
206
  const uniq = new Map;
202
- parseTokens(tokens, { minify: options.minify }).reduce((acc, curr, index, array) => {
207
+ parseTokens(tokens, { minify: true }).reduce((acc, curr, index, array) => {
203
208
  if (curr.typ == 'Whitespace') {
204
209
  if (trimWhiteSpace.includes(array[index - 1]?.typ) ||
205
210
  trimWhiteSpace.includes(array[index + 1]?.typ) ||
@@ -208,7 +213,7 @@ async function parse(iterator, opt = {}) {
208
213
  return acc;
209
214
  }
210
215
  }
211
- let t = renderToken(curr, { minify: true });
216
+ let t = renderToken(curr, { minify: false });
212
217
  if (t == ',') {
213
218
  acc.push([]);
214
219
  }
@@ -227,7 +232,7 @@ async function parse(iterator, opt = {}) {
227
232
  chi: []
228
233
  };
229
234
  let raw = [...uniq.values()];
230
- Object.defineProperty(node, 'raw', { enumerable: false, writable: true, value: raw });
235
+ Object.defineProperty(node, 'raw', { enumerable: false, configurable: true, writable: true, value: raw });
231
236
  loc = {
232
237
  sta: position,
233
238
  src
@@ -269,7 +274,7 @@ async function parse(iterator, opt = {}) {
269
274
  if (name[i].typ != 'Whitespace' && name[i].typ != 'Comment') {
270
275
  errors.push({
271
276
  action: 'drop',
272
- message: 'invalid declaration',
277
+ message: 'parse: invalid declaration',
273
278
  location: { src, ...position }
274
279
  });
275
280
  return null;
@@ -279,7 +284,7 @@ async function parse(iterator, opt = {}) {
279
284
  if (value == null) {
280
285
  errors.push({
281
286
  action: 'drop',
282
- message: 'invalid declaration',
287
+ message: 'parse: invalid declaration',
283
288
  location: { src, ...position }
284
289
  });
285
290
  return null;
@@ -287,7 +292,7 @@ async function parse(iterator, opt = {}) {
287
292
  if (value.length == 0) {
288
293
  errors.push({
289
294
  action: 'drop',
290
- message: 'invalid declaration',
295
+ message: 'parse: invalid declaration',
291
296
  location: { src, ...position }
292
297
  });
293
298
  return null;
@@ -305,7 +310,7 @@ async function parse(iterator, opt = {}) {
305
310
  if (node.val.length == 0) {
306
311
  errors.push({
307
312
  action: 'drop',
308
- message: 'invalid declaration',
313
+ message: 'parse: invalid declaration',
309
314
  location: { src, ...position }
310
315
  });
311
316
  return null;
@@ -323,18 +328,14 @@ async function parse(iterator, opt = {}) {
323
328
  }
324
329
  const iter = tokenize(iterator);
325
330
  let item;
326
- while (true) {
327
- item = iter.next().value;
328
- if (item == null) {
329
- break;
330
- }
331
- // console.debug({item});
331
+ while (item = iter.next().value) {
332
+ bytesIn = item.bytesIn;
333
+ // parse error
332
334
  if (item.hint != null && item.hint.startsWith('Bad-')) {
333
335
  // bad token
334
336
  continue;
335
337
  }
336
338
  tokens.push(item);
337
- bytesIn = item.bytesIn;
338
339
  if (item.token == ';' || item.token == '{') {
339
340
  let node = await parseNode(tokens);
340
341
  if (node != null) {
@@ -378,15 +379,28 @@ async function parse(iterator, opt = {}) {
378
379
  if (tokens.length > 0) {
379
380
  await parseNode(tokens);
380
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
+ }
381
393
  const endParseTime = performance.now();
382
394
  if (options.minify) {
383
395
  if (ast.chi.length > 0) {
384
- minify(ast, options, true);
396
+ minify(ast, options, true, errors);
385
397
  }
386
398
  }
387
399
  const endTime = performance.now();
388
400
  return {
389
- ast, errors, stats: {
401
+ ast,
402
+ errors,
403
+ stats: {
390
404
  bytesIn,
391
405
  parse: `${(endParseTime - startTime).toFixed(2)}ms`,
392
406
  minify: `${(endTime - endParseTime).toFixed(2)}ms`,
@@ -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);
@@ -137,12 +121,16 @@ function* tokenize(iterator) {
137
121
  }
138
122
  function next(count = 1) {
139
123
  let char = '';
140
- while (count-- > 0 && ind < iterator.length) {
124
+ let chr = '';
125
+ if (count < 0) {
126
+ return '';
127
+ }
128
+ while (count-- && (chr = iterator.charAt(ind + 1))) {
129
+ char += chr;
141
130
  const codepoint = iterator.charCodeAt(++ind);
142
131
  if (isNaN(codepoint)) {
143
132
  return char;
144
133
  }
145
- char += iterator.charAt(ind);
146
134
  if (isNewLine(codepoint)) {
147
135
  lin++;
148
136
  col = 0;
@@ -212,17 +200,18 @@ function* tokenize(iterator) {
212
200
  buffer += next(3);
213
201
  while (value = next()) {
214
202
  buffer += value;
215
- if (value == '>' && prev(2) == '--') {
216
- yield pushToken(buffer, 'CDOCOMM');
217
- buffer = '';
203
+ if (value == '-' && peek(2) == '->') {
218
204
  break;
219
205
  }
220
206
  }
207
+ if (value === '') {
208
+ yield pushToken(buffer, 'Bad-cdo');
209
+ }
210
+ else {
211
+ yield pushToken(buffer + next(2), 'CDOCOMM');
212
+ }
213
+ buffer = '';
221
214
  }
222
- // if (!peek()) {
223
- yield pushToken(buffer, 'Bad-cdo');
224
- buffer = '';
225
- // }
226
215
  break;
227
216
  case '\\':
228
217
  // EOF
@@ -323,73 +312,150 @@ function* tokenize(iterator) {
323
312
  if (buffer == 'url(') {
324
313
  yield pushToken(buffer);
325
314
  buffer = '';
326
- // consume either string or url token
327
- let whitespace = '';
328
- value = peek();
329
- while (isWhiteSpace(value.charCodeAt(0))) {
330
- whitespace += value;
331
- }
332
- if (whitespace.length > 0) {
333
- next(whitespace.length);
334
- }
315
+ consumeWhiteSpace();
335
316
  value = peek();
317
+ let cp;
318
+ let whitespace = '';
319
+ let hasWhiteSpace = false;
320
+ let errorState = false;
336
321
  if (value == '"' || value == "'") {
337
- yield* consumeString(next());
322
+ const quote = value;
323
+ let inquote = true;
324
+ let hasNewLine = false;
325
+ buffer = next();
326
+ while (value = next()) {
327
+ cp = value.charCodeAt(0);
328
+ // consume an invalid string
329
+ if (inquote) {
330
+ buffer += value;
331
+ if (isNewLine(cp)) {
332
+ hasNewLine = true;
333
+ while (value = next()) {
334
+ buffer += value;
335
+ if (value == ';') {
336
+ inquote = false;
337
+ break;
338
+ }
339
+ }
340
+ if (value === '') {
341
+ yield pushToken(buffer, 'Bad-string');
342
+ buffer = '';
343
+ break;
344
+ }
345
+ cp = value.charCodeAt(0);
346
+ }
347
+ // '\\'
348
+ if (cp == 0x5c) {
349
+ buffer += next();
350
+ }
351
+ else if (value == quote) {
352
+ inquote = false;
353
+ }
354
+ continue;
355
+ }
356
+ if (!inquote) {
357
+ if (isWhiteSpace(cp)) {
358
+ whitespace += value;
359
+ while (value = peek()) {
360
+ hasWhiteSpace = true;
361
+ if (isWhiteSpace(value?.charCodeAt(0))) {
362
+ whitespace += next();
363
+ continue;
364
+ }
365
+ break;
366
+ }
367
+ if (!(value = next())) {
368
+ yield pushToken(buffer, hasNewLine ? 'Bad-url-token' : 'Url-token');
369
+ buffer = '';
370
+ break;
371
+ }
372
+ }
373
+ cp = value.charCodeAt(0);
374
+ // ')'
375
+ if (cp == 0x29) {
376
+ yield pushToken(buffer, hasNewLine ? 'Bad-string' : 'String');
377
+ yield pushToken('', 'End-parens');
378
+ buffer = '';
379
+ break;
380
+ }
381
+ while (value = next()) {
382
+ cp = value.charCodeAt(0);
383
+ if (cp == 0x5c) {
384
+ buffer += value + next();
385
+ continue;
386
+ }
387
+ if (cp == 0x29) {
388
+ yield pushToken(buffer, 'Bad-string');
389
+ yield pushToken('', 'End-parens');
390
+ buffer = '';
391
+ break;
392
+ }
393
+ buffer += value;
394
+ }
395
+ if (hasNewLine) {
396
+ yield pushToken(buffer, 'Bad-string');
397
+ buffer = '';
398
+ }
399
+ break;
400
+ }
401
+ buffer += value;
402
+ }
338
403
  break;
339
404
  }
340
405
  else {
341
406
  buffer = '';
342
- do {
343
- let cp = value.charCodeAt(0);
344
- // EOF -
345
- if (cp == null) {
346
- yield pushToken('', 'Bad-url-token');
347
- break;
348
- }
407
+ while (value = next()) {
408
+ cp = value.charCodeAt(0);
349
409
  // ')'
350
- if (cp == 0x29 || cp == null) {
351
- if (buffer.length == 0) {
352
- yield pushToken(buffer, 'Bad-url-token');
353
- }
354
- else {
355
- yield pushToken(buffer, 'Url-token');
356
- }
357
- if (cp != null) {
358
- yield pushToken(next());
359
- }
410
+ if (cp == 0x29) {
411
+ yield pushToken(buffer, 'Url-token');
412
+ yield pushToken('', 'End-parens');
413
+ buffer = '';
360
414
  break;
361
415
  }
362
416
  if (isWhiteSpace(cp)) {
363
- whitespace = next();
364
- while (true) {
365
- value = peek();
417
+ hasWhiteSpace = true;
418
+ whitespace = value;
419
+ while (isWhiteSpace(peek()?.charCodeAt(0))) {
420
+ whitespace += next();
421
+ }
422
+ continue;
423
+ }
424
+ if (isNonPrintable(cp) ||
425
+ // '"'
426
+ cp == 0x22 ||
427
+ // "'"
428
+ cp == 0x27 ||
429
+ // \('
430
+ cp == 0x28 ||
431
+ hasWhiteSpace) {
432
+ errorState = true;
433
+ }
434
+ if (errorState) {
435
+ buffer += whitespace + value;
436
+ while (value = peek()) {
366
437
  cp = value.charCodeAt(0);
367
- if (isWhiteSpace(cp)) {
368
- whitespace += value;
438
+ if (cp == 0x5c) {
439
+ buffer += next(2);
369
440
  continue;
370
441
  }
371
- break;
372
- }
373
- if (cp == null || cp == 0x29) {
374
- continue;
375
- }
376
- // bad url token
377
- buffer += next(whitespace.length);
378
- do {
379
- value = peek();
380
- cp = value.charCodeAt(0);
381
- if (cp == null || cp == 0x29) {
442
+ // ')'
443
+ if (cp == 0x29) {
382
444
  break;
383
445
  }
384
446
  buffer += next();
385
- } while (true);
447
+ }
386
448
  yield pushToken(buffer, 'Bad-url-token');
387
- continue;
449
+ buffer = '';
450
+ break;
388
451
  }
389
- buffer += next();
390
- value = peek();
391
- } while (true);
452
+ buffer += value;
453
+ }
454
+ }
455
+ if (buffer !== '') {
456
+ yield pushToken(buffer, 'Url-token');
392
457
  buffer = '';
458
+ break;
393
459
  }
394
460
  break;
395
461
  }
@@ -96,6 +96,15 @@ function isIdent(name) {
96
96
  }
97
97
  return true;
98
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
+ }
99
108
  function isPseudo(name) {
100
109
  return name.charAt(0) == ':' &&
101
110
  ((name.endsWith('(') && isIdent(name.charAt(1) == ':' ? name.slice(2, -1) : name.slice(1, -1))) ||
@@ -239,22 +248,6 @@ function isHexColor(name) {
239
248
  }
240
249
  return true;
241
250
  }
242
- function isHexDigit(name) {
243
- if (name.length || name.length > 6) {
244
- return false;
245
- }
246
- for (let chr of name) {
247
- let codepoint = chr.charCodeAt(0);
248
- if (!isDigit(codepoint) &&
249
- // A F
250
- !(codepoint >= 0x41 && codepoint <= 0x46) &&
251
- // a f
252
- !(codepoint >= 0x61 && codepoint <= 0x66)) {
253
- return false;
254
- }
255
- }
256
- return true;
257
- }
258
251
  function isFunction(name) {
259
252
  return name.endsWith('(') && isIdent(name.slice(0, -1));
260
253
  }
@@ -271,4 +264,4 @@ function isWhiteSpace(codepoint) {
271
264
  codepoint == 0xa || codepoint == 0xc || codepoint == 0xd;
272
265
  }
273
266
 
274
- export { isAngle, isAtKeyword, isColor, 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 };