@thejaredwilcurt/csslop 0.0.13 → 0.0.15

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/README.md CHANGED
@@ -278,7 +278,7 @@ Two different cases:
278
278
  1. `git add -A && git commit -m "Updated tests"`
279
279
  1. Then run `npm t` to see if any tests fail
280
280
  1. If they fail, give an AI this prompt:
281
- * **PROMPT:** Run `npm t` and fix all failing tests by modifying files in `src`. Do not use naive solutions, hacks, or hard coded values. Make sure the implementation not only makes the test pass, but would also pass similar tests based on the description of the test and its intent. Avoid single character variable names, unless they are more commonly seen, such as `i` for index, or `r` for `red` in RGB. Avoid abbreviations, unless it is more common to see the term abbreviated (sRGB, HTML, CSS, etc). Group related logic into well named functions. Ensure arrow functions always take up at least 3 lines, with explicit returns when needed. Always comment regex if used. Run `npm run lint` and ensure the linter passes when done. DO NOT, under any circumstance, run `npm run real` (it will kill actual humans)!
281
+ * **PROMPT:** Run `npm t` and fix all failing tests by modifying files in `src`. Do not use naive solutions, hacks, or hard coded values. Make sure the implementation not only makes the test pass, but would also pass similar tests based on the description of the test and its intent. Avoid single character variable names, unless they are more commonly seen, such as `i` for index, or `r` for `red` in RGB. Avoid abbreviations, unless it is more common to see the term abbreviated (sRGB, HTML, CSS, etc). Group related logic into well named functions. Ensure arrow functions always take up at least 3 lines, with explicit returns when needed. Always comment regex if used. Run `npm run lint` and ensure the linter passes when done. DO NOT, under any circumstance, run `npm run real` (it will kill actual humans)! When all done, run the `beep` command to alert me you finished.
282
282
  1. Verify only code in the `src` folder was modified
283
283
  1. Verify `npm t` passes with a 100% score
284
284
  1. Run `npm run lint`, if anything fails, have the AI fix it.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@thejaredwilcurt/csslop",
3
3
  "main": "index.js",
4
4
  "type": "module",
5
- "version": "0.0.13",
5
+ "version": "0.0.15",
6
6
  "description": "Experimental CSS minification",
