@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.
Files changed (77) hide show
  1. package/CHANGELOG.md +41 -11
  2. package/dist/build/index.d.ts +1 -0
  3. package/dist/build/index.d.ts.map +1 -0
  4. package/dist/build/index.js +11 -10
  5. package/dist/build/index.js.map +1 -1
  6. package/dist/config.d.ts +1 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +42 -21
  9. package/dist/config.js.map +1 -1
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/lib/code-frame.d.ts +1 -0
  13. package/dist/lib/code-frame.d.ts.map +1 -0
  14. package/dist/lint/index.d.ts +1 -0
  15. package/dist/lint/index.d.ts.map +1 -0
  16. package/dist/lint/index.js +8 -5
  17. package/dist/lint/index.js.map +1 -1
  18. package/dist/lint/plugin-core/index.d.ts +1 -0
  19. package/dist/lint/plugin-core/index.d.ts.map +1 -0
  20. package/dist/lint/plugin-core/lib/docs.d.ts +1 -0
  21. package/dist/lint/plugin-core/lib/docs.d.ts.map +1 -0
  22. package/dist/lint/plugin-core/rules/a11y-min-contrast.d.ts +1 -0
  23. package/dist/lint/plugin-core/rules/a11y-min-contrast.d.ts.map +1 -0
  24. package/dist/lint/plugin-core/rules/a11y-min-font-size.d.ts +1 -0
  25. package/dist/lint/plugin-core/rules/a11y-min-font-size.d.ts.map +1 -0
  26. package/dist/lint/plugin-core/rules/colorspace.d.ts +1 -0
  27. package/dist/lint/plugin-core/rules/colorspace.d.ts.map +1 -0
  28. package/dist/lint/plugin-core/rules/consistent-naming.d.ts +1 -0
  29. package/dist/lint/plugin-core/rules/consistent-naming.d.ts.map +1 -0
  30. package/dist/lint/plugin-core/rules/descriptions.d.ts +1 -0
  31. package/dist/lint/plugin-core/rules/descriptions.d.ts.map +1 -0
  32. package/dist/lint/plugin-core/rules/duplicate-values.d.ts +1 -0
  33. package/dist/lint/plugin-core/rules/duplicate-values.d.ts.map +1 -0
  34. package/dist/lint/plugin-core/rules/max-gamut.d.ts +1 -0
  35. package/dist/lint/plugin-core/rules/max-gamut.d.ts.map +1 -0
  36. package/dist/lint/plugin-core/rules/required-children.d.ts +1 -0
  37. package/dist/lint/plugin-core/rules/required-children.d.ts.map +1 -0
  38. package/dist/lint/plugin-core/rules/required-modes.d.ts +1 -0
  39. package/dist/lint/plugin-core/rules/required-modes.d.ts.map +1 -0
  40. package/dist/lint/plugin-core/rules/required-typography-properties.d.ts +1 -0
  41. package/dist/lint/plugin-core/rules/required-typography-properties.d.ts.map +1 -0
  42. package/dist/logger.d.ts +4 -3
  43. package/dist/logger.d.ts.map +1 -0
  44. package/dist/logger.js +25 -14
  45. package/dist/logger.js.map +1 -1
  46. package/dist/parse/alias.d.ts +13 -3
  47. package/dist/parse/alias.d.ts.map +1 -0
  48. package/dist/parse/alias.js +94 -43
  49. package/dist/parse/alias.js.map +1 -1
  50. package/dist/parse/index.d.ts +1 -0
  51. package/dist/parse/index.d.ts.map +1 -0
  52. package/dist/parse/index.js +58 -25
  53. package/dist/parse/index.js.map +1 -1
  54. package/dist/parse/json.d.ts +3 -3
  55. package/dist/parse/json.d.ts.map +1 -0
  56. package/dist/parse/json.js +5 -7
  57. package/dist/parse/json.js.map +1 -1
  58. package/dist/parse/normalize.d.ts +1 -0
  59. package/dist/parse/normalize.d.ts.map +1 -0
  60. package/dist/parse/normalize.js +8 -6
  61. package/dist/parse/normalize.js.map +1 -1
  62. package/dist/parse/validate.d.ts +1 -0
  63. package/dist/parse/validate.d.ts.map +1 -0
  64. package/dist/parse/validate.js +194 -110
  65. package/dist/parse/validate.js.map +1 -1
  66. package/dist/types.d.ts +1 -0
  67. package/dist/types.d.ts.map +1 -0
  68. package/package.json +2 -2
  69. package/src/build/index.ts +11 -10
  70. package/src/config.ts +42 -22
  71. package/src/lint/index.ts +8 -6
  72. package/src/logger.ts +30 -20
  73. package/src/parse/alias.ts +114 -45
  74. package/src/parse/index.ts +59 -28
  75. package/src/parse/json.ts +6 -8
  76. package/src/parse/normalize.ts +8 -6
  77. package/src/parse/validate.ts +204 -110
