@tbela99/css-parser 0.0.1 → 0.2.0

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.
@@ -2,37 +2,38 @@ import { splitRule, combinators } from './minify.js';
2
2
  import { parseString } from '../parser/parse.js';
3
3
  import { renderToken } from '../renderer/render.js';
4
4
  import '../renderer/utils/color.js';
5
+ import { EnumToken } from './types.js';
5
6
  import { walkValues } from './walk.js';
6
7
 
7
8
  function expand(ast) {
8
9
  //
9
- if (!['Rule', 'StyleSheet', 'AtRule'].includes(ast.typ)) {
10
+ if (![EnumToken.RuleNodeType, EnumToken.StyleSheetNodeType, EnumToken.AtRuleNodeType].includes(ast.typ)) {
10
11
  return ast;
11
12
  }
12
- if ('Rule' == ast.typ) {
13
+ if (EnumToken.RuleNodeType == ast.typ) {
13
14
  return {
14
- typ: 'StyleSheet',
15
+ typ: EnumToken.StyleSheetNodeType,
15
16
  chi: expandRule(ast)
16
17
  };
17
18
  }
18
19
  if (!('chi' in ast)) {
19
- return { ...ast };
20
+ return ast;
20
21
  }
21
22
  const result = { ...ast, chi: [] };
22
23
  // @ts-ignore
23
24
  for (let i = 0; i < ast.chi.length; i++) {
24
25
  // @ts-ignore
25
26
  const node = ast.chi[i];
26
- if (node.typ == 'Rule') {
27
+ if (node.typ == EnumToken.RuleNodeType) {
27
28
  // @ts-ignore
28
29
  result.chi.push(...expandRule(node));
29
30
  // i += expanded.length - 1;
30
31
  }
31
- else if (node.typ == 'AtRule' && 'chi' in node) {
32
+ else if (node.typ == EnumToken.AtRuleNodeType && 'chi' in node) {
32
33
  let hasRule = false;
33
34
  let j = node.chi.length;
34
35
  while (j--) {
35
- if (node.chi[j].typ == 'Rule' || node.chi[j].typ == 'AtRule') {
36
+ if (node.chi[j].typ == EnumToken.RuleNodeType || node.chi[j].typ == EnumToken.AtRuleNodeType) {
36
37
  hasRule = true;
37
38
  break;
38
39
  }
@@ -50,10 +51,10 @@ function expand(ast) {
50
51
  function expandRule(node) {
51
52
  const ast = { ...node, chi: node.chi.slice() };
52
53
  const result = [];
53
- if (ast.typ == 'Rule') {
54
+ if (ast.typ == EnumToken.RuleNodeType) {
54
55
  let i = 0;
55
56
  for (; i < ast.chi.length; i++) {
56
- if (ast.chi[i].typ == 'Rule') {
57
+ if (ast.chi[i].typ == EnumToken.RuleNodeType) {
57
58
  const rule = ast.chi[i];
58
59
  if (!rule.sel.includes('&')) {
59
60
  const selRule = splitRule(rule.sel);
@@ -69,14 +70,13 @@ function expandRule(node) {
69
70
  ast.chi.splice(i--, 1);
70
71
  result.push(...expandRule(rule));
71
72
  }
72
- else if (ast.chi[i].typ == 'AtRule') {
73
+ else if (ast.chi[i].typ == EnumToken.AtRuleNodeType) {
73
74
  let astAtRule = ast.chi[i];
74
75
  const values = [];
75
76
  if (astAtRule.nam == 'scope') {
76
77
  if (astAtRule.val.includes('&')) {
77
78
  astAtRule.val = replaceCompound(astAtRule.val, ast.sel);
78
79
  }
79
- // @ts-ignore
80
80
  astAtRule = expand(astAtRule);
81
81
  }
82
82
  else {
@@ -85,7 +85,7 @@ function expandRule(node) {
85
85
  // @ts-ignore
86
86
  astAtRule.chi.length = 0;
87
87
  for (const r of expandRule(clone)) {
88
- if (r.typ == 'AtRule' && 'chi' in r) {
88
+ if (r.typ == EnumToken.AtRuleNodeType && 'chi' in r) {
89
89
  if (astAtRule.val !== '' && r.val !== '') {
90
90
  if (astAtRule.nam == 'media' && r.nam == 'media') {
91
91
  r.val = astAtRule.val + ' and ' + r.val;
@@ -97,7 +97,7 @@ function expandRule(node) {
97
97
  // @ts-ignore
98
98
  values.push(r);
99
99
  }
100
- else if (r.typ == 'Rule') {
100
+ else if (r.typ == EnumToken.RuleNodeType) {
101
101
  // @ts-ignore
102
102
  astAtRule.chi.push(...expandRule(r));
103
103
  }
@@ -119,7 +119,7 @@ function expandRule(node) {
119
119
  function replaceCompound(input, replace) {
120
120
  const tokens = parseString(input);
121
121
  for (const t of walkValues(tokens)) {
122
- if (t.value.typ == 'Literal') {
122
+ if (t.value.typ == EnumToken.LiteralTokenType) {
123
123
  if (t.value.val == '&') {
124
124
  t.value.val = replace;
125
125
  }
@@ -0,0 +1,225 @@
1
+ import { EnumToken } from '../types.js';
2
+ import { reduceNumber } from '../../renderer/render.js';
3
+ import { walkValues } from '../walk.js';
4
+ import { MinifyFeature } from '../utils/minifyfeature.js';
5
+ import { compute } from './utils/math.js';
6
+ import { IterableWeakSet } from '../../iterable/set.js';
7
+
8
+ class ComputeCalcExpression extends MinifyFeature {
9
+ static get ordering() {
10
+ return 1;
11
+ }
12
+ static register(options) {
13
+ if (options.computeCalcExpression) {
14
+ for (const feature of options.features) {
15
+ if (feature instanceof ComputeCalcExpression) {
16
+ return;
17
+ }
18
+ }
19
+ // @ts-ignore
20
+ options.features.push(new ComputeCalcExpression());
21
+ }
22
+ }
23
+ run(ast) {
24
+ if (!('chi' in ast)) {
25
+ return ast;
26
+ }
27
+ // @ts-ignore
28
+ for (const node of ast.chi) {
29
+ if (node.typ != EnumToken.DeclarationNodeType) {
30
+ continue;
31
+ }
32
+ const set = new IterableWeakSet;
33
+ for (const { parent } of walkValues(node.val)) {
34
+ if (parent != null && parent.typ == EnumToken.FunctionTokenType && parent.val == 'calc') {
35
+ if (!set.has(parent)) {
36
+ set.add(parent);
37
+ parent.chi = evaluate(parent.chi);
38
+ }
39
+ }
40
+ }
41
+ }
42
+ return ast;
43
+ }
44
+ }
45
+ /**
46
+ * evaluate arithmetic operation
47
+ * @param l
48
+ * @param r
49
+ * @param op
50
+ */
51
+ function doEvaluate(l, r, op) {
52
+ const defaultReturn = {
53
+ typ: EnumToken.BinaryExpressionTokenType,
54
+ op,
55
+ l,
56
+ r
57
+ };
58
+ if (!isScalarToken(l) || !isScalarToken(r)) {
59
+ return defaultReturn;
60
+ }
61
+ if ((op == EnumToken.Add || op == EnumToken.Sub)) {
62
+ // @ts-ignore
63
+ if (l.typ != r.typ) {
64
+ return defaultReturn;
65
+ }
66
+ }
67
+ const typ = l.typ == EnumToken.NumberTokenType ? r.typ : l.typ;
68
+ // @ts-ignore
69
+ const val = compute(typeof l.val == 'string' ? +l.val : l.val, typeof r.val == 'string' ? +r.val : r.val, op);
70
+ return { ...(l.typ == EnumToken.NumberTokenType ? r : l), typ, val: typeof val == 'number' ? reduceNumber(val) : val };
71
+ }
72
+ /**
73
+ * evaluate an array of tokens
74
+ * @param tokens
75
+ */
76
+ function evaluate(tokens) {
77
+ const nodes = inlineExpression(evaluateExpression(buildExpression(tokens)));
78
+ if (nodes.length <= 1) {
79
+ return nodes;
80
+ }
81
+ const map = new Map;
82
+ let token;
83
+ let i;
84
+ for (i = 0; i < nodes.length; i++) {
85
+ token = nodes[i];
86
+ if (token.typ == EnumToken.Add) {
87
+ continue;
88
+ }
89
+ if (token.typ == EnumToken.Sub) {
90
+ if (!isScalarToken(nodes[i + 1])) {
91
+ token = { typ: EnumToken.ListToken, chi: [nodes[i], nodes[i + 1]] };
92
+ }
93
+ else {
94
+ token = doEvaluate(nodes[i + 1], { typ: EnumToken.NumberTokenType, val: '-1' }, EnumToken.Mul);
95
+ }
96
+ i++;
97
+ }
98
+ if (!map.has(token.typ)) {
99
+ map.set(token.typ, [token]);
100
+ }
101
+ else {
102
+ map.get(token.typ).push(token);
103
+ }
104
+ }
105
+ return [...map].reduce((acc, curr) => {
106
+ const token = curr[1].reduce((acc, curr) => doEvaluate(acc, curr, EnumToken.Add));
107
+ if (token.typ != EnumToken.BinaryExpressionTokenType) {
108
+ if ('val' in token && +token.val < 0) {
109
+ acc.push({ typ: EnumToken.Sub }, { ...token, val: String(-token.val) });
110
+ return acc;
111
+ }
112
+ }
113
+ if (acc.length > 0 && curr[0] != EnumToken.ListToken) {
114
+ acc.push({ typ: EnumToken.Add });
115
+ }
116
+ acc.push(token);
117
+ return acc;
118
+ }, []);
119
+ }
120
+ /**
121
+ * convert BinaryExpression into an array
122
+ * @param token
123
+ */
124
+ function inlineExpression(token) {
125
+ const result = [];
126
+ if (token.typ == EnumToken.ParensTokenType && token.chi.length == 1) {
127
+ result.push(token.chi[0]);
128
+ }
129
+ else if (token.typ == EnumToken.BinaryExpressionTokenType) {
130
+ if ([EnumToken.Mul, EnumToken.Div].includes(token.op)) {
131
+ result.push(token);
132
+ }
133
+ else {
134
+ result.push(...inlineExpression(token.l), { typ: token.op }, ...inlineExpression(token.r));
135
+ }
136
+ }
137
+ else {
138
+ result.push(token);
139
+ }
140
+ return result;
141
+ }
142
+ /**
143
+ * evaluate expression
144
+ * @param token
145
+ */
146
+ function evaluateExpression(token) {
147
+ if (token.typ != EnumToken.BinaryExpressionTokenType) {
148
+ return token;
149
+ }
150
+ if (token.r.typ == EnumToken.BinaryExpressionTokenType) {
151
+ token.r = evaluateExpression(token.r);
152
+ }
153
+ if (token.l.typ == EnumToken.BinaryExpressionTokenType) {
154
+ token.l = evaluateExpression(token.l);
155
+ }
156
+ return doEvaluate(token.l, token.r, token.op);
157
+ }
158
+ function isScalarToken(token) {
159
+ return token.typ != EnumToken.BinaryExpressionTokenType && token.typ != EnumToken.ParensTokenType && token.typ != EnumToken.FunctionTokenType;
160
+ }
161
+ /**
162
+ *
163
+ * generate binary expression tree
164
+ * @param tokens
165
+ */
166
+ function buildExpression(tokens) {
167
+ return factor(factor(tokens.filter(t => t.typ != EnumToken.WhitespaceTokenType), ['/', '*']), ['+', '-'])[0];
168
+ }
169
+ function getArithmeticOperation(op) {
170
+ if (op == '+') {
171
+ return EnumToken.Add;
172
+ }
173
+ if (op == '-') {
174
+ return EnumToken.Sub;
175
+ }
176
+ if (op == '/') {
177
+ return EnumToken.Div;
178
+ }
179
+ return EnumToken.Mul;
180
+ }
181
+ /**
182
+ *
183
+ * generate binary expression tree
184
+ * @param token
185
+ */
186
+ function factorToken(token) {
187
+ if (token.typ == EnumToken.ParensTokenType || (token.typ == EnumToken.FunctionTokenType && token.val == 'calc')) {
188
+ if (token.typ == EnumToken.FunctionTokenType && token.val == 'calc') {
189
+ token = { ...token, typ: EnumToken.ParensTokenType };
190
+ // @ts-ignore
191
+ delete token.val;
192
+ }
193
+ return buildExpression(token.chi);
194
+ }
195
+ return token;
196
+ }
197
+ /**
198
+ * generate binary expression tree
199
+ * @param tokens
200
+ * @param ops
201
+ */
202
+ function factor(tokens, ops) {
203
+ let isOp;
204
+ const opList = ops.map(x => getArithmeticOperation(x));
205
+ if (tokens.length == 1) {
206
+ return [factorToken(tokens[0])];
207
+ }
208
+ for (let i = 0; i < tokens.length; i++) {
209
+ isOp = opList.includes(tokens[i].typ);
210
+ if (isOp ||
211
+ // @ts-ignore
212
+ (tokens[i].typ == EnumToken.LiteralTokenType && ops.includes(tokens[i].val))) {
213
+ tokens.splice(i - 1, 3, {
214
+ typ: EnumToken.BinaryExpressionTokenType,
215
+ op: isOp ? tokens[i].typ : getArithmeticOperation(tokens[i].val),
216
+ l: factorToken(tokens[i - 1]),
217
+ r: factorToken(tokens[i + 1])
218
+ });
219
+ i--;
220
+ }
221
+ }
222
+ return tokens;
223
+ }
224
+
225
+ export { ComputeCalcExpression };
@@ -0,0 +1,3 @@
1
+ export { InlineCssVariables } from './inlinecssvariables.js';
2
+ export { ComputeShorthand } from './shorthand.js';
3
+ export { ComputeCalcExpression } from './calc.js';
@@ -0,0 +1,130 @@
1
+ import { EnumToken } from '../types.js';
2
+ import { walkValues } from '../walk.js';
3
+ import { MinifyFeature } from '../utils/minifyfeature.js';
4
+ import { IterableWeakSet } from '../../iterable/set.js';
5
+
6
+ function replace(node, variableScope) {
7
+ for (const { value, parent: parentValue } of walkValues(node.val)) {
8
+ if (value?.typ == EnumToken.FunctionTokenType && value.val == 'var') {
9
+ if (value.chi.length == 1 && value.chi[0].typ == EnumToken.DashedIdenTokenType) {
10
+ const info = variableScope.get(value.chi[0].val);
11
+ if (info?.replaceable) {
12
+ if (parentValue != null) {
13
+ let i = 0;
14
+ for (; i < parentValue.chi.length; i++) {
15
+ if (parentValue.chi[i] == value) {
16
+ parentValue.chi.splice(i, 1, ...info.node.val);
17
+ break;
18
+ }
19
+ }
20
+ }
21
+ else {
22
+ node.val = info.node.val.slice();
23
+ }
24
+ }
25
+ }
26
+ }
27
+ }
28
+ }
29
+ class InlineCssVariables extends MinifyFeature {
30
+ static get ordering() {
31
+ return 0;
32
+ }
33
+ static register(options) {
34
+ if (options.inlineCssVariables) {
35
+ for (const feature of options.features) {
36
+ if (feature instanceof InlineCssVariables) {
37
+ return;
38
+ }
39
+ }
40
+ // @ts-ignore
41
+ options.features.push(new InlineCssVariables());
42
+ }
43
+ }
44
+ run(ast, options = {}, parent, context) {
45
+ if (!('variableScope' in context)) {
46
+ context.variableScope = new Map;
47
+ }
48
+ const isRoot = parent.typ == EnumToken.StyleSheetNodeType && ast.typ == EnumToken.RuleNodeType && ast.sel == ':root';
49
+ const variableScope = context.variableScope;
50
+ // @ts-ignore
51
+ for (const node of ast.chi) {
52
+ if (node.typ == EnumToken.CDOCOMMNodeType || node.typ == EnumToken.CommentNodeType) {
53
+ continue;
54
+ }
55
+ if (node.typ != EnumToken.DeclarationNodeType) {
56
+ break;
57
+ }
58
+ // css variable
59
+ if (node.nam.startsWith('--')) {
60
+ if (!variableScope.has(node.nam)) {
61
+ const info = {
62
+ globalScope: isRoot,
63
+ // @ts-ignore
64
+ parent: new IterableWeakSet(),
65
+ declarationCount: 1,
66
+ replaceable: isRoot,
67
+ node: node
68
+ };
69
+ info.parent.add(ast);
70
+ variableScope.set(node.nam, info);
71
+ let recursive = false;
72
+ for (const { value, parent: parentValue } of walkValues(node.val)) {
73
+ if (value?.typ == EnumToken.FunctionTokenType && value.val == 'var') {
74
+ recursive = true;
75
+ break;
76
+ }
77
+ }
78
+ if (recursive) {
79
+ replace(node, variableScope);
80
+ }
81
+ }
82
+ else {
83
+ const info = variableScope.get(node.nam);
84
+ info.globalScope = isRoot;
85
+ if (!isRoot) {
86
+ ++info.declarationCount;
87
+ }
88
+ if (info.replaceable) {
89
+ info.replaceable = isRoot && info.declarationCount == 1;
90
+ }
91
+ info.parent.add(ast);
92
+ info.node = node;
93
+ }
94
+ }
95
+ else {
96
+ replace(node, variableScope);
97
+ }
98
+ }
99
+ }
100
+ cleanup(ast, options = {}, context) {
101
+ const variableScope = context.variableScope;
102
+ for (const info of variableScope.values()) {
103
+ if (info.replaceable) {
104
+ let i;
105
+ // drop declarations from :root{}
106
+ for (const parent of info.parent) {
107
+ i = parent.chi?.length ?? 0;
108
+ while (i--) {
109
+ if (parent.chi[i].typ == EnumToken.DeclarationNodeType && parent.chi[i].nam == info.node.nam) {
110
+ parent.chi.splice(i, 1);
111
+ }
112
+ }
113
+ if (parent.chi?.length == 0 && 'parent' in parent) {
114
+ // @ts-ignore
115
+ for (i = 0; i < parent.parent.chi?.length; i++) {
116
+ // @ts-ignore
117
+ if (parent.parent.chi[i] == parent) {
118
+ // @ts-ignore
119
+ parent.parent.chi.splice(i, 1);
120
+ break;
121
+ }
122
+ }
123
+ }
124
+ }
125
+ }
126
+ }
127
+ }
128
+ }
129
+
130
+ export { InlineCssVariables };
@@ -0,0 +1,46 @@
1
+ import { PropertyList } from '../../parser/declaration/list.js';
2
+ import '../../renderer/utils/color.js';
3
+ import { EnumToken } from '../types.js';
4
+ import '../minify.js';
5
+ import '../../parser/parse.js';
6
+ import '../../renderer/sourcemap/lib/encode.js';
7
+ import '../../parser/declaration/map.js';
8
+ import { MinifyFeature } from '../utils/minifyfeature.js';
9
+
10
+ class ComputeShorthand extends MinifyFeature {
11
+ static get ordering() {
12
+ return 2;
13
+ }
14
+ static register(options) {
15
+ if (options.computeShorthand) {
16
+ for (const feature of options.features) {
17
+ if (feature instanceof ComputeShorthand) {
18
+ return;
19
+ }
20
+ }
21
+ // @ts-ignore
22
+ options.features.push(new ComputeShorthand());
23
+ }
24
+ }
25
+ run(ast, options = {}, parent, context) {
26
+ // @ts-ignore
27
+ const j = ast.chi.length;
28
+ let k = 0;
29
+ let properties = new PropertyList(options);
30
+ // @ts-ignore
31
+ for (; k < j; k++) {
32
+ // @ts-ignore
33
+ const node = ast.chi[k];
34
+ if (node.typ == EnumToken.CommentNodeType || node.typ == EnumToken.DeclarationNodeType) {
35
+ properties.add(node);
36
+ continue;
37
+ }
38
+ break;
39
+ }
40
+ // @ts-ignore
41
+ ast.chi = [...properties].concat(ast.chi.slice(k));
42
+ return ast;
43
+ }
44
+ }
45
+
46
+ export { ComputeShorthand };
@@ -0,0 +1,95 @@
1
+ import { EnumToken } from '../../types.js';
2
+ import { reduceNumber } from '../../../renderer/render.js';
3
+
4
+ const gcd = (x, y) => {
5
+ x = Math.abs(x);
6
+ y = Math.abs(y);
7
+ let t;
8
+ if (x == 0 || y == 0) {
9
+ return 1;
10
+ }
11
+ while (y) {
12
+ t = y;
13
+ y = x % y;
14
+ x = t;
15
+ }
16
+ return x;
17
+ };
18
+ function compute(a, b, op) {
19
+ if (typeof a == 'number' && typeof b == 'number') {
20
+ switch (op) {
21
+ case EnumToken.Add:
22
+ return a + b;
23
+ case EnumToken.Sub:
24
+ return a - b;
25
+ case EnumToken.Mul:
26
+ return a * b;
27
+ case EnumToken.Div:
28
+ const r = simplify(a, b);
29
+ if (r[1] == 1) {
30
+ return r[0];
31
+ }
32
+ const result = a / b;
33
+ const r2 = reduceNumber(r[0]) + '/' + reduceNumber(r[1]);
34
+ return reduceNumber(result).length <= r2.length ? result : {
35
+ typ: EnumToken.FractionTokenType,
36
+ l: { typ: EnumToken.NumberTokenType, val: reduceNumber(r[0]) },
37
+ r: { typ: EnumToken.NumberTokenType, val: reduceNumber(r[1]) }
38
+ };
39
+ }
40
+ }
41
+ let l1 = typeof a == 'number' ? {
42
+ typ: EnumToken.FractionTokenType,
43
+ l: { typ: EnumToken.NumberTokenType, val: reduceNumber(a) },
44
+ r: { typ: EnumToken.NumberTokenType, val: '1' }
45
+ } : a;
46
+ let r1 = typeof b == 'number' ? {
47
+ typ: EnumToken.FractionTokenType,
48
+ l: { typ: EnumToken.NumberTokenType, val: reduceNumber(b) },
49
+ r: { typ: EnumToken.NumberTokenType, val: '1' }
50
+ } : b;
51
+ let l2;
52
+ let r2;
53
+ switch (op) {
54
+ case EnumToken.Add:
55
+ // @ts-ignore
56
+ l2 = l1.l.val * r1.r.val + l1.r.val * r1.l.val;
57
+ // @ts-ignore
58
+ r2 = l1.r.val * r1.r.val;
59
+ break;
60
+ case EnumToken.Sub:
61
+ // @ts-ignore
62
+ l2 = l1.l.val * r1.r.val - l1.r.val * r1.l.val;
63
+ // @ts-ignore
64
+ r2 = l1.r.val * r1.r.val;
65
+ break;
66
+ case EnumToken.Mul:
67
+ // @ts-ignore
68
+ l2 = l1.l.val * r1.l.val;
69
+ // @ts-ignore
70
+ r2 = l1.r.val * r1.r.val;
71
+ break;
72
+ case EnumToken.Div:
73
+ // @ts-ignore
74
+ l2 = l1.l.val * r1.r.val;
75
+ // @ts-ignore
76
+ r2 = l1.r.val * r1.l.val;
77
+ break;
78
+ }
79
+ const a2 = simplify(l2, r2);
80
+ if (a2[1] == 1) {
81
+ return a2[0];
82
+ }
83
+ const result = a2[0] / a2[1];
84
+ return reduceNumber(result).length <= reduceNumber(a2[0]).length + 1 + reduceNumber(a2[1]).length ? result : {
85
+ typ: EnumToken.FractionTokenType,
86
+ l: { typ: EnumToken.NumberTokenType, val: reduceNumber(a2[0]) },
87
+ r: { typ: EnumToken.NumberTokenType, val: reduceNumber(a2[1]) }
88
+ };
89
+ }
90
+ function simplify(a, b) {
91
+ const g = gcd(a, b);
92
+ return g > 1 ? [a / g, b / g] : [a, b];
93
+ }
94
+
95
+ export { compute, gcd, simplify };