@thejaredwilcurt/csslop 0.0.49 → 0.0.51
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 +0 -1
- package/package.json +2 -2
- package/src/declarations/border.js +1 -1
- package/src/declarations/process.js +155 -1
- package/src/index.js +7 -7
- package/src/rules/optimize/at-rules.js +113 -0
- package/src/rules/optimize/declaration-writes.js +70 -0
- package/src/rules/optimize/empty-rules.js +51 -0
- package/src/rules/optimize/expand-nesting.js +236 -0
- package/src/rules/optimize/factor-parents.js +343 -0
- package/src/rules/optimize/index.js +71 -0
- package/src/rules/optimize/merge-declarations.js +199 -0
- package/src/rules/optimize/merge-nested-rules.js +117 -0
- package/src/rules/optimize/merge-selectors.js +252 -0
- package/src/rules/optimize/nest-rules.js +202 -0
- package/src/rules/optimize/override-index.js +264 -0
- package/src/rules/optimize/prune-overridden.js +267 -0
- package/src/rules/optimize/selector-normalization.js +60 -0
- package/src/rules/optimize/specificity.js +280 -0
- package/src/{position-try.js → rules/position-try.js} +1 -1
- package/src/rules/stringify.js +13 -0
- package/src/utilities.js +14 -0
- package/src/value/border.js +85 -0
- package/src/value/{colors.js → color/conversion.js} +3 -2
- package/src/value/color/functions.js +463 -0
- package/src/value/{light-dark.js → color/light-dark.js} +1 -1
- package/src/value/{color-mix.js → color/mix.js} +3 -3
- package/src/value/{relative-colors.js → color/relative.js} +3 -3
- package/src/value/color/shorten.js +93 -0
- package/src/value/gradients.js +1 -1
- package/src/value/hyphenate-limit-chars.js +72 -0
- package/src/value/initial-keywords.js +128 -0
- package/src/value/lengths.js +55 -0
- package/src/value/math/evaluate.js +36 -0
- package/src/value/math/fold.js +315 -0
- package/src/value/math/format.js +230 -0
- package/src/value/math/functions.js +302 -0
- package/src/value/math/index.js +219 -0
- package/src/value/math/parser.js +301 -0
- package/src/value/math/property-ranges.js +174 -0
- package/src/value/math/rewrite.js +222 -0
- package/src/value/math/tokenizer.js +160 -0
- package/src/value/math/unit-totals.js +194 -0
- package/src/value/minify.js +127 -1669
- package/src/value/numbers.js +28 -0
- package/src/value/positions.js +152 -0
- package/src/value/property-optimizations.js +485 -0
- package/src/value/segments.js +74 -0
- package/src/value/{svg-data-uri.js → svg/data-uri.js} +1 -1
- package/src/value/{svg-markup.js → svg/markup.js} +1 -1
- package/src/value/time.js +75 -0
- package/src/value/urls.js +242 -0
- package/src/value/whitespace.js +88 -0
- package/src/rules/optimize.js +0 -2314
- package/src/value/math-expression.js +0 -1156
- package/src/value/math.js +0 -886
- /package/src/value/{named-colors.js → color/named.js} +0 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Splits a CSS math function into the numbers, identifiers, function names, operators, and punctuation it is written from.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
PERCENTAGE,
|
|
7
|
+
UNITLESS
|
|
8
|
+
} from './unit-totals.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Matches a CSS number where the scanner stands: the digits it is written
|
|
12
|
+
* from, together with the fraction and the exponent it may be written with.
|
|
13
|
+
* A sign is left to the parser, which tells the sign of a number from an
|
|
14
|
+
* operator by whether whitespace stands between the two.
|
|
15
|
+
*
|
|
16
|
+
* @type {RegExp}
|
|
17
|
+
*/
|
|
18
|
+
const NUMBER_AT_INDEX = /(?:\d+\.\d+|\.\d+|\d+)(?:e[+-]?\d+)?/iy;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Matches a CSS identifier where the scanner stands: a name of letters,
|
|
22
|
+
* digits, underscores, and hyphens that does not begin with a digit. A numeric
|
|
23
|
+
* constant, a function name, and a keyword argument are all written as
|
|
24
|
+
* identifiers.
|
|
25
|
+
*
|
|
26
|
+
* @type {RegExp}
|
|
27
|
+
*/
|
|
28
|
+
const IDENTIFIER_AT_INDEX = /[a-z_][a-z0-9_-]*/iy;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Matches the unit written against a number where the scanner stands. Every
|
|
32
|
+
* CSS unit is named in letters alone, so a name holding anything else is a
|
|
33
|
+
* token of its own that a number cannot carry.
|
|
34
|
+
*
|
|
35
|
+
* @type {RegExp}
|
|
36
|
+
*/
|
|
37
|
+
const UNIT_AT_INDEX = /[a-z]+/iy;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The characters CSS counts as whitespace, which separates the tokens of a
|
|
41
|
+
* calculation and tells an operator apart from the sign of a number.
|
|
42
|
+
*
|
|
43
|
+
* @type {Set<string>}
|
|
44
|
+
*/
|
|
45
|
+
const WHITESPACE_CHARACTERS = new Set([' ', '\t', '\n', '\r', '\f']);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The characters that punctuate a calculation, and the kind of token each of
|
|
49
|
+
* them stands for.
|
|
50
|
+
*
|
|
51
|
+
* @type {Map<string, string>}
|
|
52
|
+
*/
|
|
53
|
+
const PUNCTUATION_TOKEN_TYPES = new Map([
|
|
54
|
+
['(', 'open'],
|
|
55
|
+
[')', 'close'],
|
|
56
|
+
[',', 'comma']
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The arithmetic operators a calculation is written with.
|
|
61
|
+
*
|
|
62
|
+
* @type {Set<string>}
|
|
63
|
+
*/
|
|
64
|
+
const OPERATOR_CHARACTERS = new Set(['+', '-', '*', '/']);
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Reads the unit written directly against the number that ends at the given
|
|
68
|
+
* index, which is a percent sign or an identifier naming the unit. A number
|
|
69
|
+
* with nothing written against it carries no unit.
|
|
70
|
+
*
|
|
71
|
+
* @param {string} text The math function being tokenized.
|
|
72
|
+
* @param {number} index The index just past the number's digits.
|
|
73
|
+
* @return {object} The unit read and the index just past it.
|
|
74
|
+
*/
|
|
75
|
+
function readUnitAtIndex (text, index) {
|
|
76
|
+
if (text[index] === PERCENTAGE) {
|
|
77
|
+
return { unit: PERCENTAGE, nextIndex: index + 1 };
|
|
78
|
+
}
|
|
79
|
+
UNIT_AT_INDEX.lastIndex = index;
|
|
80
|
+
const unitMatch = UNIT_AT_INDEX.exec(text);
|
|
81
|
+
if (!unitMatch) {
|
|
82
|
+
return { unit: UNITLESS, nextIndex: index };
|
|
83
|
+
}
|
|
84
|
+
return { unit: unitMatch[0], nextIndex: index + unitMatch[0].length };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Splits a math function into the tokens it is written from: its numbers with
|
|
89
|
+
* the units they carry, its identifiers, its function names, its operators,
|
|
90
|
+
* and its punctuation. Each token records whether whitespace precedes it,
|
|
91
|
+
* since that is what tells the sign of a number from an operator.
|
|
92
|
+
*
|
|
93
|
+
* @param {string} text The math function as it is written.
|
|
94
|
+
* @return {Array|null} The tokens of the math function, or null when it holds a character no calculation is written with.
|
|
95
|
+
*/
|
|
96
|
+
function tokenizeMathExpression (text) {
|
|
97
|
+
const tokens = [];
|
|
98
|
+
let index = 0;
|
|
99
|
+
let precededBySpace = false;
|
|
100
|
+
|
|
101
|
+
while (index < text.length) {
|
|
102
|
+
const character = text[index];
|
|
103
|
+
|
|
104
|
+
if (WHITESPACE_CHARACTERS.has(character)) {
|
|
105
|
+
precededBySpace = true;
|
|
106
|
+
index++;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
NUMBER_AT_INDEX.lastIndex = index;
|
|
111
|
+
const numberMatch = NUMBER_AT_INDEX.exec(text);
|
|
112
|
+
if (numberMatch) {
|
|
113
|
+
const { unit, nextIndex } = readUnitAtIndex(text, index + numberMatch[0].length);
|
|
114
|
+
tokens.push({
|
|
115
|
+
type: 'number',
|
|
116
|
+
number: Number(numberMatch[0]),
|
|
117
|
+
unit,
|
|
118
|
+
precededBySpace
|
|
119
|
+
});
|
|
120
|
+
index = nextIndex;
|
|
121
|
+
precededBySpace = false;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (OPERATOR_CHARACTERS.has(character) || PUNCTUATION_TOKEN_TYPES.has(character)) {
|
|
126
|
+
tokens.push({
|
|
127
|
+
type: PUNCTUATION_TOKEN_TYPES.get(character) || 'operator',
|
|
128
|
+
operator: character,
|
|
129
|
+
precededBySpace
|
|
130
|
+
});
|
|
131
|
+
index++;
|
|
132
|
+
precededBySpace = false;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
IDENTIFIER_AT_INDEX.lastIndex = index;
|
|
137
|
+
const identifierMatch = IDENTIFIER_AT_INDEX.exec(text);
|
|
138
|
+
if (!identifierMatch) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
index = index + identifierMatch[0].length;
|
|
142
|
+
// A name written directly against an opening parenthesis calls a function
|
|
143
|
+
const callsFunction = text[index] === '(';
|
|
144
|
+
if (callsFunction) {
|
|
145
|
+
index++;
|
|
146
|
+
}
|
|
147
|
+
tokens.push({
|
|
148
|
+
type: callsFunction ? 'function' : 'identifier',
|
|
149
|
+
name: identifierMatch[0].toLowerCase(),
|
|
150
|
+
precededBySpace
|
|
151
|
+
});
|
|
152
|
+
precededBySpace = false;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return tokens;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export {
|
|
159
|
+
tokenizeMathExpression
|
|
160
|
+
};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Represents the value of a CSS calculation as totals keyed by unit, and works out the sums, products, and quotients of such values.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { convertAbsoluteLengthToPx } from '../shared.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The unit a plain number is totalled under. A number carries no unit, and the
|
|
9
|
+
* empty string is the unit it does not carry.
|
|
10
|
+
*
|
|
11
|
+
* @type {string}
|
|
12
|
+
*/
|
|
13
|
+
const UNITLESS = '';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The unit a percentage is totalled under. A percentage is only resolved
|
|
17
|
+
* against its basis once the element is laid out, so it is totalled as a unit
|
|
18
|
+
* of its own rather than as the number it is written from.
|
|
19
|
+
*
|
|
20
|
+
* @type {string}
|
|
21
|
+
*/
|
|
22
|
+
const PERCENTAGE = '%';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Creates the totals of a value that states a single quantity, holding the
|
|
26
|
+
* number it is written from under the unit it is written with. An absolute
|
|
27
|
+
* length is held as the pixels it measures, since every length stating the
|
|
28
|
+
* same measurement then totals under the same unit.
|
|
29
|
+
*
|
|
30
|
+
* @param {number} number The number the quantity is written from.
|
|
31
|
+
* @param {string} unit The unit the quantity is written with, or the empty string when it is a plain number.
|
|
32
|
+
* @param {boolean} preserveWrittenQuantity Whether to retain the original spelling for a standalone quantity.
|
|
33
|
+
* @return {Map} The unit totals the quantity states.
|
|
34
|
+
*/
|
|
35
|
+
function createUnitTotals (number, unit, preserveWrittenQuantity = false) {
|
|
36
|
+
const lowercasedUnit = unit.toLowerCase();
|
|
37
|
+
const pixels = convertAbsoluteLengthToPx(number, lowercasedUnit);
|
|
38
|
+
let totals;
|
|
39
|
+
if (pixels === null) {
|
|
40
|
+
totals = new Map([[lowercasedUnit, number]]);
|
|
41
|
+
} else {
|
|
42
|
+
totals = new Map([['px', pixels]]);
|
|
43
|
+
}
|
|
44
|
+
if (preserveWrittenQuantity) {
|
|
45
|
+
totals.writtenQuantity = {
|
|
46
|
+
number,
|
|
47
|
+
unit: lowercasedUnit,
|
|
48
|
+
canonicalUnit: pixels === null ? lowercasedUnit : 'px',
|
|
49
|
+
canonicalNumber: pixels === null ? number : pixels
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return totals;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Reports whether a value states a plain number, which is what an operand has
|
|
57
|
+
* to be to scale or divide the quantity it is written against.
|
|
58
|
+
*
|
|
59
|
+
* @param {Map} value The unit totals the value holds.
|
|
60
|
+
* @return {boolean} Whether the value states a plain number.
|
|
61
|
+
*/
|
|
62
|
+
function isPlainNumber (value) {
|
|
63
|
+
return value.size === 1 && value.has(UNITLESS);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Reads a value as the single quantity it states, which is what a function
|
|
68
|
+
* comparing its arguments needs each of them to be. A value totalling more
|
|
69
|
+
* than one unit is only resolved once its units are resolved against the
|
|
70
|
+
* element, so it states no single number to read.
|
|
71
|
+
*
|
|
72
|
+
* @param {Map} value The unit totals the value holds.
|
|
73
|
+
* @return {object|null} The number and the unit the value states, or null when it states more than one quantity.
|
|
74
|
+
*/
|
|
75
|
+
function readSingleQuantity (value) {
|
|
76
|
+
if (value.size !== 1) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const [[unit, number]] = [...value];
|
|
80
|
+
return { number, unit };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Scales every quantity a value states by a plain number, which is how a
|
|
85
|
+
* multiplication and a division are worked out.
|
|
86
|
+
*
|
|
87
|
+
* @param {Map} value The unit totals the value holds.
|
|
88
|
+
* @param {number} factor The number every total is scaled by.
|
|
89
|
+
* @return {Map} The scaled unit totals.
|
|
90
|
+
*/
|
|
91
|
+
function scaleUnitTotals (value, factor) {
|
|
92
|
+
const scaled = new Map();
|
|
93
|
+
for (const [unit, coefficient] of value) {
|
|
94
|
+
scaled.set(unit, coefficient * factor);
|
|
95
|
+
}
|
|
96
|
+
return scaled;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Adds one value to another, totalling the quantities they state under the
|
|
101
|
+
* units they are written with. A sum of a plain number and a quantity carrying
|
|
102
|
+
* a unit states nothing a stylesheet can hold, so such a sum has no total to
|
|
103
|
+
* give.
|
|
104
|
+
*
|
|
105
|
+
* @param {Map} left The unit totals the value on the left holds.
|
|
106
|
+
* @param {Map} right The unit totals the value on the right holds.
|
|
107
|
+
* @param {number} sign The direction the right value is added in: 1 to add it, -1 to subtract it.
|
|
108
|
+
* @return {Map|null} The totals of the sum, or null when the two cannot be added.
|
|
109
|
+
*/
|
|
110
|
+
function addUnitTotals (left, right, sign) {
|
|
111
|
+
const totals = new Map(left);
|
|
112
|
+
for (const [unit, coefficient] of right) {
|
|
113
|
+
totals.set(unit, (totals.get(unit) || 0) + (coefficient * sign));
|
|
114
|
+
}
|
|
115
|
+
const addsNumberToQuantity = totals.has(UNITLESS) && totals.size > 1;
|
|
116
|
+
if (addsNumberToQuantity) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
return totals;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Multiplies two values, which CSS only allows when one of them is a plain
|
|
124
|
+
* number: the product of two quantities carrying units measures an area or a
|
|
125
|
+
* rate, and no CSS property takes one.
|
|
126
|
+
*
|
|
127
|
+
* @param {Map} left The unit totals the value on the left holds.
|
|
128
|
+
* @param {Map} right The unit totals the value on the right holds.
|
|
129
|
+
* @return {Map|null} The totals of the product, or null when neither side is a plain number.
|
|
130
|
+
*/
|
|
131
|
+
function multiplyUnitTotals (left, right) {
|
|
132
|
+
if (isPlainNumber(right)) {
|
|
133
|
+
return scaleUnitTotals(left, right.get(UNITLESS));
|
|
134
|
+
}
|
|
135
|
+
if (isPlainNumber(left)) {
|
|
136
|
+
return scaleUnitTotals(right, left.get(UNITLESS));
|
|
137
|
+
}
|
|
138
|
+
const leftQuantity = readSingleQuantity(left);
|
|
139
|
+
const rightQuantity = readSingleQuantity(right);
|
|
140
|
+
if (leftQuantity && rightQuantity) {
|
|
141
|
+
if (leftQuantity.unit.startsWith('reciprocal:') && leftQuantity.unit.slice('reciprocal:'.length) === rightQuantity.unit) {
|
|
142
|
+
return createUnitTotals(leftQuantity.number * rightQuantity.number, UNITLESS);
|
|
143
|
+
}
|
|
144
|
+
if (rightQuantity.unit.startsWith('reciprocal:') && rightQuantity.unit.slice('reciprocal:'.length) === leftQuantity.unit) {
|
|
145
|
+
return createUnitTotals(leftQuantity.number * rightQuantity.number, UNITLESS);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Divides a value by another, which CSS only allows when the divisor is a
|
|
153
|
+
* plain number. A division by zero resolves to an infinity that no property
|
|
154
|
+
* takes, so it is left for the stylesheet to state as written.
|
|
155
|
+
*
|
|
156
|
+
* @param {Map} left The unit totals of the value being divided.
|
|
157
|
+
* @param {Map} right The unit totals of the value dividing it.
|
|
158
|
+
* @return {Map|null} The totals of the quotient, or null when the division cannot be worked out.
|
|
159
|
+
*/
|
|
160
|
+
function divideUnitTotals (left, right) {
|
|
161
|
+
if (isPlainNumber(right)) {
|
|
162
|
+
const divisor = right.get(UNITLESS);
|
|
163
|
+
if (divisor === 0) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
return scaleUnitTotals(left, 1 / divisor);
|
|
167
|
+
}
|
|
168
|
+
const dividendQuantity = readSingleQuantity(left);
|
|
169
|
+
const divisorQuantity = readSingleQuantity(right);
|
|
170
|
+
if (!dividendQuantity || !divisorQuantity || divisorQuantity.number === 0) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
if (dividendQuantity.unit === divisorQuantity.unit) {
|
|
174
|
+
return createUnitTotals(dividendQuantity.number / divisorQuantity.number, UNITLESS);
|
|
175
|
+
}
|
|
176
|
+
if (dividendQuantity.unit === UNITLESS) {
|
|
177
|
+
return createUnitTotals(
|
|
178
|
+
dividendQuantity.number / divisorQuantity.number,
|
|
179
|
+
'reciprocal:' + divisorQuantity.unit
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export {
|
|
186
|
+
addUnitTotals,
|
|
187
|
+
createUnitTotals,
|
|
188
|
+
divideUnitTotals,
|
|
189
|
+
multiplyUnitTotals,
|
|
190
|
+
PERCENTAGE,
|
|
191
|
+
readSingleQuantity,
|
|
192
|
+
scaleUnitTotals,
|
|
193
|
+
UNITLESS
|
|
194
|
+
};
|