@terrazzo/parser 0.4.0 → 0.6.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 (80) hide show
  1. package/CHANGELOG.md +62 -12
  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 +13 -14
  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/duplicate-values.js +1 -1
  35. package/dist/lint/plugin-core/rules/duplicate-values.js.map +1 -1
  36. package/dist/lint/plugin-core/rules/max-gamut.d.ts +1 -0
  37. package/dist/lint/plugin-core/rules/max-gamut.d.ts.map +1 -0
  38. package/dist/lint/plugin-core/rules/required-children.d.ts +1 -0
  39. package/dist/lint/plugin-core/rules/required-children.d.ts.map +1 -0
  40. package/dist/lint/plugin-core/rules/required-modes.d.ts +1 -0
  41. package/dist/lint/plugin-core/rules/required-modes.d.ts.map +1 -0
  42. package/dist/lint/plugin-core/rules/required-typography-properties.d.ts +1 -0
  43. package/dist/lint/plugin-core/rules/required-typography-properties.d.ts.map +1 -0
  44. package/dist/logger.d.ts +4 -3
  45. package/dist/logger.d.ts.map +1 -0
  46. package/dist/logger.js +25 -14
  47. package/dist/logger.js.map +1 -1
  48. package/dist/parse/alias.d.ts +31 -48
  49. package/dist/parse/alias.d.ts.map +1 -0
  50. package/dist/parse/alias.js +281 -175
  51. package/dist/parse/alias.js.map +1 -1
  52. package/dist/parse/index.d.ts +1 -0
  53. package/dist/parse/index.d.ts.map +1 -0
  54. package/dist/parse/index.js +59 -70
  55. package/dist/parse/index.js.map +1 -1
  56. package/dist/parse/json.d.ts +3 -3
  57. package/dist/parse/json.d.ts.map +1 -0
  58. package/dist/parse/json.js +5 -7
  59. package/dist/parse/json.js.map +1 -1
  60. package/dist/parse/normalize.d.ts +1 -0
  61. package/dist/parse/normalize.d.ts.map +1 -0
  62. package/dist/parse/normalize.js +13 -7
  63. package/dist/parse/normalize.js.map +1 -1
  64. package/dist/parse/validate.d.ts +6 -0
  65. package/dist/parse/validate.d.ts.map +1 -0
  66. package/dist/parse/validate.js +205 -125
  67. package/dist/parse/validate.js.map +1 -1
  68. package/dist/types.d.ts +1 -0
  69. package/dist/types.d.ts.map +1 -0
  70. package/package.json +2 -2
  71. package/src/build/index.ts +13 -14
  72. package/src/config.ts +42 -22
  73. package/src/lint/index.ts +8 -6
  74. package/src/lint/plugin-core/rules/duplicate-values.ts +1 -1
  75. package/src/logger.ts +30 -20
  76. package/src/parse/alias.ts +330 -194
  77. package/src/parse/index.ts +59 -73
  78. package/src/parse/json.ts +6 -8
  79. package/src/parse/normalize.ts +14 -7
  80. package/src/parse/validate.ts +215 -128
