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

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/dist/index.d.ts CHANGED
@@ -91,12 +91,14 @@ interface BlockEndToken {
91
91
  }
92
92
  interface AttrStartToken {
93
93
  typ: 'Attr-start';
94
+ chi?: Token[];
94
95
  }
95
96
  interface AttrEndToken {
96
97
  typ: 'Attr-end';
97
98
  }
98
99
  interface ParensStartToken {
99
100
  typ: 'Start-parens';
101
+ chi?: Token[];
100
102
  }
101
103
  interface ParensEndToken {
102
104
  typ: 'End-parens';
@@ -131,9 +133,15 @@ interface DashMatchToken {
131
133
  interface LessThanToken {
132
134
  typ: 'Lt';
133
135
  }
136
+ interface LessThanOrEqualToken {
137
+ typ: 'Lte';
138
+ }
134
139
  interface GreaterThanToken {
135
140
  typ: 'Gt';
136
141
  }
142
+ interface GreaterThanOrEqualToken {
143
+ typ: 'Gte';
144
+ }
137
145
  interface PseudoClassToken {
138
146
  typ: 'Pseudo-class';
139
147
  val: string;
@@ -171,7 +179,31 @@ interface AttrToken {
171
179
  typ: 'Attr';
172
180
  chi: Token[];
173
181
  }
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;
182
+ 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 | LessThanOrEqualToken | GreaterThanToken | GreaterThanOrEqualToken | PseudoClassToken | PseudoClassFunctionToken | DelimToken | BadUrlToken | UrlToken | ImportantToken | ColorToken | AttrToken | EOFToken;
183
+
184
+ interface PropertyMapType {
185
+ default: string[];
186
+ types: string[];
187
+ keywords: string[];
188
+ required?: boolean;
189
+ multiple?: boolean;
190
+ prefix?: {
191
+ typ: 'Literal';
192
+ val: string;
193
+ };
194
+ previous?: string;
195
+ separator?: {
196
+ typ: 'Comma';
197
+ };
198
+ constraints?: {
199
+ [key: string]: {
200
+ [key: string]: any;
201
+ };
202
+ };
203
+ mapping?: {
204
+ [key: string]: any;
205
+ };
206
+ }
175
207
 
176
208
  interface PropertiesConfig {
177
209
  properties: PropertiesConfigProperties;
@@ -629,8 +661,10 @@ declare function isWhiteSpace(codepoint: number): boolean;
629
661
 
630
662
  declare const getConfig: () => PropertiesConfig;
631
663
 
664
+ declare function matchType(val: Token, properties: PropertyMapType): boolean;
665
+
632
666
  declare function render(data: AstNode, opt?: RenderOptions): RenderResult;
633
- declare function renderToken(token: Token, options?: RenderOptions): string;
667
+ declare function renderToken(token: Token, options?: RenderOptions, reducer?: (acc: string, curr: Token) => string): string;
634
668
 
635
669
  declare const combinators: string[];
636
670
  declare function minify(ast: AstNode, options?: ParserOptions, recursive?: boolean): AstNode;
@@ -661,4 +695,4 @@ declare function resolve(url: string, currentDirectory: string, cwd?: string): {
661
695
  declare function parse(iterator: string, opt?: ParserOptions): Promise<ParseResult>;
662
696
  declare function transform(css: string, options?: TransformOptions): Promise<TransformResult>;
663
697
 
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 };
698
+ 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 };
@@ -344,6 +344,17 @@ function minify(ast, options = {}, recursive = false) {
344
344
  continue;
345
345
  // }
346
346
  }
347
+ // @ts-ignore
348
+ if (hasDeclaration(node)) {
349
+ // @ts-ignore
350
+ minifyRule(node);
351
+ }
352
+ else {
353
+ minify(node, options, recursive);
354
+ }
355
+ previous = node;
356
+ nodeIndex = i;
357
+ continue;
347
358
  }
348
359
  // @ts-ignore
349
360
  if (node.typ == 'Rule') {
@@ -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,8 @@ class PropertyMap {
52
55
  i--;
53
56
  continue;
54
57
  }
55
- if (matchType(acc[i], props)) {
58
+ // @ts-ignore
59
+ if (('propertyName' in acc[i] && acc[i].propertyName == property) || matchType(acc[i], props)) {
56
60
  if ('prefix' in props && props.previous != null && !(props.previous in tokens)) {
57
61
  return acc;
58
62
  }
@@ -186,10 +190,12 @@ class PropertyMap {
186
190
  }
187
191
  else {
188
192
  let count = 0;
193
+ let match;
189
194
  const separator = this.config.separator;
190
195
  const tokens = {};
191
196
  // @ts-ignore
192
- /* const valid: string[] =*/ Object.entries(this.config.properties).reduce((acc, curr) => {
197
+ /* const valid: string[] =*/
198
+ Object.entries(this.config.properties).reduce((acc, curr) => {
193
199
  if (!this.declarations.has(curr[0])) {
194
200
  if (curr[1].required) {
195
201
  acc.push(curr[0]);
@@ -198,33 +204,40 @@ class PropertyMap {
198
204
  }
199
205
  let current = 0;
200
206
  const props = this.config.properties[curr[0]];
201
- const declaration = this.declarations.get(curr[0]);
202
- // @ts-ignore
203
- for (const val of (declaration instanceof PropertySet ? [...declaration][0] : declaration).val) {
204
- if (separator != null && separator.typ == val.typ && eq(separator, val)) {
205
- current++;
206
- if (tokens[curr[0]].length == current) {
207
- tokens[curr[0]].push([]);
207
+ const properties = this.declarations.get(curr[0]);
208
+ for (const declaration of [(properties instanceof PropertySet ? [...properties][0] : properties)]) {
209
+ // @ts-ignore
210
+ for (const val of declaration.val) {
211
+ if (separator != null && separator.typ == val.typ && eq(separator, val)) {
212
+ current++;
213
+ if (tokens[curr[0]].length == current) {
214
+ tokens[curr[0]].push([]);
215
+ }
216
+ continue;
208
217
  }
209
- continue;
210
- }
211
- if (val.typ == 'Whitespace' || val.typ == 'Comment') {
212
- continue;
213
- }
214
- if (props.multiple && props.separator != null && props.separator.typ == val.typ && eq(props.separator, val)) {
215
- continue;
216
- }
217
- if (matchType(val, curr[1])) {
218
- if (!(curr[0] in tokens)) {
219
- tokens[curr[0]] = [[]];
218
+ if (val.typ == 'Whitespace' || val.typ == 'Comment') {
219
+ continue;
220
+ }
221
+ if (props.multiple && props.separator != null && props.separator.typ == val.typ && eq(props.separator, val)) {
222
+ continue;
223
+ }
224
+ // @ts-ignore
225
+ match = val.typ == 'Comment' || matchType(val, curr[1]);
226
+ if (isShorthand) {
227
+ isShorthand = match;
228
+ }
229
+ // @ts-ignore
230
+ if (('propertyName' in val && val.propertyName == property) || match) {
231
+ if (!(curr[0] in tokens)) {
232
+ tokens[curr[0]] = [[]];
233
+ }
234
+ // is default value
235
+ tokens[curr[0]][current].push(val);
236
+ }
237
+ else {
238
+ acc.push(curr[0]);
239
+ break;
220
240
  }
221
- // is default value
222
- tokens[curr[0]][current].push(val);
223
- // continue;
224
- }
225
- else {
226
- acc.push(curr[0]);
227
- break;
228
241
  }
229
242
  }
230
243
  if (count == 0) {
@@ -233,7 +246,12 @@ class PropertyMap {
233
246
  return acc;
234
247
  }, []);
235
248
  count++;
236
- if (!Object.values(tokens).every(v => v.length == count)) {
249
+ if (!isShorthand || Object.entries(this.config.properties).some(entry => {
250
+ // missing required property
251
+ return entry[1].required && !(entry[0] in tokens);
252
+ }) ||
253
+ // @ts-ignore
254
+ !Object.values(tokens).every(v => v.filter(t => t.typ != 'Comment').length == count)) {
237
255
  // @ts-ignore
238
256
  iterable = this.declarations.values();
239
257
  }
@@ -5,6 +5,7 @@ import { minify, combinators } from '../ast/minify.js';
5
5
  import { tokenize } from './tokenize.js';
6
6
 
7
7
  const urlTokenMatcher = /^(["']?)[a-zA-Z0-9_/.-][a-zA-Z0-9_/:.#?-]+(\1)$/;
8
+ const trimWhiteSpace = ['Gt', 'Gte', 'Lt', 'Lte'];
8
9
  const funcLike = ['Start-parens', 'Func', 'UrlFunc', 'Pseudo-class-func'];
9
10
  /**
10
11
  *
@@ -51,6 +52,10 @@ async function parse(iterator, opt = {}) {
51
52
  let tokens = results.map(mapToken);
52
53
  let i;
53
54
  let loc;
55
+ // if ((<Token>tokens.at(-1))?.typ == 'EOF') {
56
+ //
57
+ // tokens.pop();
58
+ // }
54
59
  for (i = 0; i < tokens.length; i++) {
55
60
  if (tokens[i].typ == 'Comment') {
56
61
  // @ts-ignore
@@ -169,7 +174,7 @@ async function parse(iterator, opt = {}) {
169
174
  // https://www.w3.org/TR/css-nesting-1/#conditionals
170
175
  // allowed nesting at-rules
171
176
  // there must be a top level rule in the stack
172
- const raw = tokens.reduce((acc, curr) => {
177
+ const raw = parseTokens(tokens, { minify: options.minify }).reduce((acc, curr) => {
173
178
  acc.push(renderToken(curr, { removeComments: true }));
174
179
  return acc;
175
180
  }, []);
@@ -200,8 +205,8 @@ async function parse(iterator, opt = {}) {
200
205
  const uniq = new Map;
201
206
  parseTokens(tokens, { minify: options.minify }).reduce((acc, curr, index, array) => {
202
207
  if (curr.typ == 'Whitespace') {
203
- if (array[index - 1]?.typ == 'Gt' ||
204
- array[index + 1]?.typ == 'Gt' ||
208
+ if (trimWhiteSpace.includes(array[index - 1]?.typ) ||
209
+ trimWhiteSpace.includes(array[index + 1]?.typ) ||
205
210
  combinators.includes(array[index - 1]?.val) ||
206
211
  combinators.includes(array[index + 1]?.val)) {
207
212
  return acc;
@@ -389,13 +394,13 @@ async function parse(iterator, opt = {}) {
389
394
  };
390
395
  }
391
396
  function parseString(src, options = { location: false }) {
392
- return [...tokenize(src)].map(t => {
397
+ return parseTokens([...tokenize(src)].map(t => {
393
398
  const token = getTokenType(t.token, t.hint);
394
399
  if (options.location) {
395
400
  Object.assign(token, { loc: t.position });
396
401
  }
397
402
  return token;
398
- });
403
+ }));
399
404
  }
400
405
  function getTokenType(val, hint) {
401
406
  if (val === '' && hint == null) {
@@ -405,7 +410,7 @@ function getTokenType(val, hint) {
405
410
  return ([
406
411
  'Whitespace', 'Semi-colon', 'Colon', 'Block-start',
407
412
  'Block-start', 'Attr-start', 'Attr-end', 'Start-parens', 'End-parens',
408
- 'Comma', 'Gt', 'Lt'
413
+ 'Comma', 'Gt', 'Lt', 'Gte', 'Lte', 'EOF'
409
414
  ].includes(hint) ? { typ: hint } : { typ: hint, val });
410
415
  }
411
416
  if (val == ' ') {
@@ -533,11 +538,12 @@ function parseTokens(tokens, options = {}) {
533
538
  const t = tokens[i];
534
539
  if (t.typ == 'Whitespace' && ((i == 0 ||
535
540
  i + 1 == tokens.length ||
536
- ['Comma'].includes(tokens[i + 1].typ) ||
541
+ ['Comma', 'Gte', 'Lte'].includes(tokens[i + 1].typ)) ||
537
542
  (i > 0 &&
538
- tokens[i + 1]?.typ != 'Literal' &&
539
- funcLike.includes(tokens[i - 1].typ) &&
540
- !['var', 'calc'].includes(tokens[i - 1].val))))) {
543
+ // tokens[i + 1]?.typ != 'Literal' ||
544
+ // funcLike.includes(tokens[i - 1].typ) &&
545
+ // !['var', 'calc'].includes((<FunctionToken>tokens[i - 1]).val)))) &&
546
+ trimWhiteSpace.includes(tokens[i - 1].typ)))) {
541
547
  tokens.splice(i--, 1);
542
548
  continue;
543
549
  }
@@ -654,7 +660,7 @@ function parseTokens(tokens, options = {}) {
654
660
  let m = t.chi.length;
655
661
  while (m-- > 0) {
656
662
  // @ts-ignore
657
- if (t.chi[m].typ == 'Literal') {
663
+ if (['Literal'].concat(trimWhiteSpace).includes(t.chi[m].typ)) {
658
664
  // @ts-ignore
659
665
  if (t.chi[m + 1]?.typ == 'Whitespace') {
660
666
  // @ts-ignore
@@ -36,11 +36,6 @@ function* tokenize(iterator) {
36
36
  }
37
37
  buffer += quoteStr;
38
38
  while (value = peek()) {
39
- // if (ind >= iterator.length) {
40
- //
41
- // yield pushToken(buffer, hasNewLine ? 'Bad-string' : 'Unclosed-string');
42
- // break;
43
- // }
44
39
  if (value == '\\') {
45
40
  const sequence = peek(6);
46
41
  let escapeSequence = '';
@@ -60,9 +55,23 @@ function* tokenize(iterator) {
60
55
  }
61
56
  break;
62
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
+ }
63
72
  // not hex or new line
64
73
  // @ts-ignore
65
- if (i == 1 && !isNewLine(codepoint)) {
74
+ else if (i == 1) {
66
75
  buffer += value + sequence[i];
67
76
  next(2);
68
77
  continue;
@@ -82,13 +91,6 @@ function* tokenize(iterator) {
82
91
  next(escapeSequence.length + 1);
83
92
  continue;
84
93
  }
85
- // buffer += value;
86
- // if (ind >= iterator.length) {
87
- //
88
- // // drop '\\' at the end
89
- // yield pushToken(buffer);
90
- // break;
91
- // }
92
94
  buffer += next(2);
93
95
  continue;
94
96
  }
@@ -98,20 +100,28 @@ function* tokenize(iterator) {
98
100
  next();
99
101
  // i += value.length;
100
102
  buffer = '';
101
- break;
103
+ return;
102
104
  }
103
105
  if (isNewLine(value.charCodeAt(0))) {
104
106
  hasNewLine = true;
105
107
  }
106
108
  if (hasNewLine && value == ';') {
107
- yield pushToken(buffer, 'Bad-string');
109
+ yield pushToken(buffer + value, 'Bad-string');
108
110
  buffer = '';
111
+ next();
109
112
  break;
110
113
  }
111
114
  buffer += value;
112
- // i += value.length;
113
115
  next();
114
116
  }
117
+ if (hasNewLine) {
118
+ yield pushToken(buffer, 'Bad-string');
119
+ }
120
+ else {
121
+ // EOF - 'Unclosed-string' fixed
122
+ yield pushToken(buffer + quote, 'String');
123
+ }
124
+ buffer = '';
115
125
  }
116
126
  function peek(count = 1) {
117
127
  if (count == 1) {
@@ -225,6 +235,11 @@ function* tokenize(iterator) {
225
235
  yield pushToken(buffer);
226
236
  buffer = '';
227
237
  }
238
+ if (peek() == '=') {
239
+ yield pushToken('', 'Lte');
240
+ next();
241
+ break;
242
+ }
228
243
  buffer += value;
229
244
  value = next();
230
245
  if (ind >= iterator.length) {
@@ -293,7 +308,13 @@ function* tokenize(iterator) {
293
308
  yield pushToken(buffer);
294
309
  buffer = '';
295
310
  }
296
- yield pushToken('', 'Gt');
311
+ if (peek() == '=') {
312
+ yield pushToken('', 'Gte');
313
+ next();
314
+ }
315
+ else {
316
+ yield pushToken('', 'Gt');
317
+ }
297
318
  consumeWhiteSpace();
298
319
  break;
299
320
  case '.':
@@ -335,7 +356,7 @@ function* tokenize(iterator) {
335
356
  break;
336
357
  case '(':
337
358
  if (buffer.length == 0) {
338
- yield pushToken('', 'Start-parens');
359
+ yield pushToken(value);
339
360
  break;
340
361
  }
341
362
  buffer += value;
@@ -449,6 +470,7 @@ function* tokenize(iterator) {
449
470
  if (buffer.length > 0) {
450
471
  yield pushToken(buffer);
451
472
  }
473
+ // yield pushToken('', 'EOF');
452
474
  }
453
475
 
454
476
  export { tokenize };
@@ -159,37 +159,30 @@ function isNumber(name) {
159
159
  return true;
160
160
  }
161
161
  function isDimension(name) {
162
- let index = 0;
163
- while (index++ < name.length) {
164
- if (isDigit(name.charCodeAt(name.length - index))) {
165
- index--;
166
- break;
167
- }
168
- if (index == 3) {
169
- break;
162
+ let index = name.length;
163
+ while (index--) {
164
+ if (isLetter(name.charCodeAt(index))) {
165
+ continue;
170
166
  }
167
+ index++;
168
+ break;
171
169
  }
172
- if (index == 0 || index > 3) {
173
- return false;
174
- }
175
- const number = name.slice(0, -index);
176
- return number.length > 0 && isIdentStart(name.charCodeAt(name.length - index)) && isNumber(number);
170
+ const number = name.slice(0, index);
171
+ return number.length > 0 && isIdentStart(name.charCodeAt(index)) && isNumber(number);
177
172
  }
178
173
  function isPercentage(name) {
179
174
  return name.endsWith('%') && isNumber(name.slice(0, -1));
180
175
  }
181
176
  function parseDimension(name) {
182
- let index = 0;
183
- while (index++ < name.length) {
184
- if (isDigit(name.charCodeAt(name.length - index))) {
185
- index--;
186
- break;
187
- }
188
- if (index == 3) {
189
- break;
177
+ let index = name.length;
178
+ while (index--) {
179
+ if (isLetter(name.charCodeAt(index))) {
180
+ continue;
190
181
  }
182
+ index++;
183
+ break;
191
184
  }
192
- const dimension = { typ: 'Dimension', val: name.slice(0, -index), unit: name.slice(-index) };
185
+ const dimension = { typ: 'Dimension', val: name.slice(0, index), unit: name.slice(index) };
193
186
  if (isAngle(dimension)) {
194
187
  // @ts-ignore
195
188
  dimension.typ = 'Angle';
@@ -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