@animus-ui/properties 0.1.20 → 0.1.22
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/dist/index.d.ts +159 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +555 -172
- package/package.json +2 -2
- package/dist/registration-values.d.ts +0 -41
- package/dist/registration-values.d.ts.map +0 -1
- package/dist/shorthands.d.ts +0 -5
- package/dist/shorthands.d.ts.map +0 -1
- package/dist/unitless.d.ts +0 -4
- package/dist/unitless.d.ts.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,160 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
//#region src/shorthands.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Covers only shorthands registered in the prop system, not all CSS shorthands.
|
|
4
|
+
*/
|
|
5
|
+
export declare const SHORTHAND_PROPERTIES: readonly ['border', 'borderTop', 'borderBottom', 'borderLeft', 'borderRight', 'borderWidth', 'borderStyle', 'borderColor', 'background', 'flex', 'margin', 'padding', 'transition', 'gap', 'grid', 'gridArea', 'gridColumn', 'gridRow', 'gridTemplate', 'overflow'];
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region src/unitless.d.ts
|
|
8
|
+
export declare const UNITLESS_PROPERTIES: Set<string>;
|
|
9
|
+
/** Accepts either spelling: `line-height` or `lineHeight`,
|
|
10
|
+
* `-webkit-line-clamp` or `WebkitLineClamp`. */
|
|
11
|
+
export declare function isUnitlessProperty(property: string): boolean;
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/css-tokens.d.ts
|
|
14
|
+
/**
|
|
15
|
+
* CSS tokens as CSS Syntax Level 3 reads them, with their source offsets.
|
|
16
|
+
* Comments are trivia and make no token. Identifiers, function names,
|
|
17
|
+
* at-keywords, hashes and units are decoded, escapes included. A quoted
|
|
18
|
+
* string and an unquoted `url()` are each one opaque token, so nothing inside
|
|
19
|
+
* them reads as a name or a function.
|
|
20
|
+
*/
|
|
21
|
+
type CssTokenType = 'whitespace' | 'ident' | 'function' | 'at-keyword' | 'hash' | 'string' | 'bad-string' | 'url' | 'bad-url' | 'number' | 'percentage' | 'dimension' | 'delim' | 'CDO' | 'CDC' | '(' | ')' | '[' | ']' | '{' | '}' | ',' | ':' | ';';
|
|
22
|
+
interface CssToken {
|
|
23
|
+
type: CssTokenType;
|
|
24
|
+
start: number;
|
|
25
|
+
end: number;
|
|
26
|
+
/** The decoded name of an ident, function, at-keyword or hash, the
|
|
27
|
+
* decoded unit of a dimension, or a delim's character; otherwise `''`. */
|
|
28
|
+
value: string;
|
|
29
|
+
/** A number, percentage or dimension's numeric value; otherwise `0`. */
|
|
30
|
+
number: number;
|
|
31
|
+
}
|
|
32
|
+
/** The identifier starting at `index`, decoded, or `undefined` when none
|
|
33
|
+
* starts there. */
|
|
34
|
+
export declare function identifierAt(css: string, index: number): {
|
|
35
|
+
name: string;
|
|
36
|
+
end: number;
|
|
37
|
+
} | undefined;
|
|
38
|
+
/** `name` decoded when it is one whole identifier, else as written. */
|
|
39
|
+
export declare function decodedIdentifier(name: string): string;
|
|
40
|
+
/** Every token of `css`, in order. */
|
|
41
|
+
export declare function tokenize(css: string): CssToken[];
|
|
42
|
+
/** A component value: a function, or a `()`, `[]` or `{}` block, with its
|
|
43
|
+
* contents, or a single token. Offsets span the whole value. */
|
|
44
|
+
type ComponentValue = {
|
|
45
|
+
kind: 'block';
|
|
46
|
+
/** The function token, or the `(`, `[` or `{` token. */
|
|
47
|
+
open: CssToken;
|
|
48
|
+
/** `undefined` when the block is never closed. */
|
|
49
|
+
close: CssToken | undefined;
|
|
50
|
+
children: ComponentValue[];
|
|
51
|
+
start: number;
|
|
52
|
+
end: number;
|
|
53
|
+
} | {
|
|
54
|
+
kind: 'token';
|
|
55
|
+
token: CssToken;
|
|
56
|
+
start: number;
|
|
57
|
+
end: number;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* `tokens` as component values. Only the innermost open block's own closer
|
|
61
|
+
* closes it; any other closer stays a token, and a block left open closes at
|
|
62
|
+
* the end, as CSS reads them.
|
|
63
|
+
*/
|
|
64
|
+
export declare function componentValues(tokens: readonly CssToken[]): ComponentValue[];
|
|
65
|
+
/** Whether `test` holds for a value at any depth. */
|
|
66
|
+
export declare function someValue(values: readonly ComponentValue[], test: (value: ComponentValue) => boolean): boolean;
|
|
67
|
+
/** A `var()` read: the decoded custom-property name, the call's offsets,
|
|
68
|
+
* and its fallback's: after the comma, the first value's start, and before
|
|
69
|
+
* the closing parenthesis. */
|
|
70
|
+
interface VariableRead {
|
|
71
|
+
name: string;
|
|
72
|
+
start: number;
|
|
73
|
+
end: number;
|
|
74
|
+
fallback: {
|
|
75
|
+
start: number;
|
|
76
|
+
first: number;
|
|
77
|
+
end: number;
|
|
78
|
+
} | undefined;
|
|
79
|
+
}
|
|
80
|
+
/** Whether `value` is whitespace. */
|
|
81
|
+
export declare const isSpace: (value: ComponentValue | undefined) => boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Every `var()` call in `values` at any depth, fallbacks included, outer
|
|
84
|
+
* calls first: a function whose decoded name is `var` in any case, and whose
|
|
85
|
+
* first argument is a custom-property name.
|
|
86
|
+
*/
|
|
87
|
+
export declare function variableReads(values: readonly ComponentValue[]): VariableRead[];
|
|
88
|
+
/** How a value spells the `!important` it ends with: `hidden !important`,
|
|
89
|
+
* any case and spacing, or the shorthand `hidden!`. */
|
|
90
|
+
type ImportantSpelling = 'important' | 'shorthand';
|
|
91
|
+
interface ImportantPriority {
|
|
92
|
+
spelling: ImportantSpelling;
|
|
93
|
+
/** Where the value before the priority ends. */
|
|
94
|
+
end: number;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The `!important` `css` ends with; kept in step with `important_priority`
|
|
98
|
+
* in the Rust engine. The value has one `!`, a delim token, so a `!` inside
|
|
99
|
+
* a string, a `url()` or an escape is no priority, and neither is a lone `!`
|
|
100
|
+
* or `!important`.
|
|
101
|
+
*/
|
|
102
|
+
export declare function importantPriority(css: string): ImportantPriority | undefined;
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region src/registration-values.d.ts
|
|
105
|
+
/**
|
|
106
|
+
* Reads a custom-property registration's initial value as CSS component
|
|
107
|
+
* values: what it substitutes, which units make it depend on context, and
|
|
108
|
+
* what an all-absolute math function computes to. Tokens come from the shared
|
|
109
|
+
* CSS reader, so comments are trivia, quoted strings and `url()` stay text,
|
|
110
|
+
* and escaped names are decoded.
|
|
111
|
+
*/
|
|
112
|
+
/** Whether `value` substitutes another value (`var()`, `env()`, `attr()`),
|
|
113
|
+
* which no registration's initial value may. */
|
|
114
|
+
export declare function substitutesValue(value: string): boolean;
|
|
115
|
+
/** Whether `value` uses a font- or container-relative unit, inside math
|
|
116
|
+
* functions too, which makes a typed initial value depend on context. */
|
|
117
|
+
export declare function dependsOnContext(value: string): boolean;
|
|
118
|
+
/** What an all-absolute math expression computes to. */
|
|
119
|
+
interface Computed {
|
|
120
|
+
type: 'number' | 'length' | 'angle' | 'time' | 'resolution';
|
|
121
|
+
value: number;
|
|
122
|
+
}
|
|
123
|
+
/** What a math initial value folds to. */
|
|
124
|
+
type FoldedInitialValue =
|
|
125
|
+
/** The computed value the syntax accepts, as CSS text such as `1`, `16px`
|
|
126
|
+
* or `90deg`. */
|
|
127
|
+
{
|
|
128
|
+
kind: 'folded';
|
|
129
|
+
text: string;
|
|
130
|
+
} |
|
|
131
|
+
/** A computed value no syntax alternative accepts. */
|
|
132
|
+
{
|
|
133
|
+
kind: 'rejected';
|
|
134
|
+
text: string;
|
|
135
|
+
type: Computed['type'];
|
|
136
|
+
} |
|
|
137
|
+
/** Math that breaks CSS calculation syntax, which browsers ignore. */
|
|
138
|
+
{
|
|
139
|
+
kind: 'invalid';
|
|
140
|
+
reason: string;
|
|
141
|
+
} |
|
|
142
|
+
/** An infinite or NaN result: browsers clamp it, and it has no plain
|
|
143
|
+
* value to write. */
|
|
144
|
+
{
|
|
145
|
+
kind: 'not-finite';
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* The computed value of an initial value that is one math function (`calc()`,
|
|
149
|
+
* `min()`, `max()`, `clamp()`) over numbers and absolute units, such as
|
|
150
|
+
* `calc(1px / 1px)` → `1`. The browser computes the same value, and CSS
|
|
151
|
+
* tooling that cannot parse the function in that position reads the result.
|
|
152
|
+
* The syntax's alternatives are tried in their declared order, and an
|
|
153
|
+
* `<integer>` one rounds a number result to the nearest integer, halves
|
|
154
|
+
* toward positive infinity, as CSS does. `undefined` for anything else, and
|
|
155
|
+
* for the universal syntax, whose initial value is kept as written.
|
|
156
|
+
*/
|
|
157
|
+
export declare function foldInitialValue(syntax: string, initialValue: string): FoldedInitialValue | undefined;
|
|
158
|
+
//#endregion
|
|
159
|
+
export type { ComponentValue, CssToken, FoldedInitialValue, ImportantPriority, ImportantSpelling, VariableRead };
|
|
5
160
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/shorthands.ts","../src/unitless.ts","../src/css-tokens.ts","../src/registration-values.ts"],"mappings":";;;;qBAGa;;;qBCHA,qBAAmB;;;wBAgHhB,mBAAmB;;;;;;;;;;KCxGvB;UA0BK;EACf,MAAM;EACN;EACA;;;EAGA;;EAEA;;;;wBAiFc,aACd,aACA;EACG;EAAc;;;wBAOH,kBAAkB;;wBAyFlB,SAAS,cAAc;;;KA0E3B;EAEN;;EAEA,MAAM;;EAEN,OAAO;EACP,UAAU;EACV;EACA;;EAEA;EAAe,OAAO;EAAU;EAAe;;;;;;;wBAgBrC,gBAAgB,iBAAiB,aAAa;;wBAoC9C,UACd,iBAAiB,kBACjB,OAAO,OAAO;;;;UAWC;EACf;EACA;EACA;EACA;IAAY;IAAe;IAAe;;;;qBAI/B,UAAO,OAAW;;;;;;wBAQf,cACd,iBAAiB,mBAChB;;;KAiCS;UAEK;EACf,UAAU;;EAEV;;;;;;;;wBASc,kBAAkB,cAAc;;;;;;;;;;;;wBCtZhC,iBAAiB;;;wBA6BjB,iBAAiB;;UAWvB;EACR;EACA;;;KA+NU;;;;EAGN;EAAgB;;;;EAEhB;EAAkB;EAAc,MAAM;;;;EAEtC;EAAiB;;;;;EAGjB;;;;;;;;;;;;wBAYU,iBACd,gBACA,uBACC"}
|
package/dist/index.js
CHANGED
|
@@ -71,97 +71,415 @@ const UNITLESS_PROPERTIES = /* @__PURE__ */ new Set([
|
|
|
71
71
|
"tab-size",
|
|
72
72
|
"widows",
|
|
73
73
|
"z-index",
|
|
74
|
-
"zoom"
|
|
74
|
+
"zoom",
|
|
75
|
+
"-moz-animation-iteration-count",
|
|
76
|
+
"-moz-animation-name",
|
|
77
|
+
"-moz-box-flex",
|
|
78
|
+
"-moz-box-ordinal-group",
|
|
79
|
+
"-moz-column-count",
|
|
80
|
+
"-moz-columns",
|
|
81
|
+
"-moz-opacity",
|
|
82
|
+
"-moz-tab-size",
|
|
83
|
+
"-ms-flex",
|
|
84
|
+
"-ms-flex-positive",
|
|
85
|
+
"-ms-order",
|
|
86
|
+
"-o-animation-iteration-count",
|
|
87
|
+
"-o-animation-name",
|
|
88
|
+
"-o-tab-size",
|
|
89
|
+
"-webkit-animation-iteration-count",
|
|
90
|
+
"-webkit-animation-name",
|
|
91
|
+
"-webkit-border-image-slice",
|
|
92
|
+
"-webkit-box-flex",
|
|
93
|
+
"-webkit-box-flex-group",
|
|
94
|
+
"-webkit-box-ordinal-group",
|
|
95
|
+
"-webkit-column-count",
|
|
96
|
+
"-webkit-columns",
|
|
97
|
+
"-webkit-flex",
|
|
98
|
+
"-webkit-flex-grow",
|
|
99
|
+
"-webkit-flex-shrink",
|
|
100
|
+
"-webkit-line-clamp",
|
|
101
|
+
"-webkit-order"
|
|
75
102
|
]);
|
|
103
|
+
/**
|
|
104
|
+
* A style key's vendor prefix and the CSS prefix it stands for:
|
|
105
|
+
* `WebkitLineClamp` is `-webkit-line-clamp`, `msFlex` is `-ms-flex`. The
|
|
106
|
+
* extractor reads this table through `property_table.json`.
|
|
107
|
+
*/
|
|
108
|
+
const VENDOR_PREFIXES = [
|
|
109
|
+
["Webkit", "-webkit-"],
|
|
110
|
+
["Moz", "-moz-"],
|
|
111
|
+
["ms", "-ms-"],
|
|
112
|
+
["O", "-o-"]
|
|
113
|
+
];
|
|
114
|
+
/** The camelCase style key of a CSS property name. */
|
|
115
|
+
function styleKey(property) {
|
|
116
|
+
const vendor = VENDOR_PREFIXES.find(([, prefix]) => property.startsWith(prefix));
|
|
117
|
+
const [key, rest] = vendor ? [vendor[0], property.slice(vendor[1].length - 1)] : ["", property];
|
|
118
|
+
return key + rest.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
119
|
+
}
|
|
76
120
|
const UNITLESS_PROPERTY_SPELLINGS = /* @__PURE__ */ new Set();
|
|
77
121
|
for (const property of UNITLESS_PROPERTIES) {
|
|
78
122
|
UNITLESS_PROPERTY_SPELLINGS.add(property);
|
|
79
|
-
UNITLESS_PROPERTY_SPELLINGS.add(property
|
|
123
|
+
UNITLESS_PROPERTY_SPELLINGS.add(styleKey(property));
|
|
80
124
|
}
|
|
81
|
-
/** Accepts either spelling: `line-height` or `lineHeight
|
|
125
|
+
/** Accepts either spelling: `line-height` or `lineHeight`,
|
|
126
|
+
* `-webkit-line-clamp` or `WebkitLineClamp`. */
|
|
82
127
|
function isUnitlessProperty(property) {
|
|
83
128
|
return UNITLESS_PROPERTY_SPELLINGS.has(property);
|
|
84
129
|
}
|
|
85
130
|
//#endregion
|
|
86
|
-
//#region src/
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
131
|
+
//#region src/css-tokens.ts
|
|
132
|
+
const at = (css, index) => index < css.length ? css.charCodeAt(index) : -1;
|
|
133
|
+
const isNewline = (c) => c === 10 || c === 13 || c === 12;
|
|
134
|
+
const isWhitespace = (c) => c === 32 || c === 9 || isNewline(c);
|
|
135
|
+
const isDigit = (c) => c >= 48 && c <= 57;
|
|
136
|
+
const isHexDigit = (c) => isDigit(c) || c >= 65 && c <= 70 || c >= 97 && c <= 102;
|
|
137
|
+
const isIdentStart = (c) => c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95 || c >= 128;
|
|
138
|
+
const isIdentChar = (c) => isIdentStart(c) || isDigit(c) || c === 45;
|
|
139
|
+
const isNonPrintable = (c) => c >= 0 && c <= 8 || c === 11 || c >= 14 && c <= 31 || c === 127;
|
|
140
|
+
/** Whether a `\` at `index` starts an escape. */
|
|
141
|
+
const isEscape = (css, index) => at(css, index) === 92 && !isNewline(at(css, index + 1));
|
|
142
|
+
function startsIdentifier(css, index) {
|
|
143
|
+
const c = at(css, index);
|
|
144
|
+
if (c === 45) {
|
|
145
|
+
const next = at(css, index + 1);
|
|
146
|
+
return isIdentStart(next) || next === 45 || isEscape(css, index + 1);
|
|
147
|
+
}
|
|
148
|
+
return isIdentStart(c) || isEscape(css, index);
|
|
149
|
+
}
|
|
150
|
+
function startsNumber(css, index) {
|
|
151
|
+
const signed = at(css, index) === 43 || at(css, index) === 45 ? 1 : 0;
|
|
152
|
+
const c = at(css, index + signed);
|
|
153
|
+
return isDigit(c) || c === 46 && isDigit(at(css, index + signed + 1));
|
|
154
|
+
}
|
|
155
|
+
/** The code point the escape at `index` stands for, and the index after it. */
|
|
156
|
+
function readEscape(css, index) {
|
|
157
|
+
let i = index + 1;
|
|
158
|
+
if (i >= css.length) return ["�", i];
|
|
159
|
+
if (!isHexDigit(at(css, i))) {
|
|
160
|
+
const char = String.fromCodePoint(css.codePointAt(i) ?? 65533);
|
|
161
|
+
return [char, i + char.length];
|
|
162
|
+
}
|
|
163
|
+
const digitsEnd = Math.min(i + 6, css.length);
|
|
164
|
+
let value = 0;
|
|
165
|
+
while (i < digitsEnd && isHexDigit(at(css, i))) {
|
|
166
|
+
value = value * 16 + parseInt(css[i], 16);
|
|
167
|
+
i += 1;
|
|
168
|
+
}
|
|
169
|
+
if (at(css, i) === 13 && at(css, i + 1) === 10) i += 2;
|
|
170
|
+
else if (isWhitespace(at(css, i))) i += 1;
|
|
171
|
+
const valid = value !== 0 && value <= 1114111 && (value < 55296 || value > 57343);
|
|
172
|
+
return [String.fromCodePoint(valid ? value : 65533), i];
|
|
173
|
+
}
|
|
174
|
+
/** The identifier sequence at `index`, decoded, and the index after it. */
|
|
175
|
+
function readName(css, index) {
|
|
176
|
+
let name = "";
|
|
177
|
+
let from = index;
|
|
178
|
+
let i = index;
|
|
179
|
+
for (;;) if (isIdentChar(at(css, i))) i += 1;
|
|
180
|
+
else if (isEscape(css, i)) {
|
|
181
|
+
const [char, next] = readEscape(css, i);
|
|
182
|
+
name += css.slice(from, i) + char;
|
|
183
|
+
i = next;
|
|
184
|
+
from = i;
|
|
185
|
+
} else return [name + css.slice(from, i), i];
|
|
186
|
+
}
|
|
187
|
+
/** The identifier starting at `index`, decoded, or `undefined` when none
|
|
188
|
+
* starts there. */
|
|
189
|
+
function identifierAt(css, index) {
|
|
190
|
+
if (!startsIdentifier(css, index)) return void 0;
|
|
191
|
+
const [name, end] = readName(css, index);
|
|
192
|
+
return {
|
|
193
|
+
name,
|
|
194
|
+
end
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/** `name` decoded when it is one whole identifier, else as written. */
|
|
198
|
+
function decodedIdentifier(name) {
|
|
199
|
+
const read = identifierAt(name, 0);
|
|
200
|
+
return read?.end === name.length ? read.name : name;
|
|
201
|
+
}
|
|
202
|
+
function readString(css, index) {
|
|
203
|
+
const quote = at(css, index);
|
|
204
|
+
let i = index + 1;
|
|
205
|
+
for (;;) {
|
|
206
|
+
const c = at(css, i);
|
|
207
|
+
if (c === -1) return ["string", i];
|
|
208
|
+
if (c === quote) return ["string", i + 1];
|
|
209
|
+
if (isNewline(c)) return ["bad-string", i];
|
|
210
|
+
if (c === 92) {
|
|
211
|
+
const next = at(css, i + 1);
|
|
212
|
+
if (next === -1) i += 1;
|
|
213
|
+
else if (isNewline(next)) i += next === 13 && at(css, i + 2) === 10 ? 3 : 2;
|
|
214
|
+
else i = readEscape(css, i)[1];
|
|
215
|
+
} else i += 1;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function badUrlEnd(css, index) {
|
|
219
|
+
let i = index;
|
|
220
|
+
for (;;) {
|
|
221
|
+
const c = at(css, i);
|
|
222
|
+
if (c === -1) return i;
|
|
223
|
+
if (c === 41) return i + 1;
|
|
224
|
+
i = isEscape(css, i) ? readEscape(css, i)[1] : i + 1;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/** An unquoted `url(` body starting at `index`. */
|
|
228
|
+
function readUrl(css, index) {
|
|
229
|
+
let i = index;
|
|
230
|
+
while (isWhitespace(at(css, i))) i += 1;
|
|
231
|
+
for (;;) {
|
|
232
|
+
const c = at(css, i);
|
|
233
|
+
if (c === -1) return ["url", i];
|
|
234
|
+
if (c === 41) return ["url", i + 1];
|
|
235
|
+
if (isWhitespace(c)) {
|
|
236
|
+
while (isWhitespace(at(css, i))) i += 1;
|
|
237
|
+
const next = at(css, i);
|
|
238
|
+
if (next === -1) return ["url", i];
|
|
239
|
+
if (next === 41) return ["url", i + 1];
|
|
240
|
+
return ["bad-url", badUrlEnd(css, i)];
|
|
241
|
+
}
|
|
242
|
+
if (c === 34 || c === 39 || c === 40 || isNonPrintable(c)) return ["bad-url", badUrlEnd(css, i)];
|
|
243
|
+
if (c === 92) {
|
|
244
|
+
if (!isEscape(css, i)) return ["bad-url", badUrlEnd(css, i)];
|
|
245
|
+
i = readEscape(css, i)[1];
|
|
246
|
+
} else i += 1;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function numberEnd(css, index) {
|
|
250
|
+
let i = index;
|
|
251
|
+
if (at(css, i) === 43 || at(css, i) === 45) i += 1;
|
|
252
|
+
while (isDigit(at(css, i))) i += 1;
|
|
253
|
+
if (at(css, i) === 46 && isDigit(at(css, i + 1))) {
|
|
254
|
+
i += 2;
|
|
255
|
+
while (isDigit(at(css, i))) i += 1;
|
|
256
|
+
}
|
|
257
|
+
const e = at(css, i);
|
|
258
|
+
if (e === 69 || e === 101) {
|
|
259
|
+
const sign = at(css, i + 1) === 43 || at(css, i + 1) === 45 ? 1 : 0;
|
|
260
|
+
if (isDigit(at(css, i + 1 + sign))) {
|
|
261
|
+
i += 2 + sign;
|
|
262
|
+
while (isDigit(at(css, i))) i += 1;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return i;
|
|
266
|
+
}
|
|
267
|
+
const SINGLE = new Map([
|
|
268
|
+
"(",
|
|
269
|
+
")",
|
|
270
|
+
"[",
|
|
271
|
+
"]",
|
|
272
|
+
"{",
|
|
273
|
+
"}",
|
|
274
|
+
",",
|
|
275
|
+
":",
|
|
276
|
+
";"
|
|
277
|
+
].map((c) => [c, c]));
|
|
278
|
+
/** Every token of `css`, in order. */
|
|
279
|
+
function tokenize(css) {
|
|
280
|
+
const tokens = [];
|
|
281
|
+
let index = 0;
|
|
282
|
+
const push = (type, end, value = "", number = 0) => {
|
|
283
|
+
tokens.push({
|
|
284
|
+
type,
|
|
285
|
+
start: index,
|
|
286
|
+
end,
|
|
287
|
+
value,
|
|
288
|
+
number
|
|
289
|
+
});
|
|
290
|
+
index = end;
|
|
291
|
+
};
|
|
292
|
+
while (index < css.length) {
|
|
293
|
+
const c = css.charCodeAt(index);
|
|
294
|
+
if (c === 47 && at(css, index + 1) === 42) {
|
|
295
|
+
const close = css.indexOf("*/", index + 2);
|
|
296
|
+
index = close === -1 ? css.length : close + 2;
|
|
297
|
+
} else if (isWhitespace(c)) {
|
|
298
|
+
let end = index + 1;
|
|
299
|
+
while (isWhitespace(at(css, end))) end += 1;
|
|
300
|
+
push("whitespace", end);
|
|
301
|
+
} else if (c === 34 || c === 39) {
|
|
302
|
+
const [type, end] = readString(css, index);
|
|
303
|
+
push(type, end);
|
|
304
|
+
} else if (startsNumber(css, index)) {
|
|
305
|
+
const end = numberEnd(css, index);
|
|
306
|
+
const number = Number(css.slice(index, end));
|
|
307
|
+
if (startsIdentifier(css, end)) {
|
|
308
|
+
const [unit, unitEnd] = readName(css, end);
|
|
309
|
+
push("dimension", unitEnd, unit, number);
|
|
310
|
+
} else if (at(css, end) === 37) push("percentage", end + 1, "", number);
|
|
311
|
+
else push("number", end, "", number);
|
|
312
|
+
} else if (c === 45 && css.startsWith("->", index + 1)) push("CDC", index + 3);
|
|
313
|
+
else if (startsIdentifier(css, index)) {
|
|
314
|
+
const [name, end] = readName(css, index);
|
|
315
|
+
if (at(css, end) !== 40) push("ident", end, name);
|
|
316
|
+
else if (!/^url$/i.test(name)) push("function", end + 1, name);
|
|
317
|
+
else {
|
|
318
|
+
let body = end + 1;
|
|
319
|
+
while (isWhitespace(at(css, body)) && isWhitespace(at(css, body + 1))) body += 1;
|
|
320
|
+
const first = at(css, body);
|
|
321
|
+
const quote = isWhitespace(first) ? at(css, body + 1) : first;
|
|
322
|
+
if (quote === 34 || quote === 39) push("function", body, name);
|
|
323
|
+
else {
|
|
324
|
+
const [type, urlEnd] = readUrl(css, end + 1);
|
|
325
|
+
push(type, urlEnd);
|
|
128
326
|
}
|
|
129
|
-
const args = values(true);
|
|
130
|
-
if (args === void 0) return void 0;
|
|
131
|
-
out.push({
|
|
132
|
-
kind: "function",
|
|
133
|
-
name,
|
|
134
|
-
args
|
|
135
|
-
});
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
const number = NUMBER.exec(rest)?.[0];
|
|
139
|
-
if (number !== void 0) {
|
|
140
|
-
const unit = /^(?:%|[a-zA-Z]+)/.exec(rest.slice(number.length))?.[0];
|
|
141
|
-
out.push({
|
|
142
|
-
kind: "numeric",
|
|
143
|
-
value: Number(number),
|
|
144
|
-
unit: (unit ?? "").toLowerCase()
|
|
145
|
-
});
|
|
146
|
-
at += number.length + (unit?.length ?? 0);
|
|
147
|
-
continue;
|
|
148
327
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
328
|
+
} else if (c === 35 && (isIdentChar(at(css, index + 1)) || isEscape(css, index + 1))) {
|
|
329
|
+
const [name, end] = readName(css, index + 1);
|
|
330
|
+
push("hash", end, name);
|
|
331
|
+
} else if (c === 64 && startsIdentifier(css, index + 1)) {
|
|
332
|
+
const [name, end] = readName(css, index + 1);
|
|
333
|
+
push("at-keyword", end, name);
|
|
334
|
+
} else if (c === 60 && css.startsWith("!--", index + 1)) push("CDO", index + 4);
|
|
335
|
+
else {
|
|
336
|
+
const single = SINGLE.get(css[index]);
|
|
337
|
+
if (single === void 0) push("delim", index + 1, css[index]);
|
|
338
|
+
else push(single, index + 1);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return tokens;
|
|
342
|
+
}
|
|
343
|
+
const CLOSERS = /* @__PURE__ */ new Map([
|
|
344
|
+
["function", ")"],
|
|
345
|
+
["(", ")"],
|
|
346
|
+
["[", "]"],
|
|
347
|
+
["{", "}"]
|
|
348
|
+
]);
|
|
349
|
+
/**
|
|
350
|
+
* `tokens` as component values. Only the innermost open block's own closer
|
|
351
|
+
* closes it; any other closer stays a token, and a block left open closes at
|
|
352
|
+
* the end, as CSS reads them.
|
|
353
|
+
*/
|
|
354
|
+
function componentValues(tokens) {
|
|
355
|
+
const root = [];
|
|
356
|
+
const open = [];
|
|
357
|
+
for (const token of tokens) {
|
|
358
|
+
const top = open.at(-1);
|
|
359
|
+
if (top !== void 0 && token.type === top.closer) {
|
|
360
|
+
top.block.close = token;
|
|
361
|
+
top.block.end = token.end;
|
|
362
|
+
open.pop();
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
const list = top === void 0 ? root : top.block.children;
|
|
366
|
+
const closer = CLOSERS.get(token.type);
|
|
367
|
+
if (closer === void 0) list.push({
|
|
368
|
+
kind: "token",
|
|
369
|
+
token,
|
|
370
|
+
start: token.start,
|
|
371
|
+
end: token.end
|
|
372
|
+
});
|
|
373
|
+
else {
|
|
374
|
+
const block = {
|
|
375
|
+
kind: "block",
|
|
376
|
+
open: token,
|
|
377
|
+
close: void 0,
|
|
378
|
+
children: [],
|
|
379
|
+
start: token.start,
|
|
380
|
+
end: token.end
|
|
381
|
+
};
|
|
382
|
+
list.push(block);
|
|
383
|
+
open.push({
|
|
384
|
+
block,
|
|
385
|
+
closer
|
|
152
386
|
});
|
|
153
|
-
at += ident?.length ?? 1;
|
|
154
387
|
}
|
|
155
|
-
return nested ? void 0 : out;
|
|
156
388
|
}
|
|
157
|
-
|
|
389
|
+
for (let i = open.length - 1; i >= 0; i -= 1) {
|
|
390
|
+
const { block } = open[i];
|
|
391
|
+
block.end = Math.max(block.end, block.children.at(-1)?.end ?? block.end);
|
|
392
|
+
}
|
|
393
|
+
return root;
|
|
158
394
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
395
|
+
/** Whether `test` holds for a value at any depth. */
|
|
396
|
+
function someValue(values, test) {
|
|
397
|
+
return values.some((value) => test(value) || value.kind === "block" && someValue(value.children, test));
|
|
162
398
|
}
|
|
163
|
-
|
|
164
|
-
|
|
399
|
+
/** Whether `value` is whitespace. */
|
|
400
|
+
const isSpace = (value) => value?.kind === "token" && value.token.type === "whitespace";
|
|
401
|
+
/**
|
|
402
|
+
* Every `var()` call in `values` at any depth, fallbacks included, outer
|
|
403
|
+
* calls first: a function whose decoded name is `var` in any case, and whose
|
|
404
|
+
* first argument is a custom-property name.
|
|
405
|
+
*/
|
|
406
|
+
function variableReads(values) {
|
|
407
|
+
const reads = [];
|
|
408
|
+
const visit = (list) => {
|
|
409
|
+
for (const value of list) {
|
|
410
|
+
if (value.kind !== "block") continue;
|
|
411
|
+
if (value.open.type === "function" && /^var$/i.test(value.open.value)) {
|
|
412
|
+
const [name, comma, first] = value.children.filter((c) => !isSpace(c));
|
|
413
|
+
if (name?.kind === "token" && name.token.type === "ident" && name.token.value.startsWith("--")) {
|
|
414
|
+
const end = value.close?.start ?? value.end;
|
|
415
|
+
reads.push({
|
|
416
|
+
name: name.token.value,
|
|
417
|
+
start: value.start,
|
|
418
|
+
end: value.end,
|
|
419
|
+
fallback: comma?.kind === "token" && comma.token.type === "," ? {
|
|
420
|
+
start: comma.end,
|
|
421
|
+
first: first?.start ?? end,
|
|
422
|
+
end
|
|
423
|
+
} : void 0
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
visit(value.children);
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
visit(values);
|
|
431
|
+
return reads;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* The `!important` `css` ends with; kept in step with `important_priority`
|
|
435
|
+
* in the Rust engine. The value has one `!`, a delim token, so a `!` inside
|
|
436
|
+
* a string, a `url()` or an escape is no priority, and neither is a lone `!`
|
|
437
|
+
* or `!important`.
|
|
438
|
+
*/
|
|
439
|
+
function importantPriority(css) {
|
|
440
|
+
const trimmed = css.trimEnd();
|
|
441
|
+
const endsImportant = trimmed.length >= 9 && /^important$/i.test(trimmed.slice(-9));
|
|
442
|
+
if (!css.endsWith("!") && !endsImportant) return void 0;
|
|
443
|
+
const tokens = tokenize(css);
|
|
444
|
+
let bang = -1;
|
|
445
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
446
|
+
if (tokens[index].type !== "delim" || tokens[index].value !== "!") continue;
|
|
447
|
+
if (bang !== -1) return void 0;
|
|
448
|
+
bang = index;
|
|
449
|
+
}
|
|
450
|
+
if (bang === -1) return void 0;
|
|
451
|
+
if (bang === tokens.length - 1) {
|
|
452
|
+
const previous = tokens[bang - 1];
|
|
453
|
+
return previous !== void 0 && previous.type !== "whitespace" && previous.end === tokens[bang].start ? {
|
|
454
|
+
spelling: "shorthand",
|
|
455
|
+
end: previous.end
|
|
456
|
+
} : void 0;
|
|
457
|
+
}
|
|
458
|
+
const after = tokens.slice(bang + 1).filter((t) => t.type !== "whitespace");
|
|
459
|
+
if (after.length !== 1 || after[0].type !== "ident" || !/^important$/i.test(after[0].value)) return;
|
|
460
|
+
const value = tokens.slice(0, bang).findLast((t) => t.type !== "whitespace");
|
|
461
|
+
return value && {
|
|
462
|
+
spelling: "important",
|
|
463
|
+
end: value.end
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
//#endregion
|
|
467
|
+
//#region src/registration-values.ts
|
|
468
|
+
/**
|
|
469
|
+
* Reads a custom-property registration's initial value as CSS component
|
|
470
|
+
* values: what it substitutes, which units make it depend on context, and
|
|
471
|
+
* what an all-absolute math function computes to. Tokens come from the shared
|
|
472
|
+
* CSS reader, so comments are trivia, quoted strings and `url()` stay text,
|
|
473
|
+
* and escaped names are decoded.
|
|
474
|
+
*/
|
|
475
|
+
const asciiLower = (text) => text.replace(/[A-Z]/g, (letter) => letter.toLowerCase());
|
|
476
|
+
const parse = (text) => componentValues(tokenize(text));
|
|
477
|
+
/** A function's ASCII-lowercase name, `''` for a parenthesized block, and
|
|
478
|
+
* `undefined` for anything else. */
|
|
479
|
+
function functionName(value) {
|
|
480
|
+
if (value?.kind !== "block") return void 0;
|
|
481
|
+
if (value.open.type === "function") return asciiLower(value.open.value);
|
|
482
|
+
return value.open.type === "(" ? "" : void 0;
|
|
165
483
|
}
|
|
166
484
|
const SUBSTITUTIONS = /* @__PURE__ */ new Set([
|
|
167
485
|
"var",
|
|
@@ -169,12 +487,9 @@ const SUBSTITUTIONS = /* @__PURE__ */ new Set([
|
|
|
169
487
|
"attr"
|
|
170
488
|
]);
|
|
171
489
|
/** Whether `value` substitutes another value (`var()`, `env()`, `attr()`),
|
|
172
|
-
* which no registration's initial value may.
|
|
173
|
-
* substituting when it names one of them. */
|
|
490
|
+
* which no registration's initial value may. */
|
|
174
491
|
function substitutesValue(value) {
|
|
175
|
-
|
|
176
|
-
if (values === void 0) return /\b(?:var|env|attr)\(/i.test(value);
|
|
177
|
-
return some(values, (component) => component.kind === "function" && SUBSTITUTIONS.has(component.name));
|
|
492
|
+
return someValue(parse(value), (component) => SUBSTITUTIONS.has(functionName(component) ?? ""));
|
|
178
493
|
}
|
|
179
494
|
const CONTEXT_UNITS = /* @__PURE__ */ new Set([
|
|
180
495
|
"em",
|
|
@@ -199,9 +514,7 @@ const CONTEXT_UNITS = /* @__PURE__ */ new Set([
|
|
|
199
514
|
/** Whether `value` uses a font- or container-relative unit, inside math
|
|
200
515
|
* functions too, which makes a typed initial value depend on context. */
|
|
201
516
|
function dependsOnContext(value) {
|
|
202
|
-
|
|
203
|
-
if (values === void 0) return false;
|
|
204
|
-
return some(values, (component) => component.kind === "numeric" && CONTEXT_UNITS.has(component.unit));
|
|
517
|
+
return someValue(parse(value), (component) => component.kind === "token" && component.token.type === "dimension" && CONTEXT_UNITS.has(asciiLower(component.token.value)));
|
|
205
518
|
}
|
|
206
519
|
/** Absolute units, by the canonical unit each converts to. */
|
|
207
520
|
const ABSOLUTE_UNITS = /* @__PURE__ */ new Map([
|
|
@@ -236,94 +549,157 @@ const MATH_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
|
236
549
|
"max",
|
|
237
550
|
"clamp"
|
|
238
551
|
]);
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
function
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
552
|
+
const operatorOf = (value) => value?.kind === "token" && value.token.type === "delim" && "+-*/".includes(value.token.value) ? value.token.value : void 0;
|
|
553
|
+
/**
|
|
554
|
+
* A math function's argument read as CSS calculation syntax, or what is out
|
|
555
|
+
* of place: `+` and `-` need whitespace on both sides, every operator needs a
|
|
556
|
+
* value on each side, and two values need an operator between them.
|
|
557
|
+
*/
|
|
558
|
+
function readCalculation(values) {
|
|
559
|
+
let start = 0;
|
|
560
|
+
let end = values.length;
|
|
561
|
+
while (start < end && isSpace(values[start])) start += 1;
|
|
562
|
+
while (end > start && isSpace(values[end - 1])) end -= 1;
|
|
563
|
+
if (start === end) return { invalid: "a calculation needs a value" };
|
|
564
|
+
let at = start;
|
|
565
|
+
const skipSpace = () => {
|
|
566
|
+
while (at < end && isSpace(values[at])) at += 1;
|
|
249
567
|
};
|
|
250
|
-
|
|
568
|
+
const operand = () => {
|
|
569
|
+
skipSpace();
|
|
251
570
|
const value = values[at];
|
|
571
|
+
if (at >= end || operatorOf(value) !== void 0) return { invalid: "an operator needs a value on each side" };
|
|
252
572
|
at += 1;
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
let left = factor();
|
|
269
|
-
for (let op = operator("*", "/"); op && left; op = operator("*", "/")) {
|
|
270
|
-
const right = factor();
|
|
271
|
-
if (right === void 0) return void 0;
|
|
272
|
-
if (op === "*") {
|
|
273
|
-
if (left.type !== "number" && right.type !== "number") return void 0;
|
|
274
|
-
left = {
|
|
275
|
-
type: left.type === "number" ? right.type : left.type,
|
|
276
|
-
value: left.value * right.value
|
|
277
|
-
};
|
|
278
|
-
} else if (right.type === "number") left = {
|
|
279
|
-
type: left.type,
|
|
280
|
-
value: left.value / right.value
|
|
281
|
-
};
|
|
282
|
-
else if (right.type === left.type) left = {
|
|
283
|
-
type: "number",
|
|
284
|
-
value: left.value / right.value
|
|
285
|
-
};
|
|
286
|
-
else return;
|
|
287
|
-
}
|
|
288
|
-
return left;
|
|
289
|
-
}
|
|
290
|
-
function sum() {
|
|
291
|
-
let left = product();
|
|
292
|
-
for (let op = operator("+", "-"); op && left; op = operator("+", "-")) {
|
|
293
|
-
const right = product();
|
|
294
|
-
if (right === void 0 || right.type !== left.type) return void 0;
|
|
573
|
+
return { value };
|
|
574
|
+
};
|
|
575
|
+
const product = () => {
|
|
576
|
+
let left = operand();
|
|
577
|
+
while (!("invalid" in left)) {
|
|
578
|
+
const before = at;
|
|
579
|
+
skipSpace();
|
|
580
|
+
const op = operatorOf(values[at]);
|
|
581
|
+
if (op !== "*" && op !== "/") {
|
|
582
|
+
at = before;
|
|
583
|
+
return left;
|
|
584
|
+
}
|
|
585
|
+
at += 1;
|
|
586
|
+
const right = operand();
|
|
587
|
+
if ("invalid" in right) return right;
|
|
295
588
|
left = {
|
|
296
|
-
|
|
297
|
-
|
|
589
|
+
op,
|
|
590
|
+
left,
|
|
591
|
+
right
|
|
298
592
|
};
|
|
299
593
|
}
|
|
300
594
|
return left;
|
|
595
|
+
};
|
|
596
|
+
let sum = product();
|
|
597
|
+
while (!("invalid" in sum) && at < end) {
|
|
598
|
+
const spaced = isSpace(values[at]);
|
|
599
|
+
skipSpace();
|
|
600
|
+
const op = operatorOf(values[at]);
|
|
601
|
+
if (op !== "+" && op !== "-") return { invalid: "two values need an operator between them" };
|
|
602
|
+
if (at + 1 >= end) return { invalid: "an operator needs a value on each side" };
|
|
603
|
+
if (!spaced || !isSpace(values[at + 1])) return { invalid: "\"+\" and \"-\" need whitespace on both sides" };
|
|
604
|
+
at += 1;
|
|
605
|
+
const right = product();
|
|
606
|
+
if ("invalid" in right) return right;
|
|
607
|
+
sum = {
|
|
608
|
+
op,
|
|
609
|
+
left: sum,
|
|
610
|
+
right
|
|
611
|
+
};
|
|
301
612
|
}
|
|
302
|
-
|
|
303
|
-
return at === values.length ? result : void 0;
|
|
613
|
+
return sum;
|
|
304
614
|
}
|
|
615
|
+
/** Both sides evaluated, so a broken expression on either side is reported
|
|
616
|
+
* even where the other cannot be evaluated. */
|
|
617
|
+
function combine(op, left, right) {
|
|
618
|
+
if (left !== void 0 && "invalid" in left) return left;
|
|
619
|
+
if (right !== void 0 && "invalid" in right) return right;
|
|
620
|
+
if (left === void 0 || right === void 0) return void 0;
|
|
621
|
+
if (op === "+" || op === "-") {
|
|
622
|
+
if (left.type !== right.type) return void 0;
|
|
623
|
+
return {
|
|
624
|
+
type: left.type,
|
|
625
|
+
value: op === "+" ? left.value + right.value : left.value - right.value
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
if (op === "*") {
|
|
629
|
+
if (left.type !== "number" && right.type !== "number") return void 0;
|
|
630
|
+
return {
|
|
631
|
+
type: left.type === "number" ? right.type : left.type,
|
|
632
|
+
value: left.value * right.value
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
if (right.type === "number") return {
|
|
636
|
+
type: left.type,
|
|
637
|
+
value: left.value / right.value
|
|
638
|
+
};
|
|
639
|
+
return right.type === left.type ? {
|
|
640
|
+
type: "number",
|
|
641
|
+
value: left.value / right.value
|
|
642
|
+
} : void 0;
|
|
643
|
+
}
|
|
644
|
+
/** The calculation constants, by their ASCII-lowercase name. */
|
|
645
|
+
const CONSTANTS = /* @__PURE__ */ new Map([
|
|
646
|
+
["e", Math.E],
|
|
647
|
+
["pi", Math.PI],
|
|
648
|
+
["infinity", Infinity],
|
|
649
|
+
["-infinity", -Infinity],
|
|
650
|
+
["nan", NaN]
|
|
651
|
+
]);
|
|
652
|
+
function evaluate(node) {
|
|
653
|
+
if ("op" in node) return combine(node.op, evaluate(node.left), evaluate(node.right));
|
|
654
|
+
const { value } = node;
|
|
655
|
+
if (value.kind === "block") return mathFunction(value);
|
|
656
|
+
const { token } = value;
|
|
657
|
+
if (token.type === "number") return {
|
|
658
|
+
type: "number",
|
|
659
|
+
value: token.number
|
|
660
|
+
};
|
|
661
|
+
if (token.type === "dimension") {
|
|
662
|
+
const unit = ABSOLUTE_UNITS.get(asciiLower(token.value));
|
|
663
|
+
return unit && {
|
|
664
|
+
type: unit[0],
|
|
665
|
+
value: token.number * unit[1]
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
const constant = token.type === "ident" ? CONSTANTS.get(asciiLower(token.value)) : void 0;
|
|
669
|
+
return constant === void 0 ? void 0 : {
|
|
670
|
+
type: "number",
|
|
671
|
+
value: constant
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
/** `calc()`, `min()`, `max()` and `clamp()` over numbers and absolute
|
|
675
|
+
* dimensions, nested ones and parenthesized blocks included. */
|
|
305
676
|
function mathFunction(value) {
|
|
306
|
-
|
|
307
|
-
if (!MATH_FUNCTIONS.has(
|
|
677
|
+
const name = functionName(value);
|
|
678
|
+
if (name === void 0 || name !== "" && !MATH_FUNCTIONS.has(name)) return;
|
|
679
|
+
const parts = [[]];
|
|
680
|
+
for (const arg of value.children) if (arg.kind === "token" && arg.token.type === ",") parts.push([]);
|
|
681
|
+
else parts[parts.length - 1].push(arg);
|
|
682
|
+
if ((name === "" || name === "calc") && parts.length > 1) return { invalid: "commas separate only the arguments of min(), max() and clamp()" };
|
|
308
683
|
const args = [];
|
|
309
|
-
let
|
|
310
|
-
for (
|
|
311
|
-
const
|
|
312
|
-
if (
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
684
|
+
let unsupported = false;
|
|
685
|
+
for (const part of parts) {
|
|
686
|
+
const calculation = readCalculation(part);
|
|
687
|
+
if ("invalid" in calculation) return calculation;
|
|
688
|
+
const computed = evaluate(calculation);
|
|
689
|
+
if (computed !== void 0 && "invalid" in computed) return computed;
|
|
690
|
+
if (computed === void 0) unsupported = true;
|
|
691
|
+
else args.push(computed);
|
|
318
692
|
}
|
|
319
|
-
|
|
320
|
-
if (
|
|
693
|
+
if (unsupported) return void 0;
|
|
694
|
+
if (name === "" || name === "calc") return args[0];
|
|
695
|
+
const type = args[0].type;
|
|
696
|
+
if (args.some((arg) => arg.type !== type)) return void 0;
|
|
321
697
|
const values = args.map((arg) => arg.value);
|
|
322
|
-
if (
|
|
698
|
+
if (name === "min") return {
|
|
323
699
|
type,
|
|
324
700
|
value: Math.min(...values)
|
|
325
701
|
};
|
|
326
|
-
if (
|
|
702
|
+
if (name === "max") return {
|
|
327
703
|
type,
|
|
328
704
|
value: Math.max(...values)
|
|
329
705
|
};
|
|
@@ -349,32 +725,39 @@ const ACCEPTING_SYNTAX_TYPES = /* @__PURE__ */ new Map([
|
|
|
349
725
|
* `min()`, `max()`, `clamp()`) over numbers and absolute units, such as
|
|
350
726
|
* `calc(1px / 1px)` → `1`. The browser computes the same value, and CSS
|
|
351
727
|
* tooling that cannot parse the function in that position reads the result.
|
|
352
|
-
*
|
|
353
|
-
*
|
|
728
|
+
* The syntax's alternatives are tried in their declared order, and an
|
|
729
|
+
* `<integer>` one rounds a number result to the nearest integer, halves
|
|
730
|
+
* toward positive infinity, as CSS does. `undefined` for anything else, and
|
|
731
|
+
* for the universal syntax, whose initial value is kept as written.
|
|
354
732
|
*/
|
|
355
733
|
function foldInitialValue(syntax, initialValue) {
|
|
356
734
|
if (syntax.trim() === "*") return void 0;
|
|
357
|
-
const values = parse(initialValue);
|
|
358
|
-
const only = values
|
|
359
|
-
if (only?.kind !== "
|
|
735
|
+
const values = parse(initialValue).filter((value) => !isSpace(value));
|
|
736
|
+
const only = values.length === 1 ? values[0] : void 0;
|
|
737
|
+
if (only?.kind !== "block" || !MATH_FUNCTIONS.has(functionName(only) ?? "")) return;
|
|
360
738
|
const computed = mathFunction(only);
|
|
361
739
|
if (computed === void 0) return void 0;
|
|
362
|
-
|
|
740
|
+
if ("invalid" in computed) return {
|
|
741
|
+
kind: "invalid",
|
|
742
|
+
reason: computed.invalid
|
|
743
|
+
};
|
|
744
|
+
if (!Number.isFinite(computed.value)) return { kind: "not-finite" };
|
|
745
|
+
for (const component of syntax.split("|")) {
|
|
363
746
|
const type = /<([a-z-]+)>/.exec(component)?.[1];
|
|
364
|
-
if (type === void 0 || ACCEPTING_SYNTAX_TYPES.get(type) !== computed.type)
|
|
365
|
-
return
|
|
366
|
-
|
|
747
|
+
if (type === void 0 || ACCEPTING_SYNTAX_TYPES.get(type) !== computed.type) continue;
|
|
748
|
+
return {
|
|
749
|
+
kind: "folded",
|
|
750
|
+
text: type === "integer" ? BigInt(Math.round(computed.value)).toString() : written(computed)
|
|
751
|
+
};
|
|
752
|
+
}
|
|
367
753
|
return {
|
|
368
|
-
|
|
369
|
-
|
|
754
|
+
kind: "rejected",
|
|
755
|
+
text: written(computed),
|
|
370
756
|
type: computed.type
|
|
371
757
|
};
|
|
372
758
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
function registeredInitialValue(syntax, initialValue) {
|
|
376
|
-
const folded = foldInitialValue(syntax, initialValue);
|
|
377
|
-
return folded?.accepted ? folded.text : initialValue;
|
|
759
|
+
function written(computed) {
|
|
760
|
+
return `${Number(computed.value.toPrecision(12))}${CANONICAL_UNITS[computed.type]}`;
|
|
378
761
|
}
|
|
379
762
|
//#endregion
|
|
380
|
-
export { SHORTHAND_PROPERTIES, UNITLESS_PROPERTIES, dependsOnContext, foldInitialValue, isUnitlessProperty,
|
|
763
|
+
export { SHORTHAND_PROPERTIES, UNITLESS_PROPERTIES, componentValues, decodedIdentifier, dependsOnContext, foldInitialValue, identifierAt, importantPriority, isSpace, isUnitlessProperty, someValue, substitutesValue, tokenize, variableReads };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@animus-ui/properties",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.22",
|
|
4
4
|
"description": "CSS property classification data — unitless set, shorthand list, type re-exports",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"animus",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "bun run build:ts",
|
|
39
|
-
"build:ts": "tsdown
|
|
39
|
+
"build:ts": "tsdown",
|
|
40
40
|
"compile": "tsc --noEmit"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {},
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reads a custom-property registration's initial value as CSS component
|
|
3
|
-
* values: what it substitutes, which units make it depend on context, and
|
|
4
|
-
* what an all-absolute math function computes to. Quoted text and `url()`
|
|
5
|
-
* stay text.
|
|
6
|
-
*/
|
|
7
|
-
/** Whether `value` substitutes another value (`var()`, `env()`, `attr()`),
|
|
8
|
-
* which no registration's initial value may. Unparseable text is read as
|
|
9
|
-
* substituting when it names one of them. */
|
|
10
|
-
export declare function substitutesValue(value: string): boolean;
|
|
11
|
-
/** Whether `value` uses a font- or container-relative unit, inside math
|
|
12
|
-
* functions too, which makes a typed initial value depend on context. */
|
|
13
|
-
export declare function dependsOnContext(value: string): boolean;
|
|
14
|
-
/** What an all-absolute math expression computes to. */
|
|
15
|
-
interface Computed {
|
|
16
|
-
type: 'number' | 'length' | 'angle' | 'time' | 'resolution';
|
|
17
|
-
value: number;
|
|
18
|
-
}
|
|
19
|
-
/** A math initial value folded to its computed value. */
|
|
20
|
-
export interface FoldedInitialValue {
|
|
21
|
-
/** The computed value as CSS text, such as `1`, `16px` or `90deg`. */
|
|
22
|
-
text: string;
|
|
23
|
-
/** Whether the registration's syntax accepts it. */
|
|
24
|
-
accepted: boolean;
|
|
25
|
-
/** The computed value's data type, for a diagnostic. */
|
|
26
|
-
type: Computed['type'];
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* The computed value of an initial value that is one math function (`calc()`,
|
|
30
|
-
* `min()`, `max()`, `clamp()`) over numbers and absolute units, such as
|
|
31
|
-
* `calc(1px / 1px)` → `1`. The browser computes the same value, and CSS
|
|
32
|
-
* tooling that cannot parse the function in that position reads the result.
|
|
33
|
-
* `undefined` for anything else, and for the universal syntax, whose initial
|
|
34
|
-
* value is kept as written.
|
|
35
|
-
*/
|
|
36
|
-
export declare function foldInitialValue(syntax: string, initialValue: string): FoldedInitialValue | undefined;
|
|
37
|
-
/** An initial value folded when its syntax accepts the computed value, and
|
|
38
|
-
* unchanged otherwise: what a registration records and emits. */
|
|
39
|
-
export declare function registeredInitialValue(syntax: string, initialValue: string): string;
|
|
40
|
-
export {};
|
|
41
|
-
//# sourceMappingURL=registration-values.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"registration-values.d.ts","sourceRoot":"","sources":["../src/registration-values.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAyFH;;8CAE8C;AAC9C,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAQvD;AAuBD;0EAC0E;AAC1E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAQvD;AAED,wDAAwD;AACxD,UAAU,QAAQ;IAChB,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,YAAY,CAAC;IAC5D,KAAK,EAAE,MAAM,CAAC;CACf;AAwID,yDAAyD;AACzD,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,QAAQ,EAAE,OAAO,CAAC;IAClB,wDAAwD;IACxD,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GACnB,kBAAkB,GAAG,SAAS,CAyBhC;AAED;kEACkE;AAClE,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GACnB,MAAM,CAGR"}
|
package/dist/shorthands.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Covers only shorthands registered in the prop system, not all CSS shorthands.
|
|
3
|
-
*/
|
|
4
|
-
export declare const SHORTHAND_PROPERTIES: readonly ['border', 'borderTop', 'borderBottom', 'borderLeft', 'borderRight', 'borderWidth', 'borderStyle', 'borderColor', 'background', 'flex', 'margin', 'padding', 'transition', 'gap', 'grid', 'gridArea', 'gridColumn', 'gridRow', 'gridTemplate', 'overflow'];
|
|
5
|
-
//# sourceMappingURL=shorthands.d.ts.map
|
package/dist/shorthands.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"shorthands.d.ts","sourceRoot":"","sources":["../src/shorthands.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,oBAAoB,YAC/B,QAAQ,EACR,WAAW,EACX,cAAc,EACd,YAAY,EACZ,aAAa,EACb,aAAa,EACb,aAAa,EACb,aAAa,EACb,YAAY,EACZ,MAAM,EACN,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,KAAK,EACL,MAAM,EACN,UAAU,EACV,YAAY,EACZ,SAAS,EACT,cAAc,EACd,UAAU,CACF,CAAC"}
|
package/dist/unitless.d.ts
DELETED
package/dist/unitless.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"unitless.d.ts","sourceRoot":"","sources":["../src/unitless.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,aAgD9B,CAAC;AAUH,8DAA8D;AAC9D,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE5D"}
|