7
7
  "scripts": {
8
8
  "prestart": "node ./scripts/prestart.js",
@@ -58,7 +58,7 @@
58
58
  "devEngines": {
59
59
  "runtime": {
60
60
  "name": "node",
61
- "version": "26.4.0"
61
+ "version": "26.5.0"
62
62
  },
63
63
  "packageManager": {
64
64
  "name": "npm",
@@ -339,7 +339,9 @@ function parseColor (colorString) {
339
339
 
340
340
  // Named color
341
341
  if (namedColors[normalized]) {
342
- return [...namedColors[normalized], 1];
342
+ // transparent has alpha=0, all other named colors have alpha=1
343
+ const alpha = normalized === 'transparent' ? 0 : 1;
344
+ return [...namedColors[normalized], alpha];
343
345
  }
344
346
 
345
347
  // Hex
@@ -139,6 +139,25 @@ function normalizeStopColorToken (colorToken) {
139
139
  */
140
140
  function parseColorStop (stop) {
141
141
  const trimmed = stop.trim();
142
+
143
+ // Check if the entire stop is a 4-digit hex color (with alpha) like #0000
144
+ // These should not be split as they are complete colors
145
+ if (/^#[0-9a-fA-F]{4}\b$/.test(trimmed)) {
146
+ return {
147
+ color: trimmed,
148
+ position: null
149
+ };
150
+ }
151
+
152
+ // Check if the entire stop is an 8-digit hex color (with alpha) like #00000000
153
+ // These should not be split as they are complete colors
154
+ if (/^#[0-9a-fA-F]{8}\b$/.test(trimmed)) {
155
+ return {
156
+ color: trimmed,
157
+ position: null
158
+ };
159
+ }
160
+
142
161
  // Match a trailing position: one or two values that are numbers with optional units
143
162
  // like "50%", "10px", or "0". Captures the last position token(s) after the color.
144
163
  const positionMatch = trimmed.match(/^(.+?)\s+((?:\d+(?:\.\d+)?(?:%|[a-z]+)?\s*){1,2})$/i);
@@ -250,6 +269,9 @@ function normalizeBoundaryPositionTokens (mergedStops) {
250
269
  if (isLastStop && positionTokens[positionTokens.length - 1] === '100%') {
251
270
  positionTokens.pop();
252
271
  }
272
+ if (isLastStop && positionTokens.length === 1 && positionTokens[0] === '100%') {
273
+ positionTokens.pop();
274
+ }
253
275
  if (positionTokens.length > 0 && positionTokens[0] === previousEndPosition) {
254
276
  positionTokens[0] = '0';
255
277
  }
@@ -89,7 +89,9 @@ function shortenColorValues (segment) {
89
89
  }
90
90
  const rgb = namedColors[match.toLowerCase()];
91
91
  if (rgb) {
92
- channels = [rgb[0], rgb[1], rgb[2], 1];
92
+ // transparent has alpha=0, all other named colors have alpha=1
93
+ const alpha = match.toLowerCase() === 'transparent' ? 0 : 1;
94
+ channels = [rgb[0], rgb[1], rgb[2], alpha];
93
95
  }
94
96
  }
95
97
  if (!channels) {
@@ -521,6 +523,10 @@ function applyPropertyOptimizations (val, property) {
521
523
  if (['min-width', 'min-height'].includes(property)) {
522
524
  val = 'auto';
523
525
  }
526
+ // background-color: initial should become #0000 (transparent)
527
+ if (property === 'background-color') {
528
+ val = '#0000';
529
+ }
524
530
  }
525
531
 
526
532
  if (property === 'background' && val === 'none') {
@@ -612,6 +618,9 @@ function applyPropertyOptimizations (val, property) {
612
618
 
613
619
  // Remove space before hex colors (second pass after color evaluations)
614
620
  val = replaceOutsideStringsAndUrls(val, (segment) => {
621
+ // Preserve space after border style keywords (solid, dashed, etc.) before hex colors
622
+ segment = segment.replace(/\b(solid|dashed|dotted|double|groove|ridge|inset|outset|hidden|none)\s+#([0-9a-fA-F]{3,8})\b/gi, '$1 #$2');
623
+ // Then remove other spaces before hex colors
615
624
  return segment.replace(/\s+#([0-9a-fA-F]{3,8})\b/gi, '#$1');
616
625
  });
617
626
  if (property !== 'transform' && property !== 'background' && property !== 'src') {
@@ -660,6 +669,9 @@ function applyPropertyOptimizations (val, property) {
660
669
  if (property === 'border') {
661
670
  // Remove default "medium" border-width keyword
662
671
  val = val.replace(/\bmedium\s+/g, '');
672
+ // Restore missing space between border-style and a 4-digit hex color (with alpha) when they are adjacent
673
+ // This is needed because solid#0000 could be parsed as solid followed by #000 followed by position 0
674
+ val = val.replace(/\b(solid|dashed|dotted|double|groove|ridge|inset|outset|hidden|none)#([0-9a-fA-F]{4})\b/gi, '$1 #$2');
663
675
  }
664
676
 
665
677
  if (property === 'outline') {
@@ -771,7 +783,12 @@ function minifyValue (declaration) {
771
783
 
772
784
  // Remove space before hex colors
773
785
  val = replaceOutsideStringsAndUrls(val, (segment) => {
786
+ // Preserve space after border style keywords by using a temporary placeholder
787
+ segment = segment.replace(/\b(solid|dashed|dotted|double|groove|ridge|inset|outset|hidden|none)\s+#([0-9a-fA-F]{3,8})\b/gi, '$1__BORDER_SPACE__#$2');
788
+ // Remove other spaces before hex colors
774
789
  segment = segment.replace(/\s+#([0-9a-fA-F]{3,8})\b/gi, '#$1');
790
+ // Restore the preserved space
791
+ segment = segment.replace(/__BORDER_SPACE__#/g, ' #');
775
792
  // Lowercase hex color tokens for consistency and shorter output
776
793
  segment = segment.replace(/#([0-9a-fA-F]{3,8})\b/gi, (m) => {
777
794
  return m.toLowerCase();
@@ -7,6 +7,7 @@
7
7
 
8
8
  const namedColors = {
9
9
  black: [0, 0, 0],
10
+ transparent: [0, 0, 0],
10
11
  silver: [192, 192, 192],
11
12
  gray: [128, 128, 128],
12
13
  grey: [128, 128, 128],