@thejaredwilcurt/csslop 0.0.38 → 0.0.40

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,6 +2,11 @@
2
2
  * @file Minifies CSS transform values by simplifying individual transform functions, collapsing axes, and removing identity components.
3
3
  */
4
4
 
5
+ import {
6
+ findMatchingParenthesis,
7
+ splitTopLevelCommaList
8
+ } from '../parser/source-search.js';
9
+
5
10
  import {
6
11
  normalizeScaleComponent,
7
12
  roundCompactNumber
@@ -17,41 +22,11 @@ function isZeroTransformValue (value) {
17
22
  return /^[-+]?0(?:[a-z%]+)?$/i.test(value.trim());
18
23
  }
19
24
 
20
- /**
21
- * Splits a CSS function's comma-separated argument string into an array, correctly handling nested parentheses.
22
- *
23
- * @param {string} value The raw arguments string inside the function parentheses.
24
- * @return {Array} An array of trimmed argument strings.
25
- */
26
- function splitFunctionArguments (value) {
27
- const parts = [];
28
- let depth = 0;
29
- let current = '';
30
- for (const character of value) {
31
- if (character === '(') {
32
- depth++;
33
- }
34
- if (character === ')') {
35
- depth--;
36
- }
37
- if (character === ',' && depth === 0) {
38
- parts.push(current.trim());
39
- current = '';
40
- } else {
41
- current += character;
42
- }
43
- }
44
- if (current.trim() || parts.length) {
45
- parts.push(current.trim());
46
- }
47
- return parts;
48
- }
49
-
50
25
  /**
51
26
  * Parses a CSS transform value string into an array of objects, each containing a function name and its raw arguments string.
52
27
  *
53
28
  * @param {string} value The full CSS transform value string.
54
- * @return {Array|null} An array of { name, args } objects, or null if the string cannot be parsed.
29
+ * @return {Array|null} An array of { name, arguments } objects, or null if the string cannot be parsed.
55
30
  */
56
31
  function splitTransformFunctions (value) {
57
32
  const parts = [];
@@ -72,23 +47,15 @@ function splitTransformFunctions (value) {
72
47
  if (nameStart === position || value[position] !== '(') {
73
48
  return null;
74
49
  }
75
- const name = value.slice(nameStart, position);
76
- let depth = 1;
77
- let end = position + 1;
78
- while (end < value.length && depth > 0) {
79
- if (value[end] === '(') {
80
- depth++;
81
- }
82
- if (value[end] === ')') {
83
- depth--;
84
- }
85
- end++;
86
- }
87
- if (depth !== 0) {
50
+ const closeIndex = findMatchingParenthesis(value, position);
51
+ if (closeIndex === -1) {
88
52
  return null;
89
53
  }
90
- parts.push({ name, args: value.slice(position + 1, end - 1) });
91
- position = end;
54
+ parts.push({
55
+ name: value.slice(nameStart, position),
56
+ argumentText: value.slice(position + 1, closeIndex)
57
+ });
58
+ position = closeIndex + 1;
92
59
  }
93
60
  return parts;
94
61
  }
@@ -96,13 +63,13 @@ function splitTransformFunctions (value) {
96
63
  /**
97
64
  * Minifies a single CSS transform function by simplifying 3D functions to 2D equivalents, collapsing redundant axes, and normalizing scale percentages.
98
65
  *
99
- * @param {string} name The transform function name (e.g. "translate3d", "scale").
100
- * @param {string} args The raw comma-separated arguments string.
101
- * @return {string} The minified transform function call string.
66
+ * @param {string} name The transform function name (e.g. "translate3d", "scale").
67
+ * @param {string} argumentText The raw comma-separated arguments string.
68
+ * @return {string} The minified transform function call string.
102
69
  */
103
- function minifyTransformFunction (name, args) {
70
+ function minifyTransformFunction (name, argumentText) {
104
71
  const lowerName = name.toLowerCase();
105
- const parts = splitFunctionArguments(args);
72
+ const parts = splitTopLevelCommaList(argumentText);
106
73
 
107
74
  if (lowerName === 'rotate' && parts.length === 1) {
108
75
  // Convert turn units to degrees (e.g. 0.5turn → 180deg)
@@ -214,8 +181,8 @@ function minifyTransformValue (value) {
214
181
  if (!functions) {
215
182
  return value;
216
183
  }
217
- return functions.map(({ name, args }) => {
218
- return minifyTransformFunction(name, args);
184
+ return functions.map(({ name, argumentText }) => {
185
+ return minifyTransformFunction(name, argumentText);
219
186
  }).join(' ');
220
187
  }
221
188
 
@@ -1,144 +0,0 @@
1
- /**
2
- * @file Provides syntax-aware CSS string scanning utilities.
3
- */
4
-
5
- /**
6
- * Finds the closing parenthesis for an opening parenthesis while respecting
7
- * nested parentheses and quoted strings.
8
- *
9
- * @param {string} value The CSS text being scanned.
10
- * @param {number} openParenIndex The index of the opening `(` character.
11
- * @return {number} The closing `)` index, or -1 if unmatched.
12
- */
13
- function findMatchingParenthesis (value, openParenIndex) {
14
- let depth = 1;
15
- let index = openParenIndex + 1;
16
- let activeQuote = '';
17
-
18
- while (index < value.length) {
19
- const character = value[index];
20
- if (activeQuote) {
21
- if (character === '\\') {
22
- index += 2;
23
- continue;
24
- }
25
- if (character === activeQuote) {
26
- activeQuote = '';
27
- }
28
- index++;
29
- continue;
30
- }
31
-
32
- // An escaped character is data rather than a delimiter, even outside a
33
- // quoted string, as in the `\)` of an unquoted url()
34
- if (character === '\\') {
35
- index += 2;
36
- continue;
37
- }
38
-
39
- if (character === '"' || character === '\'') {
40
- activeQuote = character;
41
- index++;
42
- continue;
43
- }
44
-
45
- if (character === '(') {
46
- depth++;
47
- index++;
48
- continue;
49
- }
50
-
51
- if (character === ')') {
52
- depth--;
53
- if (depth === 0) {
54
- return index;
55
- }
56
- }
57
- index++;
58
- }
59
-
60
- return -1;
61
- }
62
-
63
- /**
64
- * Splits a CSS value into its top-level components, keeping parenthesized
65
- * function arguments and quoted strings intact. Components are separated by
66
- * whitespace, or by a `#`, which always starts a hash token and therefore ends
67
- * any component already in progress.
68
- *
69
- * For example, `rgb(0 0 0) red` yields `["rgb(0 0 0)", "red"]`, and the
70
- * minified `red#00f` yields `["red", "#00f"]`.
71
- *
72
- * @param {string} value The CSS value to split.
73
- * @return {Array} The top-level components of the value.
74
- */
75
- function splitTopLevelComponents (value) {
76
- const components = [];
77
- let current = '';
78
- let depth = 0;
79
- let activeQuote = '';
80
- let index = 0;
81
-
82
- while (index < value.length) {
83
- const character = value[index];
84
-
85
- if (activeQuote) {
86
- current += character;
87
- if (character === '\\') {
88
- current += value[index + 1] ?? '';
89
- index += 2;
90
- continue;
91
- }
92
- if (character === activeQuote) {
93
- activeQuote = '';
94
- }
95
- index++;
96
- continue;
97
- }
98
-
99
- if (character === '"' || character === '\'') {
100
- activeQuote = character;
101
- current += character;
102
- index++;
103
- continue;
104
- }
105
-
106
- if (character === '(') {
107
- depth++;
108
- }
109
- if (character === ')' && depth > 0) {
110
- depth--;
111
- }
112
-
113
- // Match any whitespace character, which separates components at depth zero
114
- const isSeparator = depth === 0 && /\s/.test(character);
115
- if (isSeparator) {
116
- if (current) {
117
- components.push(current);
118
- current = '';
119
- }
120
- index++;
121
- continue;
122
- }
123
-
124
- const startsHashToken = character === '#' && depth === 0 && current !== '';
125
- if (startsHashToken) {
126
- components.push(current);
127
- current = '';
128
- }
129
-
130
- current += character;
131
- index++;
132
- }
133
-
134
- if (current) {
135
- components.push(current);
136
- }
137
-
138
- return components;
139
- }
140
-
141
- export {
142
- findMatchingParenthesis,
143
- splitTopLevelComponents
144
- };