@animus-ui/properties 0.1.20 → 0.1.21

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 CHANGED
@@ -1,5 +1,145 @@
1
- export { SHORTHAND_PROPERTIES } from './shorthands';
2
- export { isUnitlessProperty, UNITLESS_PROPERTIES } from './unitless';
3
- export { dependsOnContext, foldInitialValue, registeredInitialValue, substitutesValue, } from './registration-values.js';
4
- export type { FoldedInitialValue } from './registration-values.js';
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
+ //#endregion
89
+ //#region src/registration-values.d.ts
90
+ /**
91
+ * Reads a custom-property registration's initial value as CSS component
92
+ * values: what it substitutes, which units make it depend on context, and
93
+ * what an all-absolute math function computes to. Tokens come from the shared
94
+ * CSS reader, so comments are trivia, quoted strings and `url()` stay text,
95
+ * and escaped names are decoded.
96
+ */
97
+ /** Whether `value` substitutes another value (`var()`, `env()`, `attr()`),
98
+ * which no registration's initial value may. */
99
+ export declare function substitutesValue(value: string): boolean;
100
+ /** Whether `value` uses a font- or container-relative unit, inside math
101
+ * functions too, which makes a typed initial value depend on context. */
102
+ export declare function dependsOnContext(value: string): boolean;
103
+ /** What an all-absolute math expression computes to. */
104
+ interface Computed {
105
+ type: 'number' | 'length' | 'angle' | 'time' | 'resolution';
106
+ value: number;
107
+ }
108
+ /** What a math initial value folds to. */
109
+ type FoldedInitialValue =
110
+ /** The computed value the syntax accepts, as CSS text such as `1`, `16px`
111
+ * or `90deg`. */
112
+ {
113
+ kind: 'folded';
114
+ text: string;
115
+ } |
116
+ /** A computed value no syntax alternative accepts. */
117
+ {
118
+ kind: 'rejected';
119
+ text: string;
120
+ type: Computed['type'];
121
+ } |
122
+ /** Math that breaks CSS calculation syntax, which browsers ignore. */
123
+ {
124
+ kind: 'invalid';
125
+ reason: string;
126
+ } |
127
+ /** An infinite or NaN result: browsers clamp it, and it has no plain
128
+ * value to write. */
129
+ {
130
+ kind: 'not-finite';
131
+ };
132
+ /**
133
+ * The computed value of an initial value that is one math function (`calc()`,
134
+ * `min()`, `max()`, `clamp()`) over numbers and absolute units, such as
135
+ * `calc(1px / 1px)` → `1`. The browser computes the same value, and CSS
136
+ * tooling that cannot parse the function in that position reads the result.
137
+ * The syntax's alternatives are tried in their declared order, and an
138
+ * `<integer>` one rounds a number result to the nearest integer, halves
139
+ * toward positive infinity, as CSS does. `undefined` for anything else, and
140
+ * for the universal syntax, whose initial value is kept as written.
141
+ */
142
+ export declare function foldInitialValue(syntax: string, initialValue: string): FoldedInitialValue | undefined;
143
+ //#endregion
144
+ export type { ComponentValue, CssToken, FoldedInitialValue, VariableRead };
5
145
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC"}
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;;;;;;;;;;;;wBCvWa,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,382 @@ 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.replace(/-([a-z])/g, (_, char) => char.toUpperCase()));
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/registration-values.ts
87
- const NUMBER = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?/i;
88
- const IDENT = /^(?:--|-?[a-zA-Z_])[\w-]*/;
89
- /** The component values of `text`; `undefined` when it does not parse. */
90
- function parse(text) {
91
- let at = 0;
92
- function values(nested) {
93
- const out = [];
94
- while (at < text.length) {
95
- const char = text[at];
96
- if (/\s/.test(char)) {
97
- at += 1;
98
- continue;
99
- }
100
- if (char === ")") {
101
- at += 1;
102
- return nested ? out : void 0;
103
- }
104
- if (char === "\"" || char === "'") {
105
- const end = stringEnd(text, at);
106
- if (end === void 0) return void 0;
107
- out.push({
108
- kind: "token",
109
- text: text.slice(at, end)
110
- });
111
- at = end;
112
- continue;
113
- }
114
- const rest = text.slice(at);
115
- const ident = IDENT.exec(rest)?.[0];
116
- if (char === "(" || ident && rest[ident.length] === "(") {
117
- const name = (ident ?? "").toLowerCase();
118
- at += (ident?.length ?? 0) + 1;
119
- if (name === "url") {
120
- const end = text.indexOf(")", at);
121
- if (end < 0) return void 0;
122
- out.push({
123
- kind: "token",
124
- text: "url()"
125
- });
126
- at = end + 1;
127
- continue;
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
- out.push({
150
- kind: "token",
151
- text: ident ?? char
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
- return values(false);
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
- function stringEnd(text, start) {
160
- for (let at = start + 1; at < text.length; at += 1) if (text[at] === "\\") at += 1;
161
- else if (text[at] === text[start]) return at + 1;
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
- function some(values, test) {
164
- return values.some((value) => test(value) || value.kind === "function" && some(value.args, test));
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
+ //#endregion
434
+ //#region src/registration-values.ts
435
+ /**
436
+ * Reads a custom-property registration's initial value as CSS component
437
+ * values: what it substitutes, which units make it depend on context, and
438
+ * what an all-absolute math function computes to. Tokens come from the shared
439
+ * CSS reader, so comments are trivia, quoted strings and `url()` stay text,
440
+ * and escaped names are decoded.
441
+ */
442
+ const asciiLower = (text) => text.replace(/[A-Z]/g, (letter) => letter.toLowerCase());
443
+ const parse = (text) => componentValues(tokenize(text));
444
+ /** A function's ASCII-lowercase name, `''` for a parenthesized block, and
445
+ * `undefined` for anything else. */
446
+ function functionName(value) {
447
+ if (value?.kind !== "block") return void 0;
448
+ if (value.open.type === "function") return asciiLower(value.open.value);
449
+ return value.open.type === "(" ? "" : void 0;
165
450
  }
166
451
  const SUBSTITUTIONS = /* @__PURE__ */ new Set([
167
452
  "var",
@@ -169,12 +454,9 @@ const SUBSTITUTIONS = /* @__PURE__ */ new Set([
169
454
  "attr"
170
455
  ]);
171
456
  /** Whether `value` substitutes another value (`var()`, `env()`, `attr()`),
172
- * which no registration's initial value may. Unparseable text is read as
173
- * substituting when it names one of them. */
457
+ * which no registration's initial value may. */
174
458
  function substitutesValue(value) {
175
- const values = parse(value);
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));
459
+ return someValue(parse(value), (component) => SUBSTITUTIONS.has(functionName(component) ?? ""));
178
460
  }
179
461
  const CONTEXT_UNITS = /* @__PURE__ */ new Set([
180
462
  "em",
@@ -199,9 +481,7 @@ const CONTEXT_UNITS = /* @__PURE__ */ new Set([
199
481
  /** Whether `value` uses a font- or container-relative unit, inside math
200
482
  * functions too, which makes a typed initial value depend on context. */
201
483
  function dependsOnContext(value) {
202
- const values = parse(value);
203
- if (values === void 0) return false;
204
- return some(values, (component) => component.kind === "numeric" && CONTEXT_UNITS.has(component.unit));
484
+ return someValue(parse(value), (component) => component.kind === "token" && component.token.type === "dimension" && CONTEXT_UNITS.has(asciiLower(component.token.value)));
205
485
  }
206
486
  /** Absolute units, by the canonical unit each converts to. */
207
487
  const ABSOLUTE_UNITS = /* @__PURE__ */ new Map([
@@ -236,94 +516,157 @@ const MATH_FUNCTIONS = /* @__PURE__ */ new Set([
236
516
  "max",
237
517
  "clamp"
238
518
  ]);
239
- /** Evaluates a math function's arguments: `+ - * /` over numbers and
240
- * absolute dimensions, nested `calc()`, `min()`, `max()` and `clamp()`. */
241
- function evaluate(values) {
242
- let at = 0;
243
- const operator = (...ops) => {
244
- const value = values[at];
245
- if (value?.kind === "token" && ops.includes(value.text)) {
246
- at += 1;
247
- return value.text;
248
- }
519
+ const operatorOf = (value) => value?.kind === "token" && value.token.type === "delim" && "+-*/".includes(value.token.value) ? value.token.value : void 0;
520
+ /**
521
+ * A math function's argument read as CSS calculation syntax, or what is out
522
+ * of place: `+` and `-` need whitespace on both sides, every operator needs a
523
+ * value on each side, and two values need an operator between them.
524
+ */
525
+ function readCalculation(values) {
526
+ let start = 0;
527
+ let end = values.length;
528
+ while (start < end && isSpace(values[start])) start += 1;
529
+ while (end > start && isSpace(values[end - 1])) end -= 1;
530
+ if (start === end) return { invalid: "a calculation needs a value" };
531
+ let at = start;
532
+ const skipSpace = () => {
533
+ while (at < end && isSpace(values[at])) at += 1;
249
534
  };
250
- function factor() {
535
+ const operand = () => {
536
+ skipSpace();
251
537
  const value = values[at];
538
+ if (at >= end || operatorOf(value) !== void 0) return { invalid: "an operator needs a value on each side" };
252
539
  at += 1;
253
- if (value === void 0) return void 0;
254
- if (value.kind === "numeric") {
255
- if (value.unit === "") return {
256
- type: "number",
257
- value: value.value
258
- };
259
- const unit = ABSOLUTE_UNITS.get(value.unit);
260
- return unit && {
261
- type: unit[0],
262
- value: value.value * unit[1]
263
- };
264
- }
265
- if (value.kind === "function") return mathFunction(value);
266
- }
267
- function product() {
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;
540
+ return { value };
541
+ };
542
+ const product = () => {
543
+ let left = operand();
544
+ while (!("invalid" in left)) {
545
+ const before = at;
546
+ skipSpace();
547
+ const op = operatorOf(values[at]);
548
+ if (op !== "*" && op !== "/") {
549
+ at = before;
550
+ return left;
551
+ }
552
+ at += 1;
553
+ const right = operand();
554
+ if ("invalid" in right) return right;
295
555
  left = {
296
- type: left.type,
297
- value: op === "+" ? left.value + right.value : left.value - right.value
556
+ op,
557
+ left,
558
+ right
298
559
  };
299
560
  }
300
561
  return left;
562
+ };
563
+ let sum = product();
564
+ while (!("invalid" in sum) && at < end) {
565
+ const spaced = isSpace(values[at]);
566
+ skipSpace();
567
+ const op = operatorOf(values[at]);
568
+ if (op !== "+" && op !== "-") return { invalid: "two values need an operator between them" };
569
+ if (at + 1 >= end) return { invalid: "an operator needs a value on each side" };
570
+ if (!spaced || !isSpace(values[at + 1])) return { invalid: "\"+\" and \"-\" need whitespace on both sides" };
571
+ at += 1;
572
+ const right = product();
573
+ if ("invalid" in right) return right;
574
+ sum = {
575
+ op,
576
+ left: sum,
577
+ right
578
+ };
301
579
  }
302
- const result = sum();
303
- return at === values.length ? result : void 0;
580
+ return sum;
304
581
  }
582
+ /** Both sides evaluated, so a broken expression on either side is reported
583
+ * even where the other cannot be evaluated. */
584
+ function combine(op, left, right) {
585
+ if (left !== void 0 && "invalid" in left) return left;
586
+ if (right !== void 0 && "invalid" in right) return right;
587
+ if (left === void 0 || right === void 0) return void 0;
588
+ if (op === "+" || op === "-") {
589
+ if (left.type !== right.type) return void 0;
590
+ return {
591
+ type: left.type,
592
+ value: op === "+" ? left.value + right.value : left.value - right.value
593
+ };
594
+ }
595
+ if (op === "*") {
596
+ if (left.type !== "number" && right.type !== "number") return void 0;
597
+ return {
598
+ type: left.type === "number" ? right.type : left.type,
599
+ value: left.value * right.value
600
+ };
601
+ }
602
+ if (right.type === "number") return {
603
+ type: left.type,
604
+ value: left.value / right.value
605
+ };
606
+ return right.type === left.type ? {
607
+ type: "number",
608
+ value: left.value / right.value
609
+ } : void 0;
610
+ }
611
+ /** The calculation constants, by their ASCII-lowercase name. */
612
+ const CONSTANTS = /* @__PURE__ */ new Map([
613
+ ["e", Math.E],
614
+ ["pi", Math.PI],
615
+ ["infinity", Infinity],
616
+ ["-infinity", -Infinity],
617
+ ["nan", NaN]
618
+ ]);
619
+ function evaluate(node) {
620
+ if ("op" in node) return combine(node.op, evaluate(node.left), evaluate(node.right));
621
+ const { value } = node;
622
+ if (value.kind === "block") return mathFunction(value);
623
+ const { token } = value;
624
+ if (token.type === "number") return {
625
+ type: "number",
626
+ value: token.number
627
+ };
628
+ if (token.type === "dimension") {
629
+ const unit = ABSOLUTE_UNITS.get(asciiLower(token.value));
630
+ return unit && {
631
+ type: unit[0],
632
+ value: token.number * unit[1]
633
+ };
634
+ }
635
+ const constant = token.type === "ident" ? CONSTANTS.get(asciiLower(token.value)) : void 0;
636
+ return constant === void 0 ? void 0 : {
637
+ type: "number",
638
+ value: constant
639
+ };
640
+ }
641
+ /** `calc()`, `min()`, `max()` and `clamp()` over numbers and absolute
642
+ * dimensions, nested ones and parenthesized blocks included. */
305
643
  function mathFunction(value) {
306
- if (value.name === "calc" || value.name === "") return evaluate(value.args);
307
- if (!MATH_FUNCTIONS.has(value.name)) return void 0;
644
+ const name = functionName(value);
645
+ if (name === void 0 || name !== "" && !MATH_FUNCTIONS.has(name)) return;
646
+ const parts = [[]];
647
+ for (const arg of value.children) if (arg.kind === "token" && arg.token.type === ",") parts.push([]);
648
+ else parts[parts.length - 1].push(arg);
649
+ if ((name === "" || name === "calc") && parts.length > 1) return { invalid: "commas separate only the arguments of min(), max() and clamp()" };
308
650
  const args = [];
309
- let start = 0;
310
- for (let at = 0; at <= value.args.length; at += 1) {
311
- const arg = value.args[at];
312
- if (at === value.args.length || arg?.kind === "token" && arg.text === ",") {
313
- const computed = evaluate(value.args.slice(start, at));
314
- if (computed === void 0) return void 0;
315
- args.push(computed);
316
- start = at + 1;
317
- }
651
+ let unsupported = false;
652
+ for (const part of parts) {
653
+ const calculation = readCalculation(part);
654
+ if ("invalid" in calculation) return calculation;
655
+ const computed = evaluate(calculation);
656
+ if (computed !== void 0 && "invalid" in computed) return computed;
657
+ if (computed === void 0) unsupported = true;
658
+ else args.push(computed);
318
659
  }
319
- const type = args[0]?.type;
320
- if (type === void 0 || args.some((arg) => arg.type !== type)) return;
660
+ if (unsupported) return void 0;
661
+ if (name === "" || name === "calc") return args[0];
662
+ const type = args[0].type;
663
+ if (args.some((arg) => arg.type !== type)) return void 0;
321
664
  const values = args.map((arg) => arg.value);
322
- if (value.name === "min") return {
665
+ if (name === "min") return {
323
666
  type,
324
667
  value: Math.min(...values)
325
668
  };
326
- if (value.name === "max") return {
669
+ if (name === "max") return {
327
670
  type,
328
671
  value: Math.max(...values)
329
672
  };
@@ -349,32 +692,39 @@ const ACCEPTING_SYNTAX_TYPES = /* @__PURE__ */ new Map([
349
692
  * `min()`, `max()`, `clamp()`) over numbers and absolute units, such as
350
693
  * `calc(1px / 1px)` → `1`. The browser computes the same value, and CSS
351
694
  * tooling that cannot parse the function in that position reads the result.
352
- * `undefined` for anything else, and for the universal syntax, whose initial
353
- * value is kept as written.
695
+ * The syntax's alternatives are tried in their declared order, and an
696
+ * `<integer>` one rounds a number result to the nearest integer, halves
697
+ * toward positive infinity, as CSS does. `undefined` for anything else, and
698
+ * for the universal syntax, whose initial value is kept as written.
354
699
  */
355
700
  function foldInitialValue(syntax, initialValue) {
356
701
  if (syntax.trim() === "*") return void 0;
357
- const values = parse(initialValue);
358
- const only = values?.length === 1 ? values[0] : void 0;
359
- if (only?.kind !== "function" || !MATH_FUNCTIONS.has(only.name)) return;
702
+ const values = parse(initialValue).filter((value) => !isSpace(value));
703
+ const only = values.length === 1 ? values[0] : void 0;
704
+ if (only?.kind !== "block" || !MATH_FUNCTIONS.has(functionName(only) ?? "")) return;
360
705
  const computed = mathFunction(only);
361
706
  if (computed === void 0) return void 0;
362
- const accepted = syntax.split("|").some((component) => {
707
+ if ("invalid" in computed) return {
708
+ kind: "invalid",
709
+ reason: computed.invalid
710
+ };
711
+ if (!Number.isFinite(computed.value)) return { kind: "not-finite" };
712
+ for (const component of syntax.split("|")) {
363
713
  const type = /<([a-z-]+)>/.exec(component)?.[1];
364
- if (type === void 0 || ACCEPTING_SYNTAX_TYPES.get(type) !== computed.type) return false;
365
- return type !== "integer" || Number.isInteger(computed.value);
366
- });
714
+ if (type === void 0 || ACCEPTING_SYNTAX_TYPES.get(type) !== computed.type) continue;
715
+ return {
716
+ kind: "folded",
717
+ text: type === "integer" ? BigInt(Math.round(computed.value)).toString() : written(computed)
718
+ };
719
+ }
367
720
  return {
368
- text: `${Number(computed.value.toPrecision(12))}${CANONICAL_UNITS[computed.type]}`,
369
- accepted,
721
+ kind: "rejected",
722
+ text: written(computed),
370
723
  type: computed.type
371
724
  };
372
725
  }
373
- /** An initial value folded when its syntax accepts the computed value, and
374
- * unchanged otherwise: what a registration records and emits. */
375
- function registeredInitialValue(syntax, initialValue) {
376
- const folded = foldInitialValue(syntax, initialValue);
377
- return folded?.accepted ? folded.text : initialValue;
726
+ function written(computed) {
727
+ return `${Number(computed.value.toPrecision(12))}${CANONICAL_UNITS[computed.type]}`;
378
728
  }
379
729
  //#endregion
380
- export { SHORTHAND_PROPERTIES, UNITLESS_PROPERTIES, dependsOnContext, foldInitialValue, isUnitlessProperty, registeredInitialValue, substitutesValue };
730
+ export { SHORTHAND_PROPERTIES, UNITLESS_PROPERTIES, componentValues, decodedIdentifier, dependsOnContext, foldInitialValue, identifierAt, 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.20",
3
+ "version": "0.1.21",
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 && tsc -p tsconfig.build.json",
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"}
@@ -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
@@ -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"}
@@ -1,4 +0,0 @@
1
- export declare const UNITLESS_PROPERTIES: Set<string>;
2
- /** Accepts either spelling: `line-height` or `lineHeight`. */
3
- export declare function isUnitlessProperty(property: string): boolean;
4
- //# sourceMappingURL=unitless.d.ts.map
@@ -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"}