@@ -87,19 +87,26 @@ function validateMembersAs(
87
87
  { filename, src, logger }: ValidateOptions,
88
88
  ) {
89
89
  const members = getObjMembers($value);
90
- for (const property in properties) {
91
- const { validator, required } = properties[property]!;
92
- if (!members[property]) {
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({ message: `Missing required property "${property}"`, filename, node: $value, src });
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 value = members[property];
99
- if (isMaybeAlias(value)) {
100
- validateAliasSyntax(value, node, { filename, src, logger });
105
+ const memberValue = members[name];
106
+ if (isMaybeAlias(memberValue)) {
107
+ validateAliasSyntax(memberValue, node, { filename, src, logger });
101
108
  } else {
102
- validator(value, node, { filename, src, logger });
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({ message: `Invalid alias: ${print($value)}`, filename, node: $value, src });
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({ message: `Expected object, received ${$value.type}`, filename, node: $value, src });
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', filename, node: $value, src });
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({ message: `Expected string, received ${print(v)}`, filename, node: v, src });
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({ message: `Unsupported colorspace ${print(v)}`, filename, node: v, src });
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({ message: `Expected array, received ${print(v)}`, filename, node: v, src });
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({ message: `Invalid hex color ${print(v)}`, filename, node: v, src });
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({ message: `Expected object, received ${$value.type}`, filename, node: $value, src });
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)}`, filename, node: $value, src });
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".', filename, node: $value, src });
279
+ logger.error({ ...baseMessage, message: 'Missing required property "value".' });
245
280
  }
246
281
  if (!unit) {
247
- logger.error({ message: 'Missing required property "unit".', filename, node: $value, src });
282
+ logger.error({ ...baseMessage, message: 'Missing required property "unit".' });
248
283
  }
249
284
  if (value!.type !== 'Number') {
250
- logger.error({ message: `Expected number, received ${value!.type}`, filename, node: value, src });
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}`, filename, node: $value, src });
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', filename, node: $value, src });
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)}`, filename, node: $value, src });
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".', filename, node: $value, src });
331
+ logger.error({ ...baseMessage, message: 'Missing required property "value".' });
291
332
  }
292
333
  if (!unit) {
293
- logger.error({ message: 'Missing required property "unit".', filename, node: $value, src });
334
+ logger.error({ ...baseMessage, message: 'Missing required property "unit".' });
294
335
  }
295
336
  if (value?.type !== 'Number') {
296
- logger.error({ message: `Expected number, received ${value?.type}`, filename, node: value, src });
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({ message: `Expected unit "ms" or "s", received ${print(unit!)}`, filename, node: unit, src });
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}`, filename, node: $value, src });
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', filename, node: $value, src });
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)}`, filename, node: $value, src });
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', filename, node: $value, src });
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)}`, filename, node: $value, src });
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({ message: `Expected number, received ${$value.type}`, filename, node: $value, src });
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({ message: `Expected boolean, received ${$value.type}`, filename, node: $value, src });
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({ message: `Expected Object, received ${$value.type}`, filename, node: $value, src });
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}"`, filename, node: $value, src });
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}`, filename, node: $value, src });
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}`, filename, node: $value, src });
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({ message: `Expected object, received ${$value.type}`, filename, node: $value, src });
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', filename, node, src });
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', filename, node, src });
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({ message: `Expected boolean, received ${$value.type}`, filename, node: $value, src });
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({ message: `Expected string, received ${$value.type}`, filename, node: $value, src });
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({ message: 'Expected URL, received empty string', filename, node: $value, src });
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({ message: `Expected string, received ${$value.type}`, filename, node: $value, src });
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({ message: `Expected object, received ${$value.type}`, filename, node: $value, src });
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({ message: `Group ${id} has "value". Did you mean "$value"?`, filename, node, src });
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
- sourceNode.value = injectObjMembers(
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
  }