@terrazzo/parser 0.3.5 → 0.5.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 +41 -11
- package/dist/build/index.d.ts +1 -0
- package/dist/build/index.d.ts.map +1 -0
- package/dist/build/index.js +11 -10
- package/dist/build/index.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +42 -21
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/lib/code-frame.d.ts +1 -0
- package/dist/lib/code-frame.d.ts.map +1 -0
- package/dist/lint/index.d.ts +1 -0
- package/dist/lint/index.d.ts.map +1 -0
- package/dist/lint/index.js +8 -5
- package/dist/lint/index.js.map +1 -1
- package/dist/lint/plugin-core/index.d.ts +1 -0
- package/dist/lint/plugin-core/index.d.ts.map +1 -0
- package/dist/lint/plugin-core/lib/docs.d.ts +1 -0
- package/dist/lint/plugin-core/lib/docs.d.ts.map +1 -0
- package/dist/lint/plugin-core/rules/a11y-min-contrast.d.ts +1 -0
- package/dist/lint/plugin-core/rules/a11y-min-contrast.d.ts.map +1 -0
- package/dist/lint/plugin-core/rules/a11y-min-font-size.d.ts +1 -0
- package/dist/lint/plugin-core/rules/a11y-min-font-size.d.ts.map +1 -0
- package/dist/lint/plugin-core/rules/colorspace.d.ts +1 -0
- package/dist/lint/plugin-core/rules/colorspace.d.ts.map +1 -0
- package/dist/lint/plugin-core/rules/consistent-naming.d.ts +1 -0
- package/dist/lint/plugin-core/rules/consistent-naming.d.ts.map +1 -0
- package/dist/lint/plugin-core/rules/descriptions.d.ts +1 -0
- package/dist/lint/plugin-core/rules/descriptions.d.ts.map +1 -0
- package/dist/lint/plugin-core/rules/duplicate-values.d.ts +1 -0
- package/dist/lint/plugin-core/rules/duplicate-values.d.ts.map +1 -0
- package/dist/lint/plugin-core/rules/max-gamut.d.ts +1 -0
- package/dist/lint/plugin-core/rules/max-gamut.d.ts.map +1 -0
- package/dist/lint/plugin-core/rules/required-children.d.ts +1 -0
- package/dist/lint/plugin-core/rules/required-children.d.ts.map +1 -0
- package/dist/lint/plugin-core/rules/required-modes.d.ts +1 -0
- package/dist/lint/plugin-core/rules/required-modes.d.ts.map +1 -0
- package/dist/lint/plugin-core/rules/required-typography-properties.d.ts +1 -0
- package/dist/lint/plugin-core/rules/required-typography-properties.d.ts.map +1 -0
- package/dist/logger.d.ts +4 -3
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +25 -14
- package/dist/logger.js.map +1 -1
- package/dist/parse/alias.d.ts +13 -3
- package/dist/parse/alias.d.ts.map +1 -0
- package/dist/parse/alias.js +94 -43
- package/dist/parse/alias.js.map +1 -1
- package/dist/parse/index.d.ts +1 -0
- package/dist/parse/index.d.ts.map +1 -0
- package/dist/parse/index.js +58 -25
- package/dist/parse/index.js.map +1 -1
- package/dist/parse/json.d.ts +3 -3
- package/dist/parse/json.d.ts.map +1 -0
- package/dist/parse/json.js +5 -7
- package/dist/parse/json.js.map +1 -1
- package/dist/parse/normalize.d.ts +1 -0
- package/dist/parse/normalize.d.ts.map +1 -0
- package/dist/parse/normalize.js +8 -6
- package/dist/parse/normalize.js.map +1 -1
- package/dist/parse/validate.d.ts +1 -0
- package/dist/parse/validate.d.ts.map +1 -0
- package/dist/parse/validate.js +194 -110
- package/dist/parse/validate.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/build/index.ts +11 -10
- package/src/config.ts +42 -22
- package/src/lint/index.ts +8 -6
- package/src/logger.ts +30 -20
- package/src/parse/alias.ts +114 -45
- package/src/parse/index.ts +59 -28
- package/src/parse/json.ts +6 -8
- package/src/parse/normalize.ts +8 -6
- package/src/parse/validate.ts +204 -110
package/src/parse/validate.ts
CHANGED
|
@@ -87,19 +87,26 @@ function validateMembersAs(
|
|
|
87
87
|
{ filename, src, logger }: ValidateOptions,
|
|
88
88
|
) {
|
|
89
89
|
const members = getObjMembers($value);
|
|
90
|
-
for (const
|
|
91
|
-
const { validator, required } =
|
|
92
|
-
if (!members[
|
|
90
|
+
for (const [name, value] of Object.entries(properties)) {
|
|
91
|
+
const { validator, required } = value;
|
|
92
|
+
if (!members[name]) {
|
|
93
93
|
if (required) {
|
|
94
|
-
logger.error({
|
|
94
|
+
logger.error({
|
|
95
|
+
group: 'parser',
|
|
96
|
+
label: 'validate',
|
|
97
|
+
message: `Missing required property "${name}"`,
|
|
98
|
+
filename,
|
|
99
|
+
node: $value,
|
|
100
|
+
src,
|
|
101
|
+
});
|
|
95
102
|
}
|
|
96
103
|
continue;
|
|
97
104
|
}
|
|
98
|
-
const
|
|
99
|
-
if (isMaybeAlias(
|
|
100
|
-
validateAliasSyntax(
|
|
105
|
+
const memberValue = members[name];
|
|
106
|
+
if (isMaybeAlias(memberValue)) {
|
|
107
|
+
validateAliasSyntax(memberValue, node, { filename, src, logger });
|
|
101
108
|
} else {
|
|
102
|
-
validator(
|
|
109
|
+
validator(memberValue, node, { filename, src, logger });
|
|
103
110
|
}
|
|
104
111
|
}
|
|
105
112
|
}
|
|
@@ -107,14 +114,28 @@ function validateMembersAs(
|
|
|
107
114
|
/** Verify an Alias $value is formatted correctly */
|
|
108
115
|
export function validateAliasSyntax($value: ValueNode, _node: AnyNode, { filename, src, logger }: ValidateOptions) {
|
|
109
116
|
if ($value.type !== 'String' || !isAlias($value.value)) {
|
|
110
|
-
logger.error({
|
|
117
|
+
logger.error({
|
|
118
|
+
group: 'parser',
|
|
119
|
+
label: 'validate',
|
|
120
|
+
message: `Invalid alias: ${print($value)}`,
|
|
121
|
+
filename,
|
|
122
|
+
node: $value,
|
|
123
|
+
src,
|
|
124
|
+
});
|
|
111
125
|
}
|
|
112
126
|
}
|
|
113
127
|
|
|
114
128
|
/** Verify a Border token is valid */
|
|
115
129
|
export function validateBorder($value: ValueNode, node: AnyNode, { filename, src, logger }: ValidateOptions) {
|
|
116
130
|
if ($value.type !== 'Object') {
|
|
117
|
-
logger.error({
|
|
131
|
+
logger.error({
|
|
132
|
+
group: 'parser',
|
|
133
|
+
label: 'validate',
|
|
134
|
+
message: `Expected object, received ${$value.type}`,
|
|
135
|
+
filename,
|
|
136
|
+
node: $value,
|
|
137
|
+
src,
|
|
138
|
+
});
|
|
118
139
|
} else {
|
|
119
140
|
validateMembersAs(
|
|
120
141
|
$value,
|
|
@@ -131,6 +152,7 @@ export function validateBorder($value: ValueNode, node: AnyNode, { filename, src
|
|
|
131
152
|
|
|
132
153
|
/** Verify a Color token is valid */
|
|
133
154
|
export function validateColor($value: ValueNode, node: AnyNode, { filename, src, logger }: ValidateOptions) {
|
|
155
|
+
const baseMessage = { group: 'parser' as const, label: 'validate', filename, node: $value, src };
|
|
134
156
|
if ($value.type === 'String') {
|
|
135
157
|
// TODO: enable when object notation is finalized
|
|
136
158
|
// logger.warn({
|
|
@@ -140,7 +162,7 @@ export function validateColor($value: ValueNode, node: AnyNode, { filename, src,
|
|
|
140
162
|
// src,
|
|
141
163
|
// });
|
|
142
164
|
if ($value.value === '') {
|
|
143
|
-
logger.error({ message: 'Expected color, received empty string'
|
|
165
|
+
logger.error({ ...baseMessage, message: 'Expected color, received empty string' });
|
|
144
166
|
}
|
|
145
167
|
} else if ($value.type === 'Object') {
|
|
146
168
|
validateMembersAs(
|
|
@@ -149,10 +171,18 @@ export function validateColor($value: ValueNode, node: AnyNode, { filename, src,
|
|
|
149
171
|
colorSpace: {
|
|
150
172
|
validator: (v) => {
|
|
151
173
|
if (v.type !== 'String') {
|
|
152
|
-
logger.error({
|
|
174
|
+
logger.error({
|
|
175
|
+
...baseMessage,
|
|
176
|
+
message: `Expected string, received ${print(v)}`,
|
|
177
|
+
node: v,
|
|
178
|
+
});
|
|
153
179
|
}
|
|
154
180
|
if (!VALID_COLORSPACES.has((v as StringNode).value)) {
|
|
155
|
-
logger.error({
|
|
181
|
+
logger.error({
|
|
182
|
+
...baseMessage,
|
|
183
|
+
message: `Unsupported colorspace ${print(v)}`,
|
|
184
|
+
node: v,
|
|
185
|
+
});
|
|
156
186
|
}
|
|
157
187
|
},
|
|
158
188
|
required: true,
|
|
@@ -160,23 +190,25 @@ export function validateColor($value: ValueNode, node: AnyNode, { filename, src,
|
|
|
160
190
|
channels: {
|
|
161
191
|
validator: (v) => {
|
|
162
192
|
if (v.type !== 'Array') {
|
|
163
|
-
logger.error({
|
|
193
|
+
logger.error({
|
|
194
|
+
...baseMessage,
|
|
195
|
+
message: `Expected array, received ${print(v)}`,
|
|
196
|
+
node: v,
|
|
197
|
+
});
|
|
164
198
|
} else {
|
|
165
199
|
if (v.elements?.length !== 3) {
|
|
166
200
|
logger.error({
|
|
201
|
+
...baseMessage,
|
|
167
202
|
message: `Expected 3 channels, received ${v.elements?.length ?? 0}`,
|
|
168
|
-
filename,
|
|
169
203
|
node: v,
|
|
170
|
-
src,
|
|
171
204
|
});
|
|
172
205
|
}
|
|
173
206
|
for (const element of v.elements) {
|
|
174
207
|
if (element.value.type !== 'Number') {
|
|
175
208
|
logger.error({
|
|
209
|
+
...baseMessage,
|
|
176
210
|
message: `Expected number, received ${print(element.value)}`,
|
|
177
|
-
filename,
|
|
178
211
|
node: element,
|
|
179
|
-
src,
|
|
180
212
|
});
|
|
181
213
|
}
|
|
182
214
|
}
|
|
@@ -196,7 +228,11 @@ export function validateColor($value: ValueNode, node: AnyNode, { filename, src,
|
|
|
196
228
|
v.value.length === 7 + 1 ||
|
|
197
229
|
!/^#[a-f0-9]{3,8}$/i.test(v.value)
|
|
198
230
|
) {
|
|
199
|
-
logger.error({
|
|
231
|
+
logger.error({
|
|
232
|
+
...baseMessage,
|
|
233
|
+
message: `Invalid hex color ${print(v)}`,
|
|
234
|
+
node: v,
|
|
235
|
+
});
|
|
200
236
|
}
|
|
201
237
|
},
|
|
202
238
|
},
|
|
@@ -206,30 +242,25 @@ export function validateColor($value: ValueNode, node: AnyNode, { filename, src,
|
|
|
206
242
|
{ filename, src, logger },
|
|
207
243
|
);
|
|
208
244
|
} else {
|
|
209
|
-
logger.error({
|
|
245
|
+
logger.error({
|
|
246
|
+
...baseMessage,
|
|
247
|
+
message: `Expected object, received ${$value.type}`,
|
|
248
|
+
node: $value,
|
|
249
|
+
});
|
|
210
250
|
}
|
|
211
251
|
}
|
|
212
252
|
|
|
213
253
|
/** Verify a Cubic Bézier token is valid */
|
|
214
254
|
export function validateCubicBezier($value: ValueNode, _node: AnyNode, { filename, src, logger }: ValidateOptions) {
|
|
255
|
+
const baseMessage = { group: 'parser' as const, label: 'validate', filename, node: $value, src };
|
|
215
256
|
if ($value.type !== 'Array') {
|
|
216
|
-
logger.error({ message: `Expected array of strings, received ${print($value)}
|
|
257
|
+
logger.error({ ...baseMessage, message: `Expected array of strings, received ${print($value)}` });
|
|
217
258
|
} else if (
|
|
218
259
|
!$value.elements.every((e) => e.value.type === 'Number' || (e.value.type === 'String' && isAlias(e.value.value)))
|
|
219
260
|
) {
|
|
220
|
-
logger.error({
|
|
221
|
-
message: 'Expected an array of 4 numbers, received some non-numbers',
|
|
222
|
-
filename,
|
|
223
|
-
node: $value,
|
|
224
|
-
src,
|
|
225
|
-
});
|
|
261
|
+
logger.error({ ...baseMessage, message: 'Expected an array of 4 numbers, received some non-numbers' });
|
|
226
262
|
} else if ($value.elements.length !== 4) {
|
|
227
|
-
logger.error({
|
|
228
|
-
message: `Expected an array of 4 numbers, received ${$value.elements.length}`,
|
|
229
|
-
filename,
|
|
230
|
-
node: $value,
|
|
231
|
-
src,
|
|
232
|
-
});
|
|
263
|
+
logger.error({ ...baseMessage, message: `Expected an array of 4 numbers, received ${$value.elements.length}` });
|
|
233
264
|
}
|
|
234
265
|
}
|
|
235
266
|
|
|
@@ -238,44 +269,50 @@ export function validateDimension($value: ValueNode, _node: AnyNode, { filename,
|
|
|
238
269
|
if ($value.type === 'Number' && $value.value === 0) {
|
|
239
270
|
return; // `0` is a valid number
|
|
240
271
|
}
|
|
272
|
+
|
|
273
|
+
const baseMessage = { group: 'parser' as const, label: 'validate', filename, node: $value, src };
|
|
274
|
+
|
|
275
|
+
// Give priority to object notation because it’s a faster code path
|
|
241
276
|
if ($value.type === 'Object') {
|
|
242
277
|
const { value, unit } = getObjMembers($value);
|
|
243
278
|
if (!value) {
|
|
244
|
-
logger.error({ message: 'Missing required property "value".'
|
|
279
|
+
logger.error({ ...baseMessage, message: 'Missing required property "value".' });
|
|
245
280
|
}
|
|
246
281
|
if (!unit) {
|
|
247
|
-
logger.error({ message: 'Missing required property "unit".'
|
|
282
|
+
logger.error({ ...baseMessage, message: 'Missing required property "unit".' });
|
|
248
283
|
}
|
|
249
284
|
if (value!.type !== 'Number') {
|
|
250
|
-
logger.error({
|
|
285
|
+
logger.error({
|
|
286
|
+
...baseMessage,
|
|
287
|
+
message: `Expected number, received ${value!.type}`,
|
|
288
|
+
node: value,
|
|
289
|
+
});
|
|
251
290
|
}
|
|
252
291
|
if (!['px', 'em', 'rem'].includes((unit as StringNode).value)) {
|
|
253
292
|
logger.error({
|
|
293
|
+
...baseMessage,
|
|
254
294
|
message: `Expected unit "px", "em", or "rem", received ${print(unit as StringNode)}`,
|
|
255
|
-
filename,
|
|
256
295
|
node: unit,
|
|
257
|
-
src,
|
|
258
296
|
});
|
|
259
297
|
}
|
|
260
298
|
return;
|
|
261
299
|
}
|
|
300
|
+
|
|
262
301
|
// Backwards compat: string
|
|
263
302
|
if ($value.type !== 'String') {
|
|
264
|
-
logger.error({ message: `Expected string, received ${$value.type}
|
|
303
|
+
logger.error({ ...baseMessage, message: `Expected string, received ${$value.type}` });
|
|
265
304
|
}
|
|
266
305
|
const value = ($value as StringNode).value.match(/^-?[0-9.]+/)?.[0];
|
|
267
306
|
const unit = ($value as StringNode).value.replace(value!, '');
|
|
268
307
|
if (($value as StringNode).value === '') {
|
|
269
|
-
logger.error({ message: 'Expected dimension, received empty string'
|
|
308
|
+
logger.error({ ...baseMessage, message: 'Expected dimension, received empty string' });
|
|
270
309
|
} else if (!['px', 'em', 'rem'].includes(unit)) {
|
|
271
310
|
logger.error({
|
|
311
|
+
...baseMessage,
|
|
272
312
|
message: `Expected unit "px", "em", or "rem", received ${JSON.stringify(unit || ($value as StringNode).value)}`,
|
|
273
|
-
filename,
|
|
274
|
-
node: $value,
|
|
275
|
-
src,
|
|
276
313
|
});
|
|
277
314
|
} else if (!Number.isFinite(Number.parseFloat(value!))) {
|
|
278
|
-
logger.error({ message: `Expected dimension with units, received ${print($value)}
|
|
315
|
+
logger.error({ ...baseMessage, message: `Expected dimension with units, received ${print($value)}` });
|
|
279
316
|
}
|
|
280
317
|
}
|
|
281
318
|
|
|
@@ -284,106 +321,101 @@ export function validateDuration($value: ValueNode, _node: AnyNode, { filename,
|
|
|
284
321
|
if ($value.type === 'Number' && $value.value === 0) {
|
|
285
322
|
return; // `0` is a valid number
|
|
286
323
|
}
|
|
324
|
+
|
|
325
|
+
const baseMessage = { group: 'parser' as const, label: 'validate', filename, node: $value, src };
|
|
326
|
+
|
|
327
|
+
// Give priority to object notation because it’s a faster code path
|
|
287
328
|
if ($value.type === 'Object') {
|
|
288
329
|
const { value, unit } = getObjMembers($value);
|
|
289
330
|
if (!value) {
|
|
290
|
-
logger.error({ message: 'Missing required property "value".'
|
|
331
|
+
logger.error({ ...baseMessage, message: 'Missing required property "value".' });
|
|
291
332
|
}
|
|
292
333
|
if (!unit) {
|
|
293
|
-
logger.error({ message: 'Missing required property "unit".'
|
|
334
|
+
logger.error({ ...baseMessage, message: 'Missing required property "unit".' });
|
|
294
335
|
}
|
|
295
336
|
if (value?.type !== 'Number') {
|
|
296
|
-
logger.error({
|
|
337
|
+
logger.error({
|
|
338
|
+
...baseMessage,
|
|
339
|
+
message: `Expected number, received ${value?.type}`,
|
|
340
|
+
node: value,
|
|
341
|
+
});
|
|
297
342
|
}
|
|
298
343
|
if (!['ms', 's'].includes((unit as StringNode).value)) {
|
|
299
|
-
logger.error({
|
|
344
|
+
logger.error({
|
|
345
|
+
...baseMessage,
|
|
346
|
+
message: `Expected unit "ms" or "s", received ${print(unit!)}`,
|
|
347
|
+
node: unit,
|
|
348
|
+
});
|
|
300
349
|
}
|
|
301
350
|
return;
|
|
302
351
|
}
|
|
352
|
+
|
|
303
353
|
// Backwards compat: string
|
|
304
354
|
if ($value.type !== 'String') {
|
|
305
|
-
logger.error({ message: `Expected string, received ${$value.type}
|
|
355
|
+
logger.error({ ...baseMessage, message: `Expected string, received ${$value.type}` });
|
|
306
356
|
}
|
|
307
357
|
const value = ($value as StringNode).value.match(/^-?[0-9.]+/)?.[0]!;
|
|
308
358
|
const unit = ($value as StringNode).value.replace(value, '');
|
|
309
359
|
if (($value as StringNode).value === '') {
|
|
310
|
-
logger.error({ message: 'Expected duration, received empty string'
|
|
360
|
+
logger.error({ ...baseMessage, message: 'Expected duration, received empty string' });
|
|
311
361
|
} else if (!['ms', 's'].includes(unit)) {
|
|
312
362
|
logger.error({
|
|
363
|
+
...baseMessage,
|
|
313
364
|
message: `Expected unit "ms" or "s", received ${JSON.stringify(unit || ($value as StringNode).value)}`,
|
|
314
|
-
filename,
|
|
315
|
-
node: $value,
|
|
316
|
-
src,
|
|
317
365
|
});
|
|
318
366
|
} else if (!Number.isFinite(Number.parseFloat(value))) {
|
|
319
|
-
logger.error({ message: `Expected duration with units, received ${print($value)}
|
|
367
|
+
logger.error({ ...baseMessage, message: `Expected duration with units, received ${print($value)}` });
|
|
320
368
|
}
|
|
321
369
|
}
|
|
322
370
|
|
|
323
371
|
/** Verify a Font Family token is valid */
|
|
324
372
|
export function validateFontFamily($value: ValueNode, _node: AnyNode, { filename, src, logger }: ValidateOptions) {
|
|
373
|
+
const baseMessage = { group: 'parser' as const, label: 'validate', filename, node: $value, src };
|
|
325
374
|
if ($value.type !== 'String' && $value.type !== 'Array') {
|
|
326
|
-
logger.error({
|
|
327
|
-
message: `Expected string or array of strings, received ${$value.type}`,
|
|
328
|
-
filename,
|
|
329
|
-
node: $value,
|
|
330
|
-
src,
|
|
331
|
-
});
|
|
375
|
+
logger.error({ ...baseMessage, message: `Expected string or array of strings, received ${$value.type}` });
|
|
332
376
|
}
|
|
333
377
|
if ($value.type === 'String' && $value.value === '') {
|
|
334
|
-
logger.error({ message: 'Expected font family name, received empty string'
|
|
378
|
+
logger.error({ ...baseMessage, message: 'Expected font family name, received empty string' });
|
|
335
379
|
}
|
|
336
380
|
if ($value.type === 'Array' && !$value.elements.every((e) => e.value.type === 'String' && e.value.value !== '')) {
|
|
337
381
|
logger.error({
|
|
382
|
+
...baseMessage,
|
|
338
383
|
message: 'Expected an array of strings, received some non-strings or empty strings',
|
|
339
|
-
filename,
|
|
340
|
-
node: $value,
|
|
341
|
-
src,
|
|
342
384
|
});
|
|
343
385
|
}
|
|
344
386
|
}
|
|
345
387
|
|
|
346
388
|
/** Verify a Font Weight token is valid */
|
|
347
389
|
export function validateFontWeight($value: ValueNode, _node: AnyNode, { filename, src, logger }: ValidateOptions) {
|
|
390
|
+
const baseMessage = { group: 'parser' as const, label: 'validate', filename, node: $value, src };
|
|
348
391
|
if ($value.type !== 'String' && $value.type !== 'Number') {
|
|
349
|
-
logger.error({
|
|
350
|
-
message: `Expected a font weight name or number 0–1000, received ${$value.type}`,
|
|
351
|
-
filename,
|
|
352
|
-
node: $value,
|
|
353
|
-
src,
|
|
354
|
-
});
|
|
392
|
+
logger.error({ ...baseMessage, message: `Expected a font weight name or number 0–1000, received ${$value.type}` });
|
|
355
393
|
}
|
|
356
394
|
if ($value.type === 'String' && !FONT_WEIGHT_VALUES.has($value.value)) {
|
|
357
395
|
logger.error({
|
|
396
|
+
...baseMessage,
|
|
358
397
|
message: `Unknown font weight ${print($value)}. Expected one of: ${listFormat.format([...FONT_WEIGHT_VALUES])}.`,
|
|
359
|
-
filename,
|
|
360
|
-
node: $value,
|
|
361
|
-
src,
|
|
362
398
|
});
|
|
363
399
|
}
|
|
364
400
|
if ($value.type === 'Number' && ($value.value < 0 || $value.value > 1000)) {
|
|
365
|
-
logger.error({ message: `Expected number 0–1000, received ${print($value)}
|
|
401
|
+
logger.error({ ...baseMessage, message: `Expected number 0–1000, received ${print($value)}` });
|
|
366
402
|
}
|
|
367
403
|
}
|
|
368
404
|
|
|
369
405
|
/** Verify a Gradient token is valid */
|
|
370
406
|
export function validateGradient($value: ValueNode, _node: AnyNode, { filename, src, logger }: ValidateOptions) {
|
|
407
|
+
const baseMessage = { group: 'parser' as const, label: 'validate', filename, node: $value, src };
|
|
408
|
+
|
|
371
409
|
if ($value.type !== 'Array') {
|
|
372
|
-
logger.error({
|
|
373
|
-
message: `Expected array of gradient stops, received ${$value.type}`,
|
|
374
|
-
filename,
|
|
375
|
-
node: $value,
|
|
376
|
-
src,
|
|
377
|
-
});
|
|
410
|
+
logger.error({ ...baseMessage, message: `Expected array of gradient stops, received ${$value.type}` });
|
|
378
411
|
} else {
|
|
379
412
|
for (let i = 0; i < $value.elements.length; i++) {
|
|
380
413
|
const element = $value.elements[i]!;
|
|
381
414
|
if (element.value.type !== 'Object') {
|
|
382
415
|
logger.error({
|
|
416
|
+
...baseMessage,
|
|
383
417
|
message: `Stop #${i + 1}: Expected gradient stop, received ${element.value.type}`,
|
|
384
|
-
filename,
|
|
385
418
|
node: element,
|
|
386
|
-
src,
|
|
387
419
|
});
|
|
388
420
|
break;
|
|
389
421
|
}
|
|
@@ -403,21 +435,42 @@ export function validateGradient($value: ValueNode, _node: AnyNode, { filename,
|
|
|
403
435
|
/** Verify a Number token is valid */
|
|
404
436
|
export function validateNumber($value: ValueNode, _node: AnyNode, { filename, src, logger }: ValidateOptions) {
|
|
405
437
|
if ($value.type !== 'Number') {
|
|
406
|
-
logger.error({
|
|
438
|
+
logger.error({
|
|
439
|
+
group: 'parser',
|
|
440
|
+
label: 'validate',
|
|
441
|
+
message: `Expected number, received ${$value.type}`,
|
|
442
|
+
filename,
|
|
443
|
+
node: $value,
|
|
444
|
+
src,
|
|
445
|
+
});
|
|
407
446
|
}
|
|
408
447
|
}
|
|
409
448
|
|
|
410
449
|
/** Verify a Boolean token is valid */
|
|
411
450
|
export function validateBoolean($value: ValueNode, _node: AnyNode, { filename, src, logger }: ValidateOptions) {
|
|
412
451
|
if ($value.type !== 'Boolean') {
|
|
413
|
-
logger.error({
|
|
452
|
+
logger.error({
|
|
453
|
+
group: 'parser',
|
|
454
|
+
label: 'validate',
|
|
455
|
+
message: `Expected boolean, received ${$value.type}`,
|
|
456
|
+
filename,
|
|
457
|
+
node: $value,
|
|
458
|
+
src,
|
|
459
|
+
});
|
|
414
460
|
}
|
|
415
461
|
}
|
|
416
462
|
|
|
417
463
|
/** Verify a Shadow token’s value is valid */
|
|
418
464
|
export function validateShadowLayer($value: ValueNode, node: AnyNode, { filename, src, logger }: ValidateOptions) {
|
|
419
465
|
if ($value.type !== 'Object') {
|
|
420
|
-
logger.error({
|
|
466
|
+
logger.error({
|
|
467
|
+
group: 'parser',
|
|
468
|
+
label: 'validate',
|
|
469
|
+
message: `Expected Object, received ${$value.type}`,
|
|
470
|
+
filename,
|
|
471
|
+
node: $value,
|
|
472
|
+
src,
|
|
473
|
+
});
|
|
421
474
|
} else {
|
|
422
475
|
validateMembersAs(
|
|
423
476
|
$value,
|
|
@@ -437,31 +490,33 @@ export function validateShadowLayer($value: ValueNode, node: AnyNode, { filename
|
|
|
437
490
|
|
|
438
491
|
/** Verify a Stroke Style token is valid. */
|
|
439
492
|
export function validateStrokeStyle($value: ValueNode, node: AnyNode, { filename, src, logger }: ValidateOptions) {
|
|
493
|
+
const baseMessage = { group: 'parser' as const, label: 'validate', filename, node: $value, src };
|
|
494
|
+
|
|
440
495
|
// note: strokeStyle’s values are NOT aliasable (unless by string, but that breaks validations)
|
|
441
496
|
if ($value.type === 'String') {
|
|
442
497
|
if (!STROKE_STYLE_VALUES.has($value.value)) {
|
|
443
498
|
logger.error({
|
|
499
|
+
...baseMessage,
|
|
444
500
|
message: `Unknown stroke style ${print($value)}. Expected one of: ${listFormat.format([
|
|
445
501
|
...STROKE_STYLE_VALUES,
|
|
446
502
|
])}.`,
|
|
447
|
-
filename,
|
|
448
|
-
node: $value,
|
|
449
|
-
src,
|
|
450
503
|
});
|
|
451
504
|
}
|
|
452
505
|
} else if ($value.type === 'Object') {
|
|
453
506
|
const strokeMembers = getObjMembers($value);
|
|
454
507
|
for (const property of ['dashArray', 'lineCap']) {
|
|
455
508
|
if (!strokeMembers[property]) {
|
|
456
|
-
logger.error({ message: `Missing required property "${property}"
|
|
509
|
+
logger.error({ ...baseMessage, message: `Missing required property "${property}"` });
|
|
457
510
|
}
|
|
458
511
|
}
|
|
459
512
|
const { lineCap, dashArray } = strokeMembers;
|
|
460
513
|
if (lineCap?.type !== 'String' || !STROKE_STYLE_LINE_CAP_VALUES.has(lineCap.value)) {
|
|
461
514
|
logger.error({
|
|
515
|
+
...baseMessage,
|
|
462
516
|
message: `Unknown lineCap value ${print(lineCap!)}. Expected one of: ${listFormat.format([
|
|
463
517
|
...STROKE_STYLE_LINE_CAP_VALUES,
|
|
464
518
|
])}.`,
|
|
519
|
+
node,
|
|
465
520
|
});
|
|
466
521
|
}
|
|
467
522
|
if (dashArray?.type === 'Array') {
|
|
@@ -474,25 +529,31 @@ export function validateStrokeStyle($value: ValueNode, node: AnyNode, { filename
|
|
|
474
529
|
}
|
|
475
530
|
} else {
|
|
476
531
|
logger.error({
|
|
532
|
+
...baseMessage,
|
|
477
533
|
message: 'Expected array of strings, recieved some non-strings or empty strings.',
|
|
478
|
-
filename,
|
|
479
534
|
node: element,
|
|
480
|
-
src,
|
|
481
535
|
});
|
|
482
536
|
}
|
|
483
537
|
}
|
|
484
538
|
} else {
|
|
485
|
-
logger.error({ message: `Expected array of strings, received ${dashArray!.type}
|
|
539
|
+
logger.error({ ...baseMessage, message: `Expected array of strings, received ${dashArray!.type}` });
|
|
486
540
|
}
|
|
487
541
|
} else {
|
|
488
|
-
logger.error({ message: `Expected string or object, received ${$value.type}
|
|
542
|
+
logger.error({ ...baseMessage, message: `Expected string or object, received ${$value.type}` });
|
|
489
543
|
}
|
|
490
544
|
}
|
|
491
545
|
|
|
492
546
|
/** Verify a Transition token is valid */
|
|
493
547
|
export function validateTransition($value: ValueNode, node: AnyNode, { filename, src, logger }: ValidateOptions) {
|
|
494
548
|
if ($value.type !== 'Object') {
|
|
495
|
-
logger.error({
|
|
549
|
+
logger.error({
|
|
550
|
+
group: 'parser',
|
|
551
|
+
label: 'validate',
|
|
552
|
+
message: `Expected object, received ${$value.type}`,
|
|
553
|
+
filename,
|
|
554
|
+
node: $value,
|
|
555
|
+
src,
|
|
556
|
+
});
|
|
496
557
|
} else {
|
|
497
558
|
validateMembersAs(
|
|
498
559
|
$value,
|
|
@@ -513,15 +574,15 @@ export function validateTransition($value: ValueNode, node: AnyNode, { filename,
|
|
|
513
574
|
* really helps in debug messages.
|
|
514
575
|
*/
|
|
515
576
|
export function validateTokenMemberNode(node: MemberNode, { filename, src, logger }: ValidateOptions) {
|
|
577
|
+
const baseMessage = { group: 'parser' as const, label: 'validate', filename, node, src };
|
|
578
|
+
|
|
516
579
|
if (node.type !== 'Member' && node.type !== 'Object') {
|
|
517
580
|
logger.error({
|
|
581
|
+
...baseMessage,
|
|
518
582
|
message: `Expected Object, received ${JSON.stringify(
|
|
519
583
|
// @ts-ignore Yes, TypeScript, this SHOULD be unexpected. This is why we’re validating.
|
|
520
584
|
node.type,
|
|
521
585
|
)}`,
|
|
522
|
-
filename,
|
|
523
|
-
node,
|
|
524
|
-
src,
|
|
525
586
|
});
|
|
526
587
|
}
|
|
527
588
|
|
|
@@ -530,7 +591,7 @@ export function validateTokenMemberNode(node: MemberNode, { filename, src, logge
|
|
|
530
591
|
const $type = rootMembers.$type as StringNode;
|
|
531
592
|
|
|
532
593
|
if (!$value) {
|
|
533
|
-
logger.error({ message: 'Token missing $value'
|
|
594
|
+
logger.error({ ...baseMessage, message: 'Token missing $value' });
|
|
534
595
|
}
|
|
535
596
|
// If top-level value is a valid alias, this is valid (no need for $type)
|
|
536
597
|
// ⚠️ Important: ALL Object and Array nodes below will need to check for aliases within!
|
|
@@ -540,7 +601,7 @@ export function validateTokenMemberNode(node: MemberNode, { filename, src, logge
|
|
|
540
601
|
}
|
|
541
602
|
|
|
542
603
|
if (!$type) {
|
|
543
|
-
logger.error({ message: 'Token missing $type'
|
|
604
|
+
logger.error({ ...baseMessage, message: 'Token missing $type' });
|
|
544
605
|
}
|
|
545
606
|
|
|
546
607
|
switch ($type.value) {
|
|
@@ -581,10 +642,9 @@ export function validateTokenMemberNode(node: MemberNode, { filename, src, logge
|
|
|
581
642
|
}
|
|
582
643
|
} else {
|
|
583
644
|
logger.error({
|
|
645
|
+
...baseMessage,
|
|
584
646
|
message: `Expected shadow object or array of shadow objects, received ${$value.type}`,
|
|
585
|
-
filename,
|
|
586
647
|
node: $value,
|
|
587
|
-
src,
|
|
588
648
|
});
|
|
589
649
|
}
|
|
590
650
|
break;
|
|
@@ -593,21 +653,37 @@ export function validateTokenMemberNode(node: MemberNode, { filename, src, logge
|
|
|
593
653
|
// extensions
|
|
594
654
|
case 'boolean': {
|
|
595
655
|
if ($value.type !== 'Boolean') {
|
|
596
|
-
logger.error({
|
|
656
|
+
logger.error({
|
|
657
|
+
...baseMessage,
|
|
658
|
+
message: `Expected boolean, received ${$value.type}`,
|
|
659
|
+
node: $value,
|
|
660
|
+
});
|
|
597
661
|
}
|
|
598
662
|
break;
|
|
599
663
|
}
|
|
600
664
|
case 'link': {
|
|
601
665
|
if ($value.type !== 'String') {
|
|
602
|
-
logger.error({
|
|
666
|
+
logger.error({
|
|
667
|
+
...baseMessage,
|
|
668
|
+
message: `Expected string, received ${$value.type}`,
|
|
669
|
+
node: $value,
|
|
670
|
+
});
|
|
603
671
|
} else if ($value.value === '') {
|
|
604
|
-
logger.error({
|
|
672
|
+
logger.error({
|
|
673
|
+
...baseMessage,
|
|
674
|
+
message: 'Expected URL, received empty string',
|
|
675
|
+
node: $value,
|
|
676
|
+
});
|
|
605
677
|
}
|
|
606
678
|
break;
|
|
607
679
|
}
|
|
608
680
|
case 'string': {
|
|
609
681
|
if ($value.type !== 'String') {
|
|
610
|
-
logger.error({
|
|
682
|
+
logger.error({
|
|
683
|
+
...baseMessage,
|
|
684
|
+
message: `Expected string, received ${$value.type}`,
|
|
685
|
+
node: $value,
|
|
686
|
+
});
|
|
611
687
|
}
|
|
612
688
|
break;
|
|
613
689
|
}
|
|
@@ -631,15 +707,18 @@ export function validateTokenMemberNode(node: MemberNode, { filename, src, logge
|
|
|
631
707
|
}
|
|
632
708
|
case 'typography': {
|
|
633
709
|
if ($value.type !== 'Object') {
|
|
634
|
-
logger.error({
|
|
710
|
+
logger.error({
|
|
711
|
+
...baseMessage,
|
|
712
|
+
message: `Expected object, received ${$value.type}`,
|
|
713
|
+
node: $value,
|
|
714
|
+
});
|
|
635
715
|
break;
|
|
636
716
|
}
|
|
637
717
|
if ($value.members.length === 0) {
|
|
638
718
|
logger.error({
|
|
719
|
+
...baseMessage,
|
|
639
720
|
message: 'Empty typography token. Must contain at least 1 property.',
|
|
640
|
-
filename,
|
|
641
721
|
node: $value,
|
|
642
|
-
src,
|
|
643
722
|
});
|
|
644
723
|
}
|
|
645
724
|
validateMembersAs(
|
|
@@ -675,6 +754,8 @@ export default function validateTokenNode(
|
|
|
675
754
|
node: MemberNode,
|
|
676
755
|
{ config, filename, logger, parent, src, subpath, $typeInheritance }: ValidateTokenNodeOptions,
|
|
677
756
|
): TokenNormalized | undefined {
|
|
757
|
+
// const start = performance.now();
|
|
758
|
+
|
|
678
759
|
// don’t validate $value
|
|
679
760
|
if (subpath.includes('$value') || node.value.type !== 'Object') {
|
|
680
761
|
return;
|
|
@@ -696,7 +777,14 @@ export default function validateTokenNode(
|
|
|
696
777
|
const id = subpath.join('.');
|
|
697
778
|
|
|
698
779
|
if (!subpath.includes('.$value') && members.value) {
|
|
699
|
-
logger.warn({
|
|
780
|
+
logger.warn({
|
|
781
|
+
group: 'parser',
|
|
782
|
+
label: 'validate',
|
|
783
|
+
message: `Group ${id} has "value". Did you mean "$value"?`,
|
|
784
|
+
filename,
|
|
785
|
+
node,
|
|
786
|
+
src,
|
|
787
|
+
});
|
|
700
788
|
}
|
|
701
789
|
|
|
702
790
|
const extensions = members.$extensions ? getObjMembers(members.$extensions as ObjectNode) : undefined;
|
|
@@ -714,7 +802,7 @@ export default function validateTokenNode(
|
|
|
714
802
|
}
|
|
715
803
|
}
|
|
716
804
|
if (parent$type && !members.$type) {
|
|
717
|
-
|
|
805
|
+
injectObjMembers(
|
|
718
806
|
// @ts-ignore
|
|
719
807
|
sourceNode.value,
|
|
720
808
|
[parent$type],
|
|
@@ -797,5 +885,11 @@ export default function validateTokenNode(
|
|
|
797
885
|
}
|
|
798
886
|
}
|
|
799
887
|
|
|
888
|
+
// logger.debug({
|
|
889
|
+
// message: `${token.id}: validateTokenNode`,
|
|
890
|
+
// group: 'parser', label: 'validate',
|
|
891
|
+
// label: 'validate',
|
|
892
|
+
// timing: performance.now() - start,
|
|
893
|
+
// });
|
|
800
894
|
return token;
|
|
801
895
|
}
|