design-token-library 0.3.1 → 0.4.0
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/CHANGELOG.md +32 -0
- package/dist/lib/css-reflector.d.ts +32 -24
- package/dist/lib/css-reflector.js +10 -26
- package/dist/lib/css-reflector.js.map +1 -1
- package/dist/lib/equality.d.ts +16 -0
- package/dist/lib/equality.js +90 -0
- package/dist/lib/equality.js.map +1 -0
- package/dist/lib/graph.d.ts +58 -0
- package/dist/lib/graph.js +104 -0
- package/dist/lib/graph.js.map +1 -0
- package/dist/lib/index.d.ts +4 -2
- package/dist/lib/index.js +2 -1
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/library.d.ts +221 -10
- package/dist/lib/library.js +1256 -258
- package/dist/lib/library.js.map +1 -1
- package/dist/lib/notifier.d.ts +0 -15
- package/dist/lib/notifier.js +0 -23
- package/dist/lib/notifier.js.map +1 -1
- package/dist/lib/parse-document.d.ts +21 -0
- package/dist/lib/parse-document.js +31 -0
- package/dist/lib/parse-document.js.map +1 -0
- package/dist/lib/parse-expression.d.ts +45 -0
- package/dist/lib/parse-expression.js +148 -0
- package/dist/lib/parse-expression.js.map +1 -0
- package/dist/lib/parse-extension.d.ts +24 -0
- package/dist/lib/parse-extension.js +68 -0
- package/dist/lib/parse-extension.js.map +1 -0
- package/dist/lib/parse-resolve.d.ts +16 -0
- package/dist/lib/parse-resolve.js +281 -0
- package/dist/lib/parse-resolve.js.map +1 -0
- package/dist/lib/parse-value.d.ts +35 -0
- package/dist/lib/parse-value.js +330 -0
- package/dist/lib/parse-value.js.map +1 -0
- package/dist/lib/parse.d.ts +103 -0
- package/dist/lib/parse.js +562 -0
- package/dist/lib/parse.js.map +1 -0
- package/dist/lib/queue.d.ts +13 -2
- package/dist/lib/queue.js +18 -2
- package/dist/lib/queue.js.map +1 -1
- package/dist/lib/ref.d.ts +18 -1
- package/dist/lib/ref.js +22 -4
- package/dist/lib/ref.js.map +1 -1
- package/dist/lib/standard-values.d.ts +50 -0
- package/dist/lib/standard-values.js +73 -0
- package/dist/lib/standard-values.js.map +1 -0
- package/dist/lib/tsconfig.tsbuildinfo +1 -1
- package/dist/lib/utilities.d.ts +6 -0
- package/dist/lib/utilities.js +7 -1
- package/dist/lib/utilities.js.map +1 -1
- package/package.json +5 -2
- package/dist/lib/watcher.d.ts +0 -12
- package/dist/lib/watcher.js +0 -25
- package/dist/lib/watcher.js.map +0 -1
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { isChildName, isGroupNode, isObject, isReference, isTokenNode, label, referencePath, setOwn, } from "./parse-document.js";
|
|
2
|
+
import { EXTENSION } from "./parse-extension.js";
|
|
3
|
+
/**
|
|
4
|
+
* Parses a JSON Pointer in URI fragment form (RFC 6901), such as
|
|
5
|
+
* `#/color/blue/$value`.
|
|
6
|
+
*/
|
|
7
|
+
const parsePointer = (pointer) => {
|
|
8
|
+
if (!pointer.startsWith("#")) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
const rest = pointer.slice(1);
|
|
12
|
+
if (rest === "") {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
if (!rest.startsWith("/")) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
return rest
|
|
20
|
+
.slice(1)
|
|
21
|
+
.split("/")
|
|
22
|
+
.map((segment) => decodeURIComponent(segment)
|
|
23
|
+
.replaceAll("~1", "/")
|
|
24
|
+
.replaceAll("~0", "~"));
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* The most nodes a document may have once `$extends` and `$ref` are resolved.
|
|
32
|
+
* Both copy what they name, so a chain of them grows exponentially: 22 groups
|
|
33
|
+
* that each hold two `$ref`s to the one before are four million tokens. The
|
|
34
|
+
* time to read a document is linear in its expanded size, so this bounds the
|
|
35
|
+
* time, and a real document is a small part of it.
|
|
36
|
+
*/
|
|
37
|
+
const MAX_EXPANDED_NODES = 1_000_000;
|
|
38
|
+
/**
|
|
39
|
+
* Thrown to stop expanding a document that is too large.
|
|
40
|
+
*/
|
|
41
|
+
export class TooLarge extends Error {
|
|
42
|
+
}
|
|
43
|
+
export const tooLarge = () => ({
|
|
44
|
+
path: "",
|
|
45
|
+
message: `is more than ${MAX_EXPANDED_NODES} nodes once $extends and $ref are resolved. Each one copies what it names, so a chain of them grows exponentially.`,
|
|
46
|
+
});
|
|
47
|
+
/**
|
|
48
|
+
* The number of nodes of a document, counting a node that is shared as many
|
|
49
|
+
* times as it's used. That is what the document expands to.
|
|
50
|
+
*/
|
|
51
|
+
const expandedSize = (root) => {
|
|
52
|
+
const known = new WeakMap();
|
|
53
|
+
const size = (node) => {
|
|
54
|
+
if (typeof node !== "object" || node === null) {
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
const cached = known.get(node);
|
|
58
|
+
if (cached !== undefined) {
|
|
59
|
+
return cached;
|
|
60
|
+
}
|
|
61
|
+
let total = 1;
|
|
62
|
+
for (const child of Object.values(node)) {
|
|
63
|
+
total += size(child);
|
|
64
|
+
}
|
|
65
|
+
known.set(node, total);
|
|
66
|
+
return total;
|
|
67
|
+
};
|
|
68
|
+
return size(root);
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Applies `$extends`: a group takes the tokens and groups of the group it
|
|
72
|
+
* extends, and its own override them. Groups at the same path are merged
|
|
73
|
+
* deeply; a token replaces a token in full.
|
|
74
|
+
*/
|
|
75
|
+
const resolveExtends = (doc, report) => {
|
|
76
|
+
const done = new Map();
|
|
77
|
+
const active = [];
|
|
78
|
+
const merge = (base, over) => {
|
|
79
|
+
const result = { ...base };
|
|
80
|
+
for (const key of Object.keys(over)) {
|
|
81
|
+
setOwn(result, key, isChildName(key) &&
|
|
82
|
+
isGroupNode(base[key]) &&
|
|
83
|
+
isGroupNode(over[key])
|
|
84
|
+
? merge(base[key], over[key])
|
|
85
|
+
: over[key]);
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
};
|
|
89
|
+
const targetOf = (extended, here) => {
|
|
90
|
+
if (isReference(extended)) {
|
|
91
|
+
return referencePath(extended);
|
|
92
|
+
}
|
|
93
|
+
if (isObject(extended) && typeof extended.$ref === "string") {
|
|
94
|
+
const path = parsePointer(extended.$ref);
|
|
95
|
+
if (path) {
|
|
96
|
+
return path;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
report(here, `$extends must be a reference to a group, such as "{color.brand}" or { "$ref": "#/color/brand" }, but found ${JSON.stringify(extended)}.`);
|
|
100
|
+
return undefined;
|
|
101
|
+
};
|
|
102
|
+
const resolveNode = (node, path) => {
|
|
103
|
+
if (!isGroupNode(node)) {
|
|
104
|
+
return node;
|
|
105
|
+
}
|
|
106
|
+
const key = label(path);
|
|
107
|
+
if (done.has(key)) {
|
|
108
|
+
return done.get(key);
|
|
109
|
+
}
|
|
110
|
+
if (active.includes(key)) {
|
|
111
|
+
report(key, `$extends forms a cycle: ${[...active.slice(active.indexOf(key)), key].join(" → ")}.`);
|
|
112
|
+
const { $extends, ...rest } = node;
|
|
113
|
+
return rest;
|
|
114
|
+
}
|
|
115
|
+
active.push(key);
|
|
116
|
+
const local = {};
|
|
117
|
+
for (const property of Object.keys(node)) {
|
|
118
|
+
if (property !== "$extends") {
|
|
119
|
+
setOwn(local, property, isChildName(property)
|
|
120
|
+
? resolveNode(node[property], [...path, property])
|
|
121
|
+
: node[property]);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
let result = local;
|
|
125
|
+
if ("$extends" in node) {
|
|
126
|
+
const target = targetOf(node.$extends, key);
|
|
127
|
+
if (target) {
|
|
128
|
+
const base = lookup(target);
|
|
129
|
+
if (base === undefined) {
|
|
130
|
+
report(key, `$extends names "${label(target)}", which does not exist.`);
|
|
131
|
+
}
|
|
132
|
+
else if (!isGroupNode(base)) {
|
|
133
|
+
report(key, `$extends names "${label(target)}", which is a token. It must name a group.`);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
result = merge(base, local);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
active.pop();
|
|
141
|
+
done.set(key, result);
|
|
142
|
+
return result;
|
|
143
|
+
};
|
|
144
|
+
// Finds a group or token by path, seeing the children a group inherits.
|
|
145
|
+
const lookup = (path) => {
|
|
146
|
+
let node = doc;
|
|
147
|
+
const walked = [];
|
|
148
|
+
for (const segment of path) {
|
|
149
|
+
if (!isGroupNode(node)) {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
if ("$extends" in node) {
|
|
153
|
+
node = resolveNode(node, walked);
|
|
154
|
+
}
|
|
155
|
+
if (!isChildName(segment) || !Object.hasOwn(node, segment)) {
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
node = node[segment];
|
|
159
|
+
walked.push(segment);
|
|
160
|
+
}
|
|
161
|
+
return resolveNode(node, walked);
|
|
162
|
+
};
|
|
163
|
+
return resolveNode(doc, []);
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* Resolves `{ "$ref": "#/json/pointer" }`. A pointer to a token's `$value`
|
|
167
|
+
* becomes a reference, so the value stays live; a pointer to anything else is
|
|
168
|
+
* replaced by a copy of what it points at.
|
|
169
|
+
*/
|
|
170
|
+
const resolvePointers = (doc, report) => {
|
|
171
|
+
const active = [];
|
|
172
|
+
const pointerGet = (path, here) => {
|
|
173
|
+
let node = doc;
|
|
174
|
+
for (const segment of path) {
|
|
175
|
+
if (isObject(node) && "$ref" in node) {
|
|
176
|
+
node = refObject(node, here);
|
|
177
|
+
// A reference to a token stands for its $value, so a pointer
|
|
178
|
+
// can go on through it.
|
|
179
|
+
if (isReference(node)) {
|
|
180
|
+
node = pointerGet([...referencePath(node), "$value"], here);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (Array.isArray(node)) {
|
|
184
|
+
node = node[Number(segment)];
|
|
185
|
+
}
|
|
186
|
+
else if (isObject(node) && Object.hasOwn(node, segment)) {
|
|
187
|
+
node = node[segment];
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return node;
|
|
194
|
+
};
|
|
195
|
+
const refObject = (node, here) => {
|
|
196
|
+
const where = label(here);
|
|
197
|
+
if (typeof node.$ref !== "string" || Object.keys(node).length !== 1) {
|
|
198
|
+
report(where, `a $ref must be the only property, and a string.`);
|
|
199
|
+
return node;
|
|
200
|
+
}
|
|
201
|
+
const path = parsePointer(node.$ref);
|
|
202
|
+
if (!path) {
|
|
203
|
+
report(where, `$ref "${node.$ref}" is not a JSON Pointer into this document, such as "#/color/blue/$value".`);
|
|
204
|
+
return node;
|
|
205
|
+
}
|
|
206
|
+
if (active.includes(node.$ref)) {
|
|
207
|
+
report(where, `$ref "${node.$ref}" is circular: ${[...active.slice(active.indexOf(node.$ref)), node.$ref].join(" → ")}.`);
|
|
208
|
+
return node;
|
|
209
|
+
}
|
|
210
|
+
active.push(node.$ref);
|
|
211
|
+
try {
|
|
212
|
+
const target = pointerGet(path, here);
|
|
213
|
+
if (target === undefined) {
|
|
214
|
+
report(where, `$ref "${node.$ref}" does not exist.`);
|
|
215
|
+
return node;
|
|
216
|
+
}
|
|
217
|
+
const parent = path.slice(0, -1);
|
|
218
|
+
if (path[path.length - 1] === "$value" &&
|
|
219
|
+
isTokenNode(pointerGet(parent, here))) {
|
|
220
|
+
return `{${parent.join(".")}}`;
|
|
221
|
+
}
|
|
222
|
+
if (isTokenNode(target)) {
|
|
223
|
+
return `{${path.join(".")}}`;
|
|
224
|
+
}
|
|
225
|
+
return walk(target, here);
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
228
|
+
active.pop();
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
let visited = 0;
|
|
232
|
+
// Extensions are vendor data, and a "$ref" in one is theirs, except in
|
|
233
|
+
// this library's own: it holds the library's form of the token, which
|
|
234
|
+
// the hand-written form's "$ref" reads in.
|
|
235
|
+
const extensionsOf = (extensions, here) => {
|
|
236
|
+
if (!isObject(extensions) || !Object.hasOwn(extensions, EXTENSION)) {
|
|
237
|
+
return extensions;
|
|
238
|
+
}
|
|
239
|
+
const result = { ...extensions };
|
|
240
|
+
setOwn(result, EXTENSION, walk(extensions[EXTENSION], [...here, EXTENSION]));
|
|
241
|
+
return result;
|
|
242
|
+
};
|
|
243
|
+
const walk = (node, here) => {
|
|
244
|
+
// A node is an object or an array, as `expandedSize()` counts them.
|
|
245
|
+
if (typeof node !== "object" || node === null) {
|
|
246
|
+
return node;
|
|
247
|
+
}
|
|
248
|
+
if (++visited > MAX_EXPANDED_NODES) {
|
|
249
|
+
throw new TooLarge();
|
|
250
|
+
}
|
|
251
|
+
if (Array.isArray(node)) {
|
|
252
|
+
return node.map((item, index) => walk(item, [...here, `${index}`]));
|
|
253
|
+
}
|
|
254
|
+
if ("$ref" in node) {
|
|
255
|
+
return refObject(node, here);
|
|
256
|
+
}
|
|
257
|
+
const result = {};
|
|
258
|
+
for (const key of Object.keys(node)) {
|
|
259
|
+
setOwn(result, key, key === "$extensions"
|
|
260
|
+
? extensionsOf(node[key], [...here, key])
|
|
261
|
+
: walk(node[key], [...here, key]));
|
|
262
|
+
}
|
|
263
|
+
return result;
|
|
264
|
+
};
|
|
265
|
+
return walk(doc, []);
|
|
266
|
+
};
|
|
267
|
+
/**
|
|
268
|
+
* Resolves `$extends` and `$ref`.
|
|
269
|
+
*
|
|
270
|
+
* @throws TooLarge when they expand the document past what is allowed.
|
|
271
|
+
*/
|
|
272
|
+
export const resolveReferences = (doc, report) => {
|
|
273
|
+
const extended = resolveExtends(doc, report);
|
|
274
|
+
// `$extends` shares what it copies, so it is cheap itself, and what it
|
|
275
|
+
// expands to is what the passes after it must visit.
|
|
276
|
+
if (expandedSize(extended) > MAX_EXPANDED_NODES) {
|
|
277
|
+
throw new TooLarge();
|
|
278
|
+
}
|
|
279
|
+
return resolvePointers(extended, report);
|
|
280
|
+
};
|
|
281
|
+
//# sourceMappingURL=parse-resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-resolve.js","sourceRoot":"","sources":["../../src/lib/parse-resolve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { DesignToken } from "./design-token.js";
|
|
2
|
+
/**
|
|
3
|
+
* What reading a value needs from the document it's in.
|
|
4
|
+
*
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
export interface ValueContext {
|
|
8
|
+
/**
|
|
9
|
+
* Reports a problem with the value at `path`.
|
|
10
|
+
*/
|
|
11
|
+
report(path: string, message: string): void;
|
|
12
|
+
/**
|
|
13
|
+
* Reads a value where the format allows a reference to a token of
|
|
14
|
+
* `type`: a reference, or a recipe call. Returns the ref or computed
|
|
15
|
+
* value it stands for, or `undefined` when it's neither.
|
|
16
|
+
*/
|
|
17
|
+
reference(value: unknown, type: string, path: string): unknown;
|
|
18
|
+
}
|
|
19
|
+
type StandardType = keyof DesignToken.ValueByType;
|
|
20
|
+
/**
|
|
21
|
+
* Tests whether a type is one of the standard types, defined by the DTCG
|
|
22
|
+
* format.
|
|
23
|
+
*
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
export declare const isStandardType: (type: string) => type is StandardType;
|
|
27
|
+
/**
|
|
28
|
+
* Reads the `$value` of a token of a standard type, in the shapes of the
|
|
29
|
+
* 2025.10 format and color module. A reference inside a composite value is
|
|
30
|
+
* checked to name a token of the type the position needs.
|
|
31
|
+
*
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
34
|
+
export declare const readValue: (type: StandardType, value: unknown, path: string, context: ValueContext) => unknown;
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { DesignToken } from "./design-token.js";
|
|
2
|
+
import { DIMENSION_UNITS, DURATION_UNITS, FONT_WEIGHTS, isKeyOf, LINE_CAPS, STROKE_STYLES, } from "./standard-values.js";
|
|
3
|
+
const STANDARD_TYPES = new Set(Object.values(DesignToken.Type));
|
|
4
|
+
/**
|
|
5
|
+
* Tests whether a type is one of the standard types, defined by the DTCG
|
|
6
|
+
* format.
|
|
7
|
+
*
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export const isStandardType = (type) => STANDARD_TYPES.has(type);
|
|
11
|
+
const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12
|
+
const isNumber = (value) => typeof value === "number" && Number.isFinite(value);
|
|
13
|
+
const unit = { min: 0, max: 1 };
|
|
14
|
+
const hue = { min: 0, max: 360, exclusive: true };
|
|
15
|
+
const percentage = { min: 0, max: 100 };
|
|
16
|
+
const open = { min: -Infinity, max: Infinity };
|
|
17
|
+
const positive = { min: 0, max: Infinity };
|
|
18
|
+
/**
|
|
19
|
+
* The components of each color space, in order, with their ranges.
|
|
20
|
+
*
|
|
21
|
+
* @see {@link https://www.designtokens.org/tr/2025.10/color/}
|
|
22
|
+
*/
|
|
23
|
+
const COLOR_SPACES = {
|
|
24
|
+
srgb: [unit, unit, unit],
|
|
25
|
+
"srgb-linear": [unit, unit, unit],
|
|
26
|
+
hsl: [hue, percentage, percentage],
|
|
27
|
+
hwb: [hue, percentage, percentage],
|
|
28
|
+
lab: [percentage, open, open],
|
|
29
|
+
lch: [percentage, positive, hue],
|
|
30
|
+
oklab: [unit, open, open],
|
|
31
|
+
oklch: [unit, positive, hue],
|
|
32
|
+
"display-p3": [unit, unit, unit],
|
|
33
|
+
"a98-rgb": [unit, unit, unit],
|
|
34
|
+
"prophoto-rgb": [unit, unit, unit],
|
|
35
|
+
rec2020: [unit, unit, unit],
|
|
36
|
+
"xyz-d65": [unit, unit, unit],
|
|
37
|
+
"xyz-d50": [unit, unit, unit],
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Rejects a key the format does not define, so a typo is an error instead of
|
|
41
|
+
* being ignored.
|
|
42
|
+
*/
|
|
43
|
+
const onlyKeys = (value, allowed, path, context) => {
|
|
44
|
+
for (const key of Object.keys(value)) {
|
|
45
|
+
if (!allowed.includes(key)) {
|
|
46
|
+
context.report(path, `has an unsupported property "${key}". Expected ${allowed.join(", ")}.`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const describe = (value) => {
|
|
51
|
+
return JSON.stringify(value) ?? String(value);
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* The keys of a table as alternatives, like `"a", "b" or "c"`.
|
|
55
|
+
*/
|
|
56
|
+
const oneOf = (table) => {
|
|
57
|
+
const keys = Object.keys(table).map((key) => `"${key}"`);
|
|
58
|
+
const last = keys.pop();
|
|
59
|
+
return keys.length > 0 ? `${keys.join(", ")} or ${last}` : `${last}`;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Reads a position that holds a value of `type`, or a reference to a token of
|
|
63
|
+
* that type.
|
|
64
|
+
*/
|
|
65
|
+
const at = (type, value, path, context) => context.reference(value, type, path) ?? READERS[type](value, path, context);
|
|
66
|
+
const inRange = (value, range) => {
|
|
67
|
+
return (value >= range.min &&
|
|
68
|
+
(range.exclusive ? value < range.max : value <= range.max));
|
|
69
|
+
};
|
|
70
|
+
const color = (value, path, context) => {
|
|
71
|
+
if (!isObject(value)) {
|
|
72
|
+
context.report(path, `is not a color. A color is an object with a colorSpace and components, but found ${describe(value)}.`);
|
|
73
|
+
return value;
|
|
74
|
+
}
|
|
75
|
+
onlyKeys(value, ["colorSpace", "components", "alpha", "hex"], path, context);
|
|
76
|
+
const result = { ...value };
|
|
77
|
+
const space = value.colorSpace;
|
|
78
|
+
const ranges = isKeyOf(COLOR_SPACES, space)
|
|
79
|
+
? COLOR_SPACES[space]
|
|
80
|
+
: undefined;
|
|
81
|
+
if (!ranges) {
|
|
82
|
+
context.report(`${path}.colorSpace`, `is ${describe(space)}, which is not a color space. Expected one of ${Object.keys(COLOR_SPACES).join(", ")}.`);
|
|
83
|
+
}
|
|
84
|
+
if (!Array.isArray(value.components)) {
|
|
85
|
+
context.report(`${path}.components`, "must be an array.");
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
if (ranges && value.components.length !== ranges.length) {
|
|
89
|
+
context.report(`${path}.components`, `must have ${ranges.length} components for ${space}, but has ${value.components.length}.`);
|
|
90
|
+
}
|
|
91
|
+
result.components = value.components.map((component, index) => {
|
|
92
|
+
const where = `${path}.components[${index}]`;
|
|
93
|
+
if (component === "none") {
|
|
94
|
+
return component;
|
|
95
|
+
}
|
|
96
|
+
const ref = context.reference(component, "number", where);
|
|
97
|
+
if (ref !== undefined) {
|
|
98
|
+
return ref;
|
|
99
|
+
}
|
|
100
|
+
if (!isNumber(component)) {
|
|
101
|
+
context.report(where, `must be a number or "none", but found ${describe(component)}.`);
|
|
102
|
+
}
|
|
103
|
+
else if (ranges?.[index] &&
|
|
104
|
+
!inRange(component, ranges[index])) {
|
|
105
|
+
const range = ranges[index];
|
|
106
|
+
context.report(where, `is ${component}, outside the range of ${space} component ${index + 1}: ${range.min} to ${range.max}${range.exclusive ? " (exclusive)" : ""}.`);
|
|
107
|
+
}
|
|
108
|
+
return component;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (value.alpha !== undefined) {
|
|
112
|
+
const where = `${path}.alpha`;
|
|
113
|
+
const ref = context.reference(value.alpha, "number", where);
|
|
114
|
+
if (ref !== undefined) {
|
|
115
|
+
result.alpha = ref;
|
|
116
|
+
}
|
|
117
|
+
else if (!(isNumber(value.alpha) && inRange(value.alpha, unit))) {
|
|
118
|
+
context.report(where, `must be a number from 0 to 1, but found ${describe(value.alpha)}.`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (value.hex !== undefined &&
|
|
122
|
+
!(typeof value.hex === "string" && /^#[0-9a-fA-F]{6}$/.test(value.hex))) {
|
|
123
|
+
context.report(`${path}.hex`, `must be six digit hex notation like "#ff00ff", but found ${describe(value.hex)}.`);
|
|
124
|
+
}
|
|
125
|
+
return result;
|
|
126
|
+
};
|
|
127
|
+
const withUnit = (name, units) => (value, path, context) => {
|
|
128
|
+
if (!isObject(value)) {
|
|
129
|
+
context.report(path, `is not a ${name}. A ${name} is an object with a value and a unit, but found ${describe(value)}.`);
|
|
130
|
+
return value;
|
|
131
|
+
}
|
|
132
|
+
onlyKeys(value, ["value", "unit"], path, context);
|
|
133
|
+
if (!isKeyOf(units, value.unit)) {
|
|
134
|
+
context.report(`${path}.unit`, `must be ${oneOf(units)}, but found ${describe(value.unit)}.`);
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
...value,
|
|
138
|
+
value: at("number", value.value, `${path}.value`, context),
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
const fontFamily = (value, path, context) => {
|
|
142
|
+
if (typeof value === "string") {
|
|
143
|
+
return value;
|
|
144
|
+
}
|
|
145
|
+
if (Array.isArray(value) &&
|
|
146
|
+
value.length > 0 &&
|
|
147
|
+
value.every((name) => typeof name === "string")) {
|
|
148
|
+
return [...value];
|
|
149
|
+
}
|
|
150
|
+
context.report(path, `must be a string or an array of strings, but found ${describe(value)}.`);
|
|
151
|
+
return value;
|
|
152
|
+
};
|
|
153
|
+
const fontWeight = (value, path, context) => {
|
|
154
|
+
const ok = (isNumber(value) && value >= 1 && value <= 1000) ||
|
|
155
|
+
isKeyOf(FONT_WEIGHTS, value);
|
|
156
|
+
if (!ok) {
|
|
157
|
+
context.report(path, `must be a number from 1 to 1000 or a font weight keyword, but found ${describe(value)}.`);
|
|
158
|
+
}
|
|
159
|
+
return value;
|
|
160
|
+
};
|
|
161
|
+
const cubicBezier = (value, path, context) => {
|
|
162
|
+
if (!Array.isArray(value) || value.length !== 4 || !value.every(isNumber)) {
|
|
163
|
+
context.report(path, `must be an array of four numbers, but found ${describe(value)}.`);
|
|
164
|
+
return value;
|
|
165
|
+
}
|
|
166
|
+
for (const index of [0, 2]) {
|
|
167
|
+
if (!inRange(value[index], unit)) {
|
|
168
|
+
context.report(`${path}[${index}]`, `is ${value[index]}, but the x coordinates of a cubic Bézier must be from 0 to 1.`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return [...value];
|
|
172
|
+
};
|
|
173
|
+
const number = (value, path, context) => {
|
|
174
|
+
if (!isNumber(value)) {
|
|
175
|
+
context.report(path, `must be a number, but found ${describe(value)}.`);
|
|
176
|
+
}
|
|
177
|
+
return value;
|
|
178
|
+
};
|
|
179
|
+
const strokeStyle = (value, path, context) => {
|
|
180
|
+
if (typeof value === "string") {
|
|
181
|
+
if (!isKeyOf(STROKE_STYLES, value)) {
|
|
182
|
+
context.report(path, `is "${value}", which is not a stroke style. Expected one of ${Object.keys(STROKE_STYLES).join(", ")}, or a dashArray and lineCap.`);
|
|
183
|
+
}
|
|
184
|
+
return value;
|
|
185
|
+
}
|
|
186
|
+
if (!isObject(value)) {
|
|
187
|
+
context.report(path, `must be a stroke style keyword or an object, but found ${describe(value)}.`);
|
|
188
|
+
return value;
|
|
189
|
+
}
|
|
190
|
+
onlyKeys(value, ["dashArray", "lineCap"], path, context);
|
|
191
|
+
const result = { ...value };
|
|
192
|
+
if (!Array.isArray(value.dashArray) || value.dashArray.length === 0) {
|
|
193
|
+
context.report(`${path}.dashArray`, "must be an array of dimensions.");
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
result.dashArray = value.dashArray.map((dash, index) => at("dimension", dash, `${path}.dashArray[${index}]`, context));
|
|
197
|
+
}
|
|
198
|
+
if (!isKeyOf(LINE_CAPS, value.lineCap)) {
|
|
199
|
+
context.report(`${path}.lineCap`, `must be ${oneOf(LINE_CAPS)}, but found ${describe(value.lineCap)}.`);
|
|
200
|
+
}
|
|
201
|
+
return result;
|
|
202
|
+
};
|
|
203
|
+
/**
|
|
204
|
+
* Reads the required properties of an object that have a type, each of which
|
|
205
|
+
* may be a reference to a token of its type.
|
|
206
|
+
*/
|
|
207
|
+
const properties = (value, types, path, context) => {
|
|
208
|
+
const result = { ...value };
|
|
209
|
+
for (const [property, type] of Object.entries(types)) {
|
|
210
|
+
if (value[property] === undefined) {
|
|
211
|
+
context.report(`${path}.${property}`, "is required.");
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
result[property] = at(type, value[property], `${path}.${property}`, context);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return result;
|
|
218
|
+
};
|
|
219
|
+
/**
|
|
220
|
+
* A reader of an object whose every property is required and has a type.
|
|
221
|
+
*/
|
|
222
|
+
const composite = (name, types) => (value, path, context) => {
|
|
223
|
+
if (!isObject(value)) {
|
|
224
|
+
context.report(path, `is not a ${name}. A ${name} is an object, but found ${describe(value)}.`);
|
|
225
|
+
return value;
|
|
226
|
+
}
|
|
227
|
+
onlyKeys(value, Object.keys(types), path, context);
|
|
228
|
+
return properties(value, types, path, context);
|
|
229
|
+
};
|
|
230
|
+
const shadowLayer = (value, path, context) => {
|
|
231
|
+
if (!isObject(value)) {
|
|
232
|
+
context.report(path, `is not a shadow. A shadow is an object, but found ${describe(value)}.`);
|
|
233
|
+
return value;
|
|
234
|
+
}
|
|
235
|
+
onlyKeys(value, ["color", "offsetX", "offsetY", "blur", "spread", "inset"], path, context);
|
|
236
|
+
if (value.inset !== undefined && typeof value.inset !== "boolean") {
|
|
237
|
+
context.report(`${path}.inset`, `must be true or false, but found ${describe(value.inset)}.`);
|
|
238
|
+
}
|
|
239
|
+
return properties(value, {
|
|
240
|
+
color: "color",
|
|
241
|
+
offsetX: "dimension",
|
|
242
|
+
offsetY: "dimension",
|
|
243
|
+
blur: "dimension",
|
|
244
|
+
spread: "dimension",
|
|
245
|
+
}, path, context);
|
|
246
|
+
};
|
|
247
|
+
const shadow = (value, path, context) => {
|
|
248
|
+
if (!Array.isArray(value)) {
|
|
249
|
+
return shadowLayer(value, path, context);
|
|
250
|
+
}
|
|
251
|
+
if (value.length === 0) {
|
|
252
|
+
context.report(path, "must have at least one shadow.");
|
|
253
|
+
}
|
|
254
|
+
return value.map((layer, index) => {
|
|
255
|
+
const where = `${path}[${index}]`;
|
|
256
|
+
return (context.reference(layer, "shadow", where) ??
|
|
257
|
+
shadowLayer(layer, where, context));
|
|
258
|
+
});
|
|
259
|
+
};
|
|
260
|
+
const gradient = (value, path, context) => {
|
|
261
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
262
|
+
context.report(path, `must be an array of gradient stops, but found ${describe(value)}.`);
|
|
263
|
+
return value;
|
|
264
|
+
}
|
|
265
|
+
return value.map((stop, index) => {
|
|
266
|
+
const where = `${path}[${index}]`;
|
|
267
|
+
const ref = context.reference(stop, "gradient", where);
|
|
268
|
+
if (ref !== undefined) {
|
|
269
|
+
return ref;
|
|
270
|
+
}
|
|
271
|
+
if (!isObject(stop)) {
|
|
272
|
+
context.report(where, `is not a gradient stop. A stop is an object with a color and a position, but found ${describe(stop)}.`);
|
|
273
|
+
return stop;
|
|
274
|
+
}
|
|
275
|
+
onlyKeys(stop, ["color", "position"], where, context);
|
|
276
|
+
const result = properties(stop, { color: "color" }, where, context);
|
|
277
|
+
const position = `${where}.position`;
|
|
278
|
+
if (stop.position === undefined) {
|
|
279
|
+
context.report(position, "is required.");
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
const ref = context.reference(stop.position, "number", position);
|
|
283
|
+
if (ref !== undefined) {
|
|
284
|
+
result.position = ref;
|
|
285
|
+
}
|
|
286
|
+
else if (!isNumber(stop.position)) {
|
|
287
|
+
context.report(position, `must be a number from 0 to 1, but found ${describe(stop.position)}.`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return result;
|
|
291
|
+
});
|
|
292
|
+
};
|
|
293
|
+
const READERS = {
|
|
294
|
+
border: composite("border", {
|
|
295
|
+
color: "color",
|
|
296
|
+
width: "dimension",
|
|
297
|
+
style: "strokeStyle",
|
|
298
|
+
}),
|
|
299
|
+
color,
|
|
300
|
+
cubicBezier,
|
|
301
|
+
dimension: withUnit("dimension", DIMENSION_UNITS),
|
|
302
|
+
duration: withUnit("duration", DURATION_UNITS),
|
|
303
|
+
fontFamily,
|
|
304
|
+
fontWeight,
|
|
305
|
+
gradient,
|
|
306
|
+
number,
|
|
307
|
+
shadow,
|
|
308
|
+
strokeStyle,
|
|
309
|
+
transition: composite("transition", {
|
|
310
|
+
duration: "duration",
|
|
311
|
+
delay: "duration",
|
|
312
|
+
timingFunction: "cubicBezier",
|
|
313
|
+
}),
|
|
314
|
+
typography: composite("typography", {
|
|
315
|
+
fontFamily: "fontFamily",
|
|
316
|
+
fontSize: "dimension",
|
|
317
|
+
fontWeight: "fontWeight",
|
|
318
|
+
letterSpacing: "dimension",
|
|
319
|
+
lineHeight: "number",
|
|
320
|
+
}),
|
|
321
|
+
};
|
|
322
|
+
/**
|
|
323
|
+
* Reads the `$value` of a token of a standard type, in the shapes of the
|
|
324
|
+
* 2025.10 format and color module. A reference inside a composite value is
|
|
325
|
+
* checked to name a token of the type the position needs.
|
|
326
|
+
*
|
|
327
|
+
* @internal
|
|
328
|
+
*/
|
|
329
|
+
export const readValue = (type, value, path, context) => READERS[type](value, path, context);
|
|
330
|
+
//# sourceMappingURL=parse-value.js.map
|