@thejaredwilcurt/csslop 0.0.45 → 0.0.46
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/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.
|
|
5
|
+
"version": "0.0.46",
|
|
6
6
|
"description": "Experimental CSS minification",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"prestart": "node ./scripts/pre/start.js",
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
"proto": "proto pin node latest --resolve --tool-native && proto pin npm latest --resolve --tool-native && git status"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@codemirror/autocomplete": "^6.20.
|
|
26
|
+
"@codemirror/autocomplete": "^6.20.4",
|
|
27
27
|
"@codemirror/lang-css": "^6.3.1",
|
|
28
|
-
"@codemirror/view": "^6.43.
|
|
28
|
+
"@codemirror/view": "^6.43.15",
|
|
29
29
|
"@eslint/js": "^10.0.1",
|
|
30
30
|
"@node-projects/css-parser": "^5.2.0",
|
|
31
31
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"eslint-config-tjw-import-x": "^1.0.1",
|
|
36
36
|
"eslint-config-tjw-jsdoc": "^2.0.1",
|
|
37
37
|
"eslint-plugin-import-x": "^4.17.0",
|
|
38
|
-
"eslint-plugin-jsdoc": "^65.
|
|
38
|
+
"eslint-plugin-jsdoc": "^65.4.0",
|
|
39
39
|
"fflate": "^0.8.3",
|
|
40
40
|
"globals": "^17.13.0",
|
|
41
41
|
"pretty-ms": "^10.0.0",
|
|
@@ -162,8 +162,8 @@ function startsUrlToken (source, index) {
|
|
|
162
162
|
|
|
163
163
|
/**
|
|
164
164
|
* Finds the first of several delimiter characters that is written at the top
|
|
165
|
-
* level of the text, meaning it is not escaped, quoted,
|
|
166
|
-
* group.
|
|
165
|
+
* level of the text, meaning it is not escaped, quoted, inside a comment, or
|
|
166
|
+
* nested inside a group.
|
|
167
167
|
*
|
|
168
168
|
* @param {string} source The raw CSS text being scanned.
|
|
169
169
|
* @param {string} delimiters The delimiter characters to look for.
|
|
@@ -174,6 +174,11 @@ function findTopLevelDelimiter (source, delimiters, startIndex) {
|
|
|
174
174
|
let index = startIndex;
|
|
175
175
|
while (index < source.length) {
|
|
176
176
|
const character = source[index];
|
|
177
|
+
if (character === '/' && source[index + 1] === '*') {
|
|
178
|
+
const closingIndex = source.indexOf('*/', index + 2);
|
|
179
|
+
index = closingIndex === -1 ? source.length : closingIndex + 2;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
177
182
|
if (character === '\\') {
|
|
178
183
|
index += 2;
|
|
179
184
|
continue;
|
package/src/rules/optimize.js
CHANGED
|
@@ -262,6 +262,9 @@ function createNestedSelectorMatcher (parentSelector) {
|
|
|
262
262
|
|
|
263
263
|
return (childSelector) => {
|
|
264
264
|
const child = childSelector.trim();
|
|
265
|
+
if (child === parent) {
|
|
266
|
+
return '&';
|
|
267
|
+
}
|
|
265
268
|
if (child.startsWith(parent + ':') || child.startsWith(parent + '::')) {
|
|
266
269
|
return '&' + child.slice(parent.length);
|
|
267
270
|
}
|
|
@@ -497,7 +500,7 @@ function factorCommonParents (rules) {
|
|
|
497
500
|
while (index < rules.length) {
|
|
498
501
|
const rule = rules[index];
|
|
499
502
|
if (rule.type === 'rule' && rule.selectors?.length > 1) {
|
|
500
|
-
const candidateParent = extractLeadingCompound(rule.selectors[0]);
|
|
503
|
+
const candidateParent = extractLeadingCompound(rule.selectors[0]) || rule.selectors[0];
|
|
501
504
|
if (candidateParent) {
|
|
502
505
|
const matchNestedSelector = createNestedSelectorMatcher(candidateParent);
|
|
503
506
|
const childSelectors = rule.selectors.map(matchNestedSelector);
|
|
@@ -571,8 +574,8 @@ function factorCommonParents (rules) {
|
|
|
571
574
|
}
|
|
572
575
|
|
|
573
576
|
/**
|
|
574
|
-
* Shares a descendant prefix through `:is()` when
|
|
575
|
-
*
|
|
577
|
+
* Shares a descendant prefix through `:is()` when a nesting wrapper did not
|
|
578
|
+
* save enough space to factor the parent out of the selector list.
|
|
576
579
|
*
|
|
577
580
|
* @param {object} rule The style rule whose selector list may be shortened.
|
|
578
581
|
*/
|
package/src/rules/stringify.js
CHANGED
|
@@ -3,8 +3,15 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { processDeclarations } from '../declarations/process.js';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
skipParenthesizedGroup,
|
|
8
|
+
skipQuotedString,
|
|
9
|
+
splitTopLevelCommaList,
|
|
10
|
+
startsUrlToken
|
|
11
|
+
} from '../parser/source-search.js';
|
|
12
|
+
import { tokenizeCssValue } from '../parser/tokenizer.js';
|
|
7
13
|
import { minifyValueForOutput } from '../value/minify.js';
|
|
14
|
+
import { elideRedundantSeparators } from '../value/tokens.js';
|
|
8
15
|
|
|
9
16
|
import {
|
|
10
17
|
collapseCustomPropertyWhitespace,
|
|
@@ -111,6 +118,67 @@ function minifyFunctionPrelude (prelude) {
|
|
|
111
118
|
return functionName + '(' + minifiedParameters.join(',') + ')';
|
|
112
119
|
}
|
|
113
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Removes comments between namespace tokens while leaving strings, URLs, and
|
|
123
|
+
* escaped characters intact.
|
|
124
|
+
*
|
|
125
|
+
* @param {string} prelude The namespace prelude as written.
|
|
126
|
+
* @return {string} The prelude with comments replaced by separators.
|
|
127
|
+
*/
|
|
128
|
+
function removeNamespaceComments (prelude) {
|
|
129
|
+
let result = '';
|
|
130
|
+
let index = 0;
|
|
131
|
+
while (index < prelude.length) {
|
|
132
|
+
if (prelude.startsWith('/*', index)) {
|
|
133
|
+
const closingIndex = prelude.indexOf('*/', index + 2);
|
|
134
|
+
index = closingIndex === -1 ? prelude.length : closingIndex + 2;
|
|
135
|
+
result += ' ';
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
let end = index + 1;
|
|
139
|
+
if (prelude[index] === '"' || prelude[index] === '\'') {
|
|
140
|
+
end = skipQuotedString(prelude, index);
|
|
141
|
+
} else if (prelude[index] === '\\') {
|
|
142
|
+
end = Math.min(index + 2, prelude.length);
|
|
143
|
+
} else if (startsUrlToken(prelude, index)) {
|
|
144
|
+
end = skipParenthesizedGroup(prelude, index + 3);
|
|
145
|
+
}
|
|
146
|
+
result += prelude.slice(index, end);
|
|
147
|
+
index = end;
|
|
148
|
+
}
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Unwraps quoted namespace URLs and removes token separators without changing
|
|
154
|
+
* whitespace or escapes inside the namespace name's string.
|
|
155
|
+
*
|
|
156
|
+
* @param {string} prelude The optional namespace prefix and its URL.
|
|
157
|
+
* @return {string} The minified namespace statement.
|
|
158
|
+
*/
|
|
159
|
+
function stringifyNamespace (prelude) {
|
|
160
|
+
const tokens = tokenizeCssValue(removeNamespaceComments(prelude)).filter((token) => {
|
|
161
|
+
return token.type !== 'whitespace';
|
|
162
|
+
});
|
|
163
|
+
const parts = ['@namespace'];
|
|
164
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
165
|
+
const token = tokens[index];
|
|
166
|
+
const isQuotedUrl = (
|
|
167
|
+
token.type === 'function' &&
|
|
168
|
+
token.text.toLowerCase() === 'url(' &&
|
|
169
|
+
tokens[index + 1]?.type === 'string' &&
|
|
170
|
+
tokens[index + 2]?.text === ')'
|
|
171
|
+
);
|
|
172
|
+
if (isQuotedUrl) {
|
|
173
|
+
parts.push(tokens[index + 1].text);
|
|
174
|
+
index += 2;
|
|
175
|
+
} else {
|
|
176
|
+
parts.push(token.text);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return elideRedundantSeparators(parts.join(' ')) + ';';
|
|
180
|
+
}
|
|
181
|
+
|
|
114
182
|
/**
|
|
115
183
|
* Stringifies a generic `at-rule` AST node into minified CSS, with specialized
|
|
116
184
|
* prelude minification for `@function` rules and generic whitespace handling
|
|
@@ -466,6 +534,10 @@ function stringifyRule (rule, context) {
|
|
|
466
534
|
return '@charset ' + rule.charset + ';';
|
|
467
535
|
}
|
|
468
536
|
|
|
537
|
+
if (rule.type === 'namespace') {
|
|
538
|
+
return stringifyNamespace(rule.namespace);
|
|
539
|
+
}
|
|
540
|
+
|
|
469
541
|
if (rule.type === 'import') {
|
|
470
542
|
let importStatement = rule.import;
|
|
471
543
|
// Unwrap url() with a quoted string to just the quoted string
|