@@ -60,33 +60,54 @@ function isMaybeAlias(node) {
60
60
  /** Assert object members match given types */
61
61
  function validateMembersAs($value, properties, node, { filename, src, logger }) {
62
62
  const members = getObjMembers($value);
63
- for (const property in properties) {
64
- const { validator, required } = properties[property];
65
- if (!members[property]) {
63
+ for (const [name, value] of Object.entries(properties)) {
64
+ const { validator, required } = value;
65
+ if (!members[name]) {
66
66
  if (required) {
67
- logger.error({ message: `Missing required property "${property}"`, filename, node: $value, src });
67
+ logger.error({
68
+ group: 'parser',
69
+ label: 'validate',
70
+ message: `Missing required property "${name}"`,
71
+ filename,
72
+ node: $value,
73
+ src,
74
+ });
68
75
  }
69
76
  continue;
70
77
  }
71
- const value = members[property];
72
- if (isMaybeAlias(value)) {
73
- validateAliasSyntax(value, node, { filename, src, logger });
78
+ const memberValue = members[name];
79
+ if (isMaybeAlias(memberValue)) {
80
+ validateAliasSyntax(memberValue, node, { filename, src, logger });
74
81
  }
75
82
  else {
76
- validator(value, node, { filename, src, logger });
83
+ validator(memberValue, node, { filename, src, logger });
77
84
  }
78
85
  }
79
86
  }
80
87
  /** Verify an Alias $value is formatted correctly */
81
88
  export function validateAliasSyntax($value, _node, { filename, src, logger }) {
82
89
  if ($value.type !== 'String' || !isAlias($value.value)) {
83
- logger.error({ message: `Invalid alias: ${print($value)}`, filename, node: $value, src });
90
+ logger.error({
91
+ group: 'parser',
92
+ label: 'validate',
93
+ message: `Invalid alias: ${print($value)}`,
94
+ filename,
95
+ node: $value,
96
+ src,
97
+ });
84
98
  }
85
99
  }
86
100
  /** Verify a Border token is valid */
87
101
  export function validateBorder($value, node, { filename, src, logger }) {
88
102
  if ($value.type !== 'Object') {
89
- logger.error({ message: `Expected object, received ${$value.type}`, filename, node: $value, src });
103
+ logger.error({
104
+ group: 'parser',
105
+ label: 'validate',
106
+ message: `Expected object, received ${$value.type}`,
107
+ filename,
108
+ node: $value,
109
+ src,
110
+ });
90
111
  }
91
112
  else {
92
113
  validateMembersAs($value, {
@@ -98,6 +119,7 @@ export function validateBorder($value, node, { filename, src, logger }) {
98
119
  }
99
120
  /** Verify a Color token is valid */
100
121
  export function validateColor($value, node, { filename, src, logger }) {
122
+ const baseMessage = { group: 'parser', label: 'validate', filename, node: $value, src };
101
123
  if ($value.type === 'String') {
102
124
  // TODO: enable when object notation is finalized
103
125
  // logger.warn({
@@ -107,7 +129,7 @@ export function validateColor($value, node, { filename, src, logger }) {
107
129
  // src,
108
130
  // });
109
131
  if ($value.value === '') {
110
- logger.error({ message: 'Expected color, received empty string', filename, node: $value, src });
132
+ logger.error({ ...baseMessage, message: 'Expected color, received empty string' });
111
133
  }
112
134
  }
113
135
  else if ($value.type === 'Object') {
@@ -115,10 +137,18 @@ export function validateColor($value, node, { filename, src, logger }) {
115
137
  colorSpace: {
116
138
  validator: (v) => {
117
139
  if (v.type !== 'String') {
118
- logger.error({ message: `Expected string, received ${print(v)}`, filename, node: v, src });
140
+ logger.error({
141
+ ...baseMessage,
142
+ message: `Expected string, received ${print(v)}`,
143
+ node: v,
144
+ });
119
145
  }
120
146
  if (!VALID_COLORSPACES.has(v.value)) {
121
- logger.error({ message: `Unsupported colorspace ${print(v)}`, filename, node: v, src });
147
+ logger.error({
148
+ ...baseMessage,
149
+ message: `Unsupported colorspace ${print(v)}`,
150
+ node: v,
151
+ });
122
152
  }
123
153
  },
124
154
  required: true,
@@ -126,24 +156,26 @@ export function validateColor($value, node, { filename, src, logger }) {
126
156
  channels: {
127
157
  validator: (v) => {
128
158
  if (v.type !== 'Array') {
129
- logger.error({ message: `Expected array, received ${print(v)}`, filename, node: v, src });
159
+ logger.error({
160
+ ...baseMessage,
161
+ message: `Expected array, received ${print(v)}`,
162
+ node: v,
163
+ });
130
164
  }
131
165
  else {
132
166
  if (v.elements?.length !== 3) {
133
167
  logger.error({
168
+ ...baseMessage,
134
169
  message: `Expected 3 channels, received ${v.elements?.length ?? 0}`,
135
- filename,
136
170
  node: v,
137
- src,
138
171
  });
139
172
  }
140
173
  for (const element of v.elements) {
141
174
  if (element.value.type !== 'Number') {
142
175
  logger.error({
176
+ ...baseMessage,
143
177
  message: `Expected number, received ${print(element.value)}`,
144
- filename,
145
178
  node: element,
146
- src,
147
179
  });
148
180
  }
149
181
  }
@@ -161,7 +193,11 @@ export function validateColor($value, node, { filename, src, logger }) {
161
193
  v.value.length === 5 + 1 ||
162
194
  v.value.length === 7 + 1 ||
163
195
  !/^#[a-f0-9]{3,8}$/i.test(v.value)) {
164
- logger.error({ message: `Invalid hex color ${print(v)}`, filename, node: v, src });
196
+ logger.error({
197
+ ...baseMessage,
198
+ message: `Invalid hex color ${print(v)}`,
199
+ node: v,
200
+ });
165
201
  }
166
202
  },
167
203
  },
@@ -169,29 +205,24 @@ export function validateColor($value, node, { filename, src, logger }) {
169
205
  }, node, { filename, src, logger });
170
206
  }
171
207
  else {
172
- logger.error({ message: `Expected object, received ${$value.type}`, filename, node: $value, src });
208
+ logger.error({
209
+ ...baseMessage,
210
+ message: `Expected object, received ${$value.type}`,
211
+ node: $value,
212
+ });
173
213
  }
174
214
  }
175
215
  /** Verify a Cubic Bézier token is valid */
176
216
  export function validateCubicBezier($value, _node, { filename, src, logger }) {
217
+ const baseMessage = { group: 'parser', label: 'validate', filename, node: $value, src };
177
218
  if ($value.type !== 'Array') {
178
- logger.error({ message: `Expected array of strings, received ${print($value)}`, filename, node: $value, src });
219
+ logger.error({ ...baseMessage, message: `Expected array of numbers, received ${print($value)}` });
179
220
  }
180
- else if (!$value.elements.every((e) => e.value.type === 'Number' || (e.value.type === 'String' && isAlias(e.value.value)))) {
181
- logger.error({
182
- message: 'Expected an array of 4 numbers, received some non-numbers',
183
- filename,
184
- node: $value,
185
- src,
186
- });
221
+ else if (!$value.elements.every((e) => e.value.type === 'Number')) {
222
+ logger.error({ ...baseMessage, message: 'Expected an array of 4 numbers, received some non-numbers' });
187
223
  }
188
224
  else if ($value.elements.length !== 4) {
189
- logger.error({
190
- message: `Expected an array of 4 numbers, received ${$value.elements.length}`,
191
- filename,
192
- node: $value,
193
- src,
194
- });
225
+ logger.error({ ...baseMessage, message: `Expected an array of 4 numbers, received ${$value.elements.length}` });
195
226
  }
196
227
  }
197
228
  /** Verify a Dimension token is valid */
@@ -199,46 +230,49 @@ export function validateDimension($value, _node, { filename, src, logger }) {
199
230
  if ($value.type === 'Number' && $value.value === 0) {
200
231
  return; // `0` is a valid number
201
232
  }
233
+ const baseMessage = { group: 'parser', label: 'validate', filename, node: $value, src };
234
+ // Give priority to object notation because it’s a faster code path
202
235
  if ($value.type === 'Object') {
203
236
  const { value, unit } = getObjMembers($value);
204
237
  if (!value) {
205
- logger.error({ message: 'Missing required property "value".', filename, node: $value, src });
238
+ logger.error({ ...baseMessage, message: 'Missing required property "value".' });
206
239
  }
207
240
  if (!unit) {
208
- logger.error({ message: 'Missing required property "unit".', filename, node: $value, src });
241
+ logger.error({ ...baseMessage, message: 'Missing required property "unit".' });
209
242
  }
210
243
  if (value.type !== 'Number') {
211
- logger.error({ message: `Expected number, received ${value.type}`, filename, node: value, src });
244
+ logger.error({
245
+ ...baseMessage,
246
+ message: `Expected number, received ${value.type}`,
247
+ node: value,
248
+ });
212
249
  }
213
250
  if (!['px', 'em', 'rem'].includes(unit.value)) {
214
251
  logger.error({
252
+ ...baseMessage,
215
253
  message: `Expected unit "px", "em", or "rem", received ${print(unit)}`,
216
- filename,
217
254
  node: unit,
218
- src,
219
255
  });
220
256
  }
221
257
  return;
222
258
  }
223
259
  // Backwards compat: string
224
260
  if ($value.type !== 'String') {
225
- logger.error({ message: `Expected string, received ${$value.type}`, filename, node: $value, src });
261
+ logger.error({ ...baseMessage, message: `Expected string, received ${$value.type}` });
226
262
  }
227
263
  const value = $value.value.match(/^-?[0-9.]+/)?.[0];
228
264
  const unit = $value.value.replace(value, '');
229
265
  if ($value.value === '') {
230
- logger.error({ message: 'Expected dimension, received empty string', filename, node: $value, src });
266
+ logger.error({ ...baseMessage, message: 'Expected dimension, received empty string' });
231
267
  }
232
268
  else if (!['px', 'em', 'rem'].includes(unit)) {
233
269
  logger.error({
270
+ ...baseMessage,
234
271
  message: `Expected unit "px", "em", or "rem", received ${JSON.stringify(unit || $value.value)}`,
235
- filename,
236
- node: $value,
237
- src,
238
272
  });
239
273
  }
240
274
  else if (!Number.isFinite(Number.parseFloat(value))) {
241
- logger.error({ message: `Expected dimension with units, received ${print($value)}`, filename, node: $value, src });
275
+ logger.error({ ...baseMessage, message: `Expected dimension with units, received ${print($value)}` });
242
276
  }
243
277
  }
244
278
  /** Verify a Duration token is valid */
@@ -246,106 +280,97 @@ export function validateDuration($value, _node, { filename, src, logger }) {
246
280
  if ($value.type === 'Number' && $value.value === 0) {
247
281
  return; // `0` is a valid number
248
282
  }
283
+ const baseMessage = { group: 'parser', label: 'validate', filename, node: $value, src };
284
+ // Give priority to object notation because it’s a faster code path
249
285
  if ($value.type === 'Object') {
250
286
  const { value, unit } = getObjMembers($value);
251
287
  if (!value) {
252
- logger.error({ message: 'Missing required property "value".', filename, node: $value, src });
288
+ logger.error({ ...baseMessage, message: 'Missing required property "value".' });
253
289
  }
254
290
  if (!unit) {
255
- logger.error({ message: 'Missing required property "unit".', filename, node: $value, src });
291
+ logger.error({ ...baseMessage, message: 'Missing required property "unit".' });
256
292
  }
257
293
  if (value?.type !== 'Number') {
258
- logger.error({ message: `Expected number, received ${value?.type}`, filename, node: value, src });
294
+ logger.error({
295
+ ...baseMessage,
296
+ message: `Expected number, received ${value?.type}`,
297
+ node: value,
298
+ });
259
299
  }
260
300
  if (!['ms', 's'].includes(unit.value)) {
261
- logger.error({ message: `Expected unit "ms" or "s", received ${print(unit)}`, filename, node: unit, src });
301
+ logger.error({
302
+ ...baseMessage,
303
+ message: `Expected unit "ms" or "s", received ${print(unit)}`,
304
+ node: unit,
305
+ });
262
306
  }
263
307
  return;
264
308
  }
265
309
  // Backwards compat: string
266
310
  if ($value.type !== 'String') {
267
- logger.error({ message: `Expected string, received ${$value.type}`, filename, node: $value, src });
311
+ logger.error({ ...baseMessage, message: `Expected string, received ${$value.type}` });
268
312
  }
269
313
  const value = $value.value.match(/^-?[0-9.]+/)?.[0];
270
314
  const unit = $value.value.replace(value, '');
271
315
  if ($value.value === '') {
272
- logger.error({ message: 'Expected duration, received empty string', filename, node: $value, src });
316
+ logger.error({ ...baseMessage, message: 'Expected duration, received empty string' });
273
317
  }
274
318
  else if (!['ms', 's'].includes(unit)) {
275
319
  logger.error({
320
+ ...baseMessage,
276
321
  message: `Expected unit "ms" or "s", received ${JSON.stringify(unit || $value.value)}`,
277
- filename,
278
- node: $value,
279
- src,
280
322
  });
281
323
  }
282
324
  else if (!Number.isFinite(Number.parseFloat(value))) {
283
- logger.error({ message: `Expected duration with units, received ${print($value)}`, filename, node: $value, src });
325
+ logger.error({ ...baseMessage, message: `Expected duration with units, received ${print($value)}` });
284
326
  }
285
327
  }
286
328
  /** Verify a Font Family token is valid */
287
329
  export function validateFontFamily($value, _node, { filename, src, logger }) {
330
+ const baseMessage = { group: 'parser', label: 'validate', filename, node: $value, src };
288
331
  if ($value.type !== 'String' && $value.type !== 'Array') {
289
- logger.error({
290
- message: `Expected string or array of strings, received ${$value.type}`,
291
- filename,
292
- node: $value,
293
- src,
294
- });
332
+ logger.error({ ...baseMessage, message: `Expected string or array of strings, received ${$value.type}` });
295
333
  }
296
334
  if ($value.type === 'String' && $value.value === '') {
297
- logger.error({ message: 'Expected font family name, received empty string', filename, node: $value, src });
335
+ logger.error({ ...baseMessage, message: 'Expected font family name, received empty string' });
298
336
  }
299
337
  if ($value.type === 'Array' && !$value.elements.every((e) => e.value.type === 'String' && e.value.value !== '')) {
300
338
  logger.error({
339
+ ...baseMessage,
301
340
  message: 'Expected an array of strings, received some non-strings or empty strings',
302
- filename,
303
- node: $value,
304
- src,
305
341
  });
306
342
  }
307
343
  }
308
344
  /** Verify a Font Weight token is valid */
309
345
  export function validateFontWeight($value, _node, { filename, src, logger }) {
346
+ const baseMessage = { group: 'parser', label: 'validate', filename, node: $value, src };
310
347
  if ($value.type !== 'String' && $value.type !== 'Number') {
311
- logger.error({
312
- message: `Expected a font weight name or number 0–1000, received ${$value.type}`,
313
- filename,
314
- node: $value,
315
- src,
316
- });
348
+ logger.error({ ...baseMessage, message: `Expected a font weight name or number 0–1000, received ${$value.type}` });
317
349
  }
318
350
  if ($value.type === 'String' && !FONT_WEIGHT_VALUES.has($value.value)) {
319
351
  logger.error({
352
+ ...baseMessage,
320
353
  message: `Unknown font weight ${print($value)}. Expected one of: ${listFormat.format([...FONT_WEIGHT_VALUES])}.`,
321
- filename,
322
- node: $value,
323
- src,
324
354
  });
325
355
  }
326
356
  if ($value.type === 'Number' && ($value.value < 0 || $value.value > 1000)) {
327
- logger.error({ message: `Expected number 0–1000, received ${print($value)}`, filename, node: $value, src });
357
+ logger.error({ ...baseMessage, message: `Expected number 0–1000, received ${print($value)}` });
328
358
  }
329
359
  }
330
360
  /** Verify a Gradient token is valid */
331
361
  export function validateGradient($value, _node, { filename, src, logger }) {
362
+ const baseMessage = { group: 'parser', label: 'validate', filename, node: $value, src };
332
363
  if ($value.type !== 'Array') {
333
- logger.error({
334
- message: `Expected array of gradient stops, received ${$value.type}`,
335
- filename,
336
- node: $value,
337
- src,
338
- });
364
+ logger.error({ ...baseMessage, message: `Expected array of gradient stops, received ${$value.type}` });
339
365
  }
340
366
  else {
341
367
  for (let i = 0; i < $value.elements.length; i++) {
342
368
  const element = $value.elements[i];
343
369
  if (element.value.type !== 'Object') {
344
370
  logger.error({
371
+ ...baseMessage,
345
372
  message: `Stop #${i + 1}: Expected gradient stop, received ${element.value.type}`,
346
- filename,
347
373
  node: element,
348
- src,
349
374
  });
350
375
  break;
351
376
  }
@@ -359,19 +384,40 @@ export function validateGradient($value, _node, { filename, src, logger }) {
359
384
  /** Verify a Number token is valid */
360
385
  export function validateNumber($value, _node, { filename, src, logger }) {
361
386
  if ($value.type !== 'Number') {
362
- logger.error({ message: `Expected number, received ${$value.type}`, filename, node: $value, src });
387
+ logger.error({
388
+ group: 'parser',
389
+ label: 'validate',
390
+ message: `Expected number, received ${$value.type}`,
391
+ filename,
392
+ node: $value,
393
+ src,
394
+ });
363
395
  }
364
396
  }
365
397
  /** Verify a Boolean token is valid */
366
398
  export function validateBoolean($value, _node, { filename, src, logger }) {
367
399
  if ($value.type !== 'Boolean') {
368
- logger.error({ message: `Expected boolean, received ${$value.type}`, filename, node: $value, src });
400
+ logger.error({
401
+ group: 'parser',
402
+ label: 'validate',
403
+ message: `Expected boolean, received ${$value.type}`,
404
+ filename,
405
+ node: $value,
406
+ src,
407
+ });
369
408
  }
370
409
  }
371
410
  /** Verify a Shadow token’s value is valid */
372
411
  export function validateShadowLayer($value, node, { filename, src, logger }) {
373
412
  if ($value.type !== 'Object') {
374
- logger.error({ message: `Expected Object, received ${$value.type}`, filename, node: $value, src });
413
+ logger.error({
414
+ group: 'parser',
415
+ label: 'validate',
416
+ message: `Expected Object, received ${$value.type}`,
417
+ filename,
418
+ node: $value,
419
+ src,
420
+ });
375
421
  }
376
422
  else {
377
423
  validateMembersAs($value, {
@@ -386,16 +432,15 @@ export function validateShadowLayer($value, node, { filename, src, logger }) {
386
432
  }
387
433
  /** Verify a Stroke Style token is valid. */
388
434
  export function validateStrokeStyle($value, node, { filename, src, logger }) {
435
+ const baseMessage = { group: 'parser', label: 'validate', filename, node: $value, src };
389
436
  // note: strokeStyle’s values are NOT aliasable (unless by string, but that breaks validations)
390
437
  if ($value.type === 'String') {
391
438
  if (!STROKE_STYLE_VALUES.has($value.value)) {
392
439
  logger.error({
440
+ ...baseMessage,
393
441
  message: `Unknown stroke style ${print($value)}. Expected one of: ${listFormat.format([
394
442
  ...STROKE_STYLE_VALUES,
395
443
  ])}.`,
396
- filename,
397
- node: $value,
398
- src,
399
444
  });
400
445
  }
401
446
  }
@@ -403,15 +448,17 @@ export function validateStrokeStyle($value, node, { filename, src, logger }) {
403
448
  const strokeMembers = getObjMembers($value);
404
449
  for (const property of ['dashArray', 'lineCap']) {
405
450
  if (!strokeMembers[property]) {
406
- logger.error({ message: `Missing required property "${property}"`, filename, node: $value, src });
451
+ logger.error({ ...baseMessage, message: `Missing required property "${property}"` });
407
452
  }
408
453
  }
409
454
  const { lineCap, dashArray } = strokeMembers;
410
455
  if (lineCap?.type !== 'String' || !STROKE_STYLE_LINE_CAP_VALUES.has(lineCap.value)) {
411
456
  logger.error({
457
+ ...baseMessage,
412
458
  message: `Unknown lineCap value ${print(lineCap)}. Expected one of: ${listFormat.format([
413
459
  ...STROKE_STYLE_LINE_CAP_VALUES,
414
460
  ])}.`,
461
+ node,
415
462
  });
416
463
  }
417
464
  if (dashArray?.type === 'Array') {
@@ -426,26 +473,32 @@ export function validateStrokeStyle($value, node, { filename, src, logger }) {
426
473
  }
427
474
  else {
428
475
  logger.error({
476
+ ...baseMessage,
429
477
  message: 'Expected array of strings, recieved some non-strings or empty strings.',
430
- filename,
431
478
  node: element,
432
- src,
433
479
  });
434
480
  }
435
481
  }
436
482
  }
437
483
  else {
438
- logger.error({ message: `Expected array of strings, received ${dashArray.type}`, filename, node: $value, src });
484
+ logger.error({ ...baseMessage, message: `Expected array of strings, received ${dashArray.type}` });
439
485
  }
440
486
  }
441
487
  else {
442
- logger.error({ message: `Expected string or object, received ${$value.type}`, filename, node: $value, src });
488
+ logger.error({ ...baseMessage, message: `Expected string or object, received ${$value.type}` });
443
489
  }
444
490
  }
445
491
  /** Verify a Transition token is valid */
446
492
  export function validateTransition($value, node, { filename, src, logger }) {
447
493
  if ($value.type !== 'Object') {
448
- logger.error({ message: `Expected object, received ${$value.type}`, filename, node: $value, src });
494
+ logger.error({
495
+ group: 'parser',
496
+ label: 'validate',
497
+ message: `Expected object, received ${$value.type}`,
498
+ filename,
499
+ node: $value,
500
+ src,
501
+ });
449
502
  }
450
503
  else {
451
504
  validateMembersAs($value, {
@@ -461,21 +514,20 @@ export function validateTransition($value, node, { filename, src, logger }) {
461
514
  * really helps in debug messages.
462
515
  */
463
516
  export function validateTokenMemberNode(node, { filename, src, logger }) {
517
+ const baseMessage = { group: 'parser', label: 'validate', filename, node, src };
464
518
  if (node.type !== 'Member' && node.type !== 'Object') {
465
519
  logger.error({
520
+ ...baseMessage,
466
521
  message: `Expected Object, received ${JSON.stringify(
467
522
  // @ts-ignore Yes, TypeScript, this SHOULD be unexpected. This is why we’re validating.
468
523
  node.type)}`,
469
- filename,
470
- node,
471
- src,
472
524
  });
473
525
  }
474
526
  const rootMembers = node.value.type === 'Object' ? getObjMembers(node.value) : {};
475
527
  const $value = rootMembers.$value;
476
528
  const $type = rootMembers.$type;
477
529
  if (!$value) {
478
- logger.error({ message: 'Token missing $value', filename, node, src });
530
+ logger.error({ ...baseMessage, message: 'Token missing $value' });
479
531
  }
480
532
  // If top-level value is a valid alias, this is valid (no need for $type)
481
533
  // ⚠️ Important: ALL Object and Array nodes below will need to check for aliases within!
@@ -484,7 +536,7 @@ export function validateTokenMemberNode(node, { filename, src, logger }) {
484
536
  return;
485
537
  }
486
538
  if (!$type) {
487
- logger.error({ message: 'Token missing $type', filename, node, src });
539
+ logger.error({ ...baseMessage, message: 'Token missing $type' });
488
540
  }
489
541
  switch ($type.value) {
490
542
  case 'color': {
@@ -526,10 +578,9 @@ export function validateTokenMemberNode(node, { filename, src, logger }) {
526
578
  }
527
579
  else {
528
580
  logger.error({
581
+ ...baseMessage,
529
582
  message: `Expected shadow object or array of shadow objects, received ${$value.type}`,
530
- filename,
531
583
  node: $value,
532
- src,
533
584
  });
534
585
  }
535
586
  break;
@@ -537,22 +588,38 @@ export function validateTokenMemberNode(node, { filename, src, logger }) {
537
588
  // extensions
538
589
  case 'boolean': {
539
590
  if ($value.type !== 'Boolean') {
540
- logger.error({ message: `Expected boolean, received ${$value.type}`, filename, node: $value, src });
591
+ logger.error({
592
+ ...baseMessage,
593
+ message: `Expected boolean, received ${$value.type}`,
594
+ node: $value,
595
+ });
541
596
  }
542
597
  break;
543
598
  }
544
599
  case 'link': {
545
600
  if ($value.type !== 'String') {
546
- logger.error({ message: `Expected string, received ${$value.type}`, filename, node: $value, src });
601
+ logger.error({
602
+ ...baseMessage,
603
+ message: `Expected string, received ${$value.type}`,
604
+ node: $value,
605
+ });
547
606
  }
548
607
  else if ($value.value === '') {
549
- logger.error({ message: 'Expected URL, received empty string', filename, node: $value, src });
608
+ logger.error({
609
+ ...baseMessage,
610
+ message: 'Expected URL, received empty string',
611
+ node: $value,
612
+ });
550
613
  }
551
614
  break;
552
615
  }
553
616
  case 'string': {
554
617
  if ($value.type !== 'String') {
555
- logger.error({ message: `Expected string, received ${$value.type}`, filename, node: $value, src });
618
+ logger.error({
619
+ ...baseMessage,
620
+ message: `Expected string, received ${$value.type}`,
621
+ node: $value,
622
+ });
556
623
  }
557
624
  break;
558
625
  }
@@ -575,15 +642,18 @@ export function validateTokenMemberNode(node, { filename, src, logger }) {
575
642
  }
576
643
  case 'typography': {
577
644
  if ($value.type !== 'Object') {
578
- logger.error({ message: `Expected object, received ${$value.type}`, filename, node: $value, src });
645
+ logger.error({
646
+ ...baseMessage,
647
+ message: `Expected object, received ${$value.type}`,
648
+ node: $value,
649
+ });
579
650
  break;
580
651
  }
581
652
  if ($value.members.length === 0) {
582
653
  logger.error({
654
+ ...baseMessage,
583
655
  message: 'Empty typography token. Must contain at least 1 property.',
584
- filename,
585
656
  node: $value,
586
- src,
587
657
  });
588
658
  }
589
659
  validateMembersAs($value, {
@@ -598,7 +668,13 @@ export function validateTokenMemberNode(node, { filename, src, logger }) {
598
668
  }
599
669
  }
600
670
  }
671
+ /**
672
+ * Validate does a little more than validate; it also converts to TokenNormalized
673
+ * and sets up the basic data structure. But aliases are unresolved, and we need
674
+ * a 2nd normalization pass afterward.
675
+ */
601
676
  export default function validateTokenNode(node, { config, filename, logger, parent, src, subpath, $typeInheritance }) {
677
+ // const start = performance.now();
602
678
  // don’t validate $value
603
679
  if (subpath.includes('$value') || node.value.type !== 'Object') {
604
680
  return;
@@ -615,7 +691,14 @@ export default function validateTokenNode(node, { config, filename, logger, pare
615
691
  }
616
692
  const id = subpath.join('.');
617
693
  if (!subpath.includes('.$value') && members.value) {
618
- logger.warn({ message: `Group ${id} has "value". Did you mean "$value"?`, filename, node, src });
694
+ logger.warn({
695
+ group: 'parser',
696
+ label: 'validate',
697
+ message: `Group ${id} has "value". Did you mean "$value"?`,
698
+ filename,
699
+ node,
700
+ src,
701
+ });
619
702
  }
620
703
  const extensions = members.$extensions ? getObjMembers(members.$extensions) : undefined;
621
704
  const sourceNode = structuredClone(node);
@@ -631,7 +714,7 @@ export default function validateTokenNode(node, { config, filename, logger, pare
631
714
  }
632
715
  }
633
716
  if (parent$type && !members.$type) {
634
- sourceNode.value = injectObjMembers(
717
+ injectObjMembers(
635
718
  // @ts-ignore
636
719
  sourceNode.value, [parent$type]);
637
720
  }
@@ -683,28 +766,25 @@ export default function validateTokenNode(node, { config, filename, logger, pare
683
766
  }
684
767
  // handle modes
685
768
  // note that circular refs are avoided here, such as not duplicating `modes`
686
- const modeValues = extensions?.mode
687
- ? getObjMembers(
688
- // @ts-ignore
689
- extensions.mode)
690
- : {};
769
+ const modeValues = extensions?.mode ? getObjMembers(extensions.mode) : {};
691
770
  for (const mode of ['.', ...Object.keys(modeValues)]) {
771
+ const modeValue = mode === '.' ? token.$value : evaluate(modeValues[mode]);
692
772
  token.mode[mode] = {
693
- id: token.id,
694
- // @ts-ignore
695
- $type: token.$type,
696
- // @ts-ignore
697
- $value: mode === '.' ? token.$value : evaluate(modeValues[mode]),
773
+ $value: modeValue,
774
+ originalValue: modeValue,
698
775
  source: {
699
776
  loc: filename ? filename.href : undefined,
700
777
  // @ts-ignore
701
- node: mode === '.' ? structuredClone(token.source.node) : modeValues[mode],
778
+ node: modeValues[mode],
702
779
  },
703
780
  };
704
- if (token.$description) {
705
- token.mode[mode].$description = token.$description;
706
- }
707
781
  }
782
+ // logger.debug({
783
+ // message: `${token.id}: validateTokenNode`,
784
+ // group: 'parser', label: 'validate',
785
+ // label: 'validate',
786
+ // timing: performance.now() - start,
787
+ // });
708
788
  return token;
709
789
  }
710
790
  //# sourceMappingURL=validate.js.map