@terrazzo/parser 0.1.2 → 0.2.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 (118) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/CHANGELOG.md +26 -0
  3. package/CONTRIBUTING.md +0 -12
  4. package/dist/build/index.d.ts +19 -0
  5. package/dist/build/index.js +165 -0
  6. package/dist/build/index.js.map +1 -0
  7. package/dist/config.d.ts +7 -0
  8. package/dist/config.js +269 -0
  9. package/dist/config.js.map +1 -0
  10. package/{index.d.ts → dist/index.d.ts} +1 -5
  11. package/dist/index.js +13 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/lib/code-frame.d.ts +30 -0
  14. package/dist/lib/code-frame.js +108 -0
  15. package/dist/lib/code-frame.js.map +1 -0
  16. package/dist/lint/index.d.ts +11 -0
  17. package/dist/lint/index.js +102 -0
  18. package/dist/lint/index.js.map +1 -0
  19. package/dist/lint/plugin-core/index.d.ts +12 -0
  20. package/dist/lint/plugin-core/index.js +40 -0
  21. package/dist/lint/plugin-core/index.js.map +1 -0
  22. package/dist/lint/plugin-core/lib/docs.d.ts +1 -0
  23. package/dist/lint/plugin-core/lib/docs.js +4 -0
  24. package/dist/lint/plugin-core/lib/docs.js.map +1 -0
  25. package/dist/lint/plugin-core/rules/a11y-min-contrast.d.ts +39 -0
  26. package/dist/lint/plugin-core/rules/a11y-min-contrast.js +58 -0
  27. package/dist/lint/plugin-core/rules/a11y-min-contrast.js.map +1 -0
  28. package/dist/lint/plugin-core/rules/a11y-min-font-size.d.ts +13 -0
  29. package/dist/lint/plugin-core/rules/a11y-min-font-size.js +45 -0
  30. package/dist/lint/plugin-core/rules/a11y-min-font-size.js.map +1 -0
  31. package/dist/lint/plugin-core/rules/colorspace.d.ts +14 -0
  32. package/dist/lint/plugin-core/rules/colorspace.js +85 -0
  33. package/dist/lint/plugin-core/rules/colorspace.js.map +1 -0
  34. package/dist/lint/plugin-core/rules/consistent-naming.d.ts +11 -0
  35. package/dist/lint/plugin-core/rules/consistent-naming.js +49 -0
  36. package/dist/lint/plugin-core/rules/consistent-naming.js.map +1 -0
  37. package/dist/lint/plugin-core/rules/descriptions.d.ts +9 -0
  38. package/dist/lint/plugin-core/rules/descriptions.js +32 -0
  39. package/dist/lint/plugin-core/rules/descriptions.js.map +1 -0
  40. package/dist/lint/plugin-core/rules/duplicate-values.d.ts +9 -0
  41. package/dist/lint/plugin-core/rules/duplicate-values.js +65 -0
  42. package/dist/lint/plugin-core/rules/duplicate-values.js.map +1 -0
  43. package/dist/lint/plugin-core/rules/max-gamut.d.ts +14 -0
  44. package/dist/lint/plugin-core/rules/max-gamut.js +101 -0
  45. package/dist/lint/plugin-core/rules/max-gamut.js.map +1 -0
  46. package/dist/lint/plugin-core/rules/required-children.d.ts +18 -0
  47. package/dist/lint/plugin-core/rules/required-children.js +78 -0
  48. package/dist/lint/plugin-core/rules/required-children.js.map +1 -0
  49. package/dist/lint/plugin-core/rules/required-modes.d.ts +13 -0
  50. package/dist/lint/plugin-core/rules/required-modes.js +52 -0
  51. package/dist/lint/plugin-core/rules/required-modes.js.map +1 -0
  52. package/dist/lint/plugin-core/rules/required-typography-properties.d.ts +10 -0
  53. package/dist/lint/plugin-core/rules/required-typography-properties.js +38 -0
  54. package/dist/lint/plugin-core/rules/required-typography-properties.js.map +1 -0
  55. package/dist/logger.d.ts +76 -0
  56. package/dist/logger.js +123 -0
  57. package/dist/logger.js.map +1 -0
  58. package/dist/parse/alias.d.ts +51 -0
  59. package/dist/parse/alias.js +188 -0
  60. package/dist/parse/alias.js.map +1 -0
  61. package/dist/parse/index.d.ts +27 -0
  62. package/dist/parse/index.js +379 -0
  63. package/dist/parse/index.js.map +1 -0
  64. package/dist/parse/json.d.ts +36 -0
  65. package/dist/parse/json.js +88 -0
  66. package/dist/parse/json.js.map +1 -0
  67. package/dist/parse/normalize.d.ts +23 -0
  68. package/dist/parse/normalize.js +163 -0
  69. package/dist/parse/normalize.js.map +1 -0
  70. package/dist/parse/validate.d.ts +45 -0
  71. package/dist/parse/validate.js +601 -0
  72. package/dist/parse/validate.js.map +1 -0
  73. package/dist/types.d.ts +264 -0
  74. package/dist/types.js +2 -0
  75. package/dist/types.js.map +1 -0
  76. package/package.json +7 -7
  77. package/{build/index.js → src/build/index.ts} +47 -63
  78. package/src/config.ts +280 -0
  79. package/src/index.ts +18 -0
  80. package/{lib/code-frame.js → src/lib/code-frame.ts} +41 -8
  81. package/src/lint/index.ts +135 -0
  82. package/src/lint/plugin-core/index.ts +47 -0
  83. package/src/lint/plugin-core/lib/docs.ts +3 -0
  84. package/src/lint/plugin-core/rules/a11y-min-contrast.ts +91 -0
  85. package/src/lint/plugin-core/rules/a11y-min-font-size.ts +64 -0
  86. package/src/lint/plugin-core/rules/colorspace.ts +101 -0
  87. package/src/lint/plugin-core/rules/consistent-naming.ts +65 -0
  88. package/src/lint/plugin-core/rules/descriptions.ts +41 -0
  89. package/src/lint/plugin-core/rules/duplicate-values.ts +80 -0
  90. package/src/lint/plugin-core/rules/max-gamut.ts +121 -0
  91. package/src/lint/plugin-core/rules/required-children.ts +104 -0
  92. package/src/lint/plugin-core/rules/required-modes.ts +71 -0
  93. package/src/lint/plugin-core/rules/required-typography-properties.ts +53 -0
  94. package/{logger.js → src/logger.ts} +55 -16
  95. package/src/parse/alias.ts +224 -0
  96. package/src/parse/index.ts +457 -0
  97. package/src/parse/json.ts +106 -0
  98. package/{parse/normalize.js → src/parse/normalize.ts} +70 -23
  99. package/{parse/validate.js → src/parse/validate.ts} +159 -227
  100. package/src/types.ts +310 -0
  101. package/build/index.d.ts +0 -113
  102. package/config.d.ts +0 -64
  103. package/config.js +0 -206
  104. package/index.js +0 -35
  105. package/lib/code-frame.d.ts +0 -56
  106. package/lint/index.d.ts +0 -44
  107. package/lint/index.js +0 -59
  108. package/lint/plugin-core/index.d.ts +0 -3
  109. package/lint/plugin-core/index.js +0 -12
  110. package/lint/plugin-core/rules/duplicate-values.d.ts +0 -10
  111. package/lint/plugin-core/rules/duplicate-values.js +0 -68
  112. package/logger.d.ts +0 -71
  113. package/parse/index.d.ts +0 -45
  114. package/parse/index.js +0 -592
  115. package/parse/json.d.ts +0 -30
  116. package/parse/json.js +0 -94
  117. package/parse/normalize.d.ts +0 -3
  118. package/parse/validate.d.ts +0 -43
package/parse/index.js DELETED
@@ -1,592 +0,0 @@
1
- import { evaluate, parse as parseJSON, print } from '@humanwhocodes/momoa';
2
- import { isAlias, parseAlias, pluralize, splitID } from '@terrazzo/token-tools';
3
- import lintRunner from '../lint/index.js';
4
- import Logger from '../logger.js';
5
- import normalize from './normalize.js';
6
- import validate from './validate.js';
7
- import { getObjMembers, injectObjMembers, traverse } from './json.js';
8
-
9
- export * from './validate.js';
10
-
11
- /** @typedef {import("@humanwhocodes/momoa").DocumentNode} DocumentNode */
12
- /** @typedef {import("../config.js").Plugin} Plugin */
13
- /** @typedef {import("../types.js").TokenNormalized} TokenNormalized */
14
- /**
15
- * @typedef {object} ParseResult
16
- * @property {Record<string, TokenNormalized} tokens
17
- * @property {Object[]} src
18
- */
19
- /**
20
- * @typedef {object} ParseInput
21
- * @property {string | object} src
22
- * @property {URL} [filename]
23
- */
24
- /**
25
- * @typedef {object} ParseOptions
26
- * @property {Logger} logger
27
- * @property {import("../config.js").Config} config
28
- * @property {import("yamlToMomoa")} yamlToMomoa
29
- * @property {boolean} [skipLint=false]
30
- * @property {boolean} [continueOnError=false]
31
- */
32
- /**
33
- * Parse
34
- * @param {ParseInput[]} input
35
- * @param {ParseOptions} [options]
36
- * @return {Promise<ParseResult>}
37
- */
38
- export default async function parse(
39
- input,
40
- { logger = new Logger(), skipLint = false, config = {}, continueOnError = false, yamlToMomoa } = {},
41
- ) {
42
- let tokens = {};
43
- // note: only keeps track of sources with locations on disk; in-memory sources are discarded
44
- // (it’s only for reporting line numbers, which doesn’t mean as much for dynamic sources)
45
- const sources = {};
46
-
47
- if (!Array.isArray(input)) {
48
- logger.error({ group: 'parser', task: 'init', message: 'Input must be an array of input objects.' });
49
- }
50
- for (let i = 0; i < input.length; i++) {
51
- if (!input[i] || typeof input[i] !== 'object') {
52
- logger.error({ group: 'parser', task: 'init', message: `Input (${i}) must be an object.` });
53
- }
54
- if (!input[i].src || (typeof input[i].src !== 'string' && typeof input[i].src !== 'object')) {
55
- logger.error({
56
- group: 'parser',
57
- task: 'init',
58
- message: `Input (${i}) missing "src" with a JSON/YAML string, or JSON object.`,
59
- });
60
- }
61
- if (input[i].filename && !(input[i].filename instanceof URL)) {
62
- logger.error({
63
- group: 'parser',
64
- task: 'init',
65
- message: `Input (${i}) "filename" must be a URL (remote or file URL).`,
66
- });
67
- }
68
-
69
- const result = await parseSingle(input[i].src, {
70
- filename: input[i].filename,
71
- logger,
72
- config,
73
- skipLint,
74
- continueOnError,
75
- yamlToMomoa,
76
- });
77
-
78
- tokens = Object.assign(tokens, result.tokens);
79
- if (input[i].filename) {
80
- sources[input[i].filename.protocol === 'file:' ? input[i].filename.href : input[i].filename.href] = {
81
- filename: input[i].filename,
82
- src: result.src,
83
- document: result.document,
84
- };
85
- }
86
- }
87
-
88
- const totalStart = performance.now();
89
-
90
- // 5. Resolve aliases and populate groups
91
- for (const id in tokens) {
92
- if (!Object.hasOwn(tokens, id)) {
93
- continue;
94
- }
95
- const token = tokens[id];
96
- applyAliases(token, {
97
- tokens,
98
- filename: sources[token.source.loc]?.filename,
99
- src: sources[token.source.loc]?.src,
100
- node: token.source.node,
101
- logger,
102
- });
103
- token.mode['.'].$value = token.$value;
104
- if (token.aliasOf) {
105
- token.mode['.'].aliasOf = token.aliasOf;
106
- }
107
- if (token.partialAliasOf) {
108
- token.mode['.'].partialAliasOf = token.partialAliasOf;
109
- }
110
- const { group: parentGroup } = splitID(id);
111
- for (const siblingID in tokens) {
112
- const { group: siblingGroup } = splitID(siblingID);
113
- if (siblingGroup?.startsWith(parentGroup)) {
114
- token.group.tokens.push(siblingID);
115
- }
116
- }
117
- }
118
-
119
- // 6. resolve mode aliases
120
- const modesStart = performance.now();
121
- logger.debug({
122
- group: 'parser',
123
- task: 'modes',
124
- message: 'Start mode resolution',
125
- });
126
- for (const id in tokens) {
127
- if (!Object.hasOwn(tokens, id)) {
128
- continue;
129
- }
130
- for (const mode in tokens[id].mode) {
131
- if (mode === '.') {
132
- continue; // skip shadow of root value
133
- }
134
- applyAliases(tokens[id].mode[mode], {
135
- tokens,
136
- node: tokens[id].mode[mode].source.node,
137
- logger,
138
- src: sources[tokens[id].source.loc]?.src,
139
- });
140
- }
141
- }
142
- logger.debug({
143
- group: 'parser',
144
- task: 'modes',
145
- message: 'Finish token modes',
146
- timing: performance.now() - modesStart,
147
- });
148
-
149
- logger.debug({
150
- group: 'parser',
151
- task: 'core',
152
- message: 'Finish all parser tasks',
153
- timing: performance.now() - totalStart,
154
- });
155
-
156
- if (continueOnError) {
157
- const { errorCount } = logger.stats();
158
- if (errorCount > 0) {
159
- logger.error({
160
- message: `Parser encountered ${errorCount} ${pluralize(errorCount, 'error', 'errors')}. Exiting.`,
161
- });
162
- }
163
- }
164
-
165
- return {
166
- tokens,
167
- sources,
168
- };
169
- }
170
-
171
- /**
172
- * Parse a single input
173
- * @param {string | object} input
174
- * @param {object} options
175
- * @param {URL} [options.filename]
176
- * @param {Logger} [options.logger]
177
- * @param {import("../config.js").Config} [options.config]
178
- * @param {boolean} [options.skipLint]
179
- */
180
- async function parseSingle(input, { filename, logger, config, skipLint, continueOnError = false, yamlToMomoa }) {
181
- // 1. Build AST
182
- let src;
183
- if (typeof input === 'string') {
184
- src = input;
185
- }
186
- const startParsing = performance.now();
187
- logger.debug({ group: 'parser', task: 'parse', message: 'Start tokens parsing' });
188
- let document;
189
- if (typeof input === 'string' && !maybeJSONString(input)) {
190
- if (yamlToMomoa) {
191
- try {
192
- document = yamlToMomoa(input); // if string, but not JSON, attempt YAML
193
- } catch (err) {
194
- logger.error({ message: String(err), filename, src: input, continueOnError });
195
- }
196
- } else {
197
- logger.error({
198
- group: 'parser',
199
- task: 'parse',
200
- message: `Install \`yaml-to-momoa\` package to parse YAML, and pass in as option, e.g.:
201
-
202
- import { parse } from '@terrazzo/parser';
203
- import yamlToMomoa from 'yaml-to-momoa';
204
-
205
- parse(yamlString, { yamlToMomoa });`,
206
- continueOnError: false, // fail here; no point in continuing
207
- });
208
- }
209
- } else {
210
- document = parseJSON(
211
- typeof input === 'string' ? input : JSON.stringify(input, undefined, 2), // everything else: assert it’s JSON-serializable
212
- {
213
- mode: 'jsonc',
214
- },
215
- );
216
- }
217
- if (!src) {
218
- src = print(document, { indent: 2 });
219
- }
220
- logger.debug({
221
- group: 'parser',
222
- task: 'parse',
223
- message: 'Finish tokens parsing',
224
- timing: performance.now() - startParsing,
225
- });
226
-
227
- const tokens = {};
228
-
229
- // 2. Walk AST once to validate tokens
230
- const startValidation = performance.now();
231
- logger.debug({ group: 'parser', task: 'validate', message: 'Start tokens validation' });
232
- const $typeInheritance = {};
233
- traverse(document, {
234
- enter(node, parent, path) {
235
- if (node.type === 'Member' && node.value.type === 'Object' && node.value.members) {
236
- const members = getObjMembers(node.value);
237
-
238
- // keep track of $types
239
- if (members.$type && members.$type.type === 'String' && !members.$value) {
240
- $typeInheritance[path.join('.') || '.'] = node.value.members.find((m) => m.name.value === '$type');
241
- }
242
-
243
- const id = path.join('.');
244
-
245
- if (members.$value) {
246
- const extensions = members.$extensions ? getObjMembers(members.$extensions) : undefined;
247
- const sourceNode = structuredClone(node);
248
-
249
- // get parent type by taking the closest-scoped $type (length === closer)
250
- let parent$type;
251
- let longestPath = '';
252
- for (const [k, v] of Object.entries($typeInheritance)) {
253
- if (k === '.' || id.startsWith(k)) {
254
- if (k.length > longestPath.length) {
255
- parent$type = v;
256
- longestPath = k;
257
- }
258
- }
259
- }
260
- if (parent$type && !members.$type) {
261
- sourceNode.value = injectObjMembers(sourceNode.value, [parent$type]);
262
- }
263
-
264
- validate(sourceNode, { filename, src, logger });
265
-
266
- const group = { id: splitID(id).group, tokens: [] };
267
- if (parent$type) {
268
- group.$type = parent$type.value.value;
269
- }
270
- // note: this will also include sibling tokens, so be selective about only accessing group-specific properties
271
- const groupMembers = getObjMembers(parent);
272
- if (groupMembers.$description) {
273
- group.$description = evaluate(groupMembers.$description);
274
- }
275
- if (groupMembers.$extensions) {
276
- group.$extensions = evaluate(groupMembers.$extensions);
277
- }
278
- const token = {
279
- $type: members.$type?.value ?? parent$type?.value.value,
280
- $value: evaluate(members.$value),
281
- id,
282
- mode: {},
283
- originalValue: evaluate(node.value),
284
- group,
285
- source: {
286
- loc: filename ? filename.href : undefined,
287
- node: sourceNode.value,
288
- },
289
- };
290
- if (members.$description?.value) {
291
- token.$description = members.$description.value;
292
- }
293
-
294
- // handle modes
295
- // note that circular refs are avoided here, such as not duplicating `modes`
296
- const modeValues = extensions?.mode ? getObjMembers(extensions.mode) : {};
297
- for (const mode of ['.', ...Object.keys(modeValues)]) {
298
- token.mode[mode] = {
299
- id: token.id,
300
- $type: token.$type,
301
- $value: mode === '.' ? token.$value : evaluate(modeValues[mode]),
302
- source: {
303
- loc: filename ? filename.href : undefined,
304
- node: mode === '.' ? structuredClone(token.source.node) : modeValues[mode],
305
- },
306
- };
307
- if (token.$description) {
308
- token.mode[mode].$description = token.$description;
309
- }
310
- }
311
-
312
- tokens[id] = token;
313
- } else if (!id.includes('.$value') && members.value) {
314
- logger.warn({ message: `Group ${id} has "value". Did you mean "$value"?`, filename, node, src });
315
- }
316
- }
317
-
318
- // edge case: if $type appears at root of tokens.json, collect it
319
- if (node.type === 'Document' && node.body.type === 'Object' && node.body.members) {
320
- const members = getObjMembers(node.body);
321
- if (members.$type && members.$type.type === 'String' && !members.$value) {
322
- $typeInheritance['.'] = node.body.members.find((m) => m.name.value === '$type');
323
- }
324
- }
325
- },
326
- });
327
- logger.debug({
328
- group: 'parser',
329
- task: 'validate',
330
- message: 'Finish tokens validation',
331
- timing: performance.now() - startValidation,
332
- });
333
-
334
- // 3. Execute lint runner with loaded plugins
335
- if (!skipLint && config?.plugins?.length) {
336
- const lintStart = performance.now();
337
- logger.debug({
338
- group: 'parser',
339
- task: 'validate',
340
- message: 'Start token linting',
341
- });
342
- await lintRunner({ document, filename, src, config, logger });
343
- logger.debug({
344
- group: 'parser',
345
- task: 'validate',
346
- message: 'Finish token linting',
347
- timing: performance.now() - lintStart,
348
- });
349
- }
350
-
351
- // 4. normalize values
352
- const normalizeStart = performance.now();
353
- logger.debug({
354
- group: 'parser',
355
- task: 'normalize',
356
- message: 'Start token normalization',
357
- });
358
- for (const id in tokens) {
359
- if (!Object.hasOwn(tokens, id)) {
360
- continue;
361
- }
362
- try {
363
- tokens[id].$value = normalize(tokens[id]);
364
- } catch (err) {
365
- let { node } = tokens[id].source;
366
- const members = getObjMembers(node);
367
- if (members.$value) {
368
- node = members.$value;
369
- }
370
- logger.error({ message: err.message, filename, src, node, continueOnError });
371
- }
372
- for (const mode in tokens[id].mode) {
373
- if (mode === '.') {
374
- continue;
375
- }
376
- try {
377
- tokens[id].mode[mode].$value = normalize(tokens[id].mode[mode]);
378
- } catch (err) {
379
- let { node } = tokens[id].source;
380
- const members = getObjMembers(node);
381
- if (members.$value) {
382
- node = members.$value;
383
- }
384
- logger.error({
385
- message: err.message,
386
- filename,
387
- src,
388
- node: tokens[id].mode[mode].source.node,
389
- continueOnError,
390
- });
391
- }
392
- }
393
- }
394
- logger.debug({
395
- group: 'parser',
396
- task: 'normalize',
397
- message: 'Finish token normalization',
398
- timing: performance.now() - normalizeStart,
399
- });
400
-
401
- return { tokens, document, src };
402
- }
403
-
404
- /**
405
- * Determine if an input is likely a JSON string
406
- * @param {string} input
407
- * @return {boolean}
408
- */
409
- export function maybeJSONString(input) {
410
- return typeof input === 'string' && input.trim().startsWith('{');
411
- }
412
-
413
- /**
414
- * Resolve alias
415
- * @param {string} alias
416
- * @param {Object} options
417
- * @param {Record<string, TokenNormalized>} options.tokens
418
- * @param {Logger} options.logger
419
- * @param {string} [options.filename]
420
- * @param {AnyNode} [options.node]
421
- * @param {string} [options.string]
422
- * @param {string} [options.scanned=[]]
423
- * @param {string}
424
- */
425
- export function resolveAlias(alias, { tokens, logger, filename, src, node, scanned = [] }) {
426
- const { id } = parseAlias(alias);
427
- if (!tokens[id]) {
428
- logger.error({ message: `Alias "${alias}" not found.`, filename, src, node });
429
- }
430
- if (scanned.includes(id)) {
431
- logger.error({ message: `Circular alias detected from "${alias}".`, filename, src, node });
432
- }
433
- const token = tokens[id];
434
- if (!isAlias(token.$value)) {
435
- return id;
436
- }
437
- return resolveAlias(token.$value, { tokens, logger, filename, node, src, scanned: [...scanned, id] });
438
- }
439
-
440
- /** Throw error if resolved alias for composite properties doesn’t match expected $type. */
441
- const COMPOSITE_TYPE_VALUES = {
442
- border: {
443
- color: ['color'],
444
- width: ['dimension'],
445
- strokeStyle: ['strokeStyle'],
446
- },
447
- gradient: {
448
- color: ['color'],
449
- position: ['number'],
450
- },
451
- shadow: {
452
- color: ['color'],
453
- position: ['dimension'],
454
- },
455
- strokeStyle: {
456
- dashArray: ['dimension'],
457
- },
458
- transition: {
459
- duration: ['duration'],
460
- delay: ['duration'],
461
- timingFunction: ['cubicBezier'],
462
- },
463
- typography: {
464
- fontFamily: ['fontFamily'],
465
- fontSize: ['dimension'],
466
- fontWeight: ['fontWeight'],
467
- letterSpacing: ['dimension'],
468
- lineHeight: ['dimension', 'number'],
469
- },
470
- };
471
-
472
- /** Resolve aliases, update values, and mutate `token` to add `aliasOf` / `partialAliasOf` */
473
- function applyAliases(token, { tokens, logger, filename, src, node }) {
474
- const $valueNode = (node.type === 'Object' && node.members.find((m) => m.name.value === '$value')?.value) || node;
475
- const expectedAliasTypes = COMPOSITE_TYPE_VALUES[token.$type];
476
-
477
- // handle simple aliases
478
- if (isAlias(token.$value)) {
479
- const aliasOfID = resolveAlias(token.$value, { tokens, logger, filename, node, src });
480
- const { mode: aliasMode } = parseAlias(token.$value);
481
- const aliasOf = tokens[aliasOfID];
482
- token.aliasOf = aliasOfID;
483
- token.$value = aliasOf.mode[aliasMode]?.$value || aliasOf.$value;
484
- if (token.$type && token.$type !== aliasOf.$type) {
485
- logger.error({
486
- message: `Invalid alias: expected $type: "${token.$type}", received $type: "${aliasOf.$type}".`,
487
- node: $valueNode,
488
- filename,
489
- src,
490
- });
491
- }
492
- token.$type = aliasOf.$type;
493
- }
494
-
495
- // handle aliases within array values (e.g. cubicBezier, gradient)
496
- else if (Array.isArray(token.$value)) {
497
- // some arrays are primitives, some are objects. handle both
498
- for (let i = 0; i < token.$value.length; i++) {
499
- if (isAlias(token.$value[i])) {
500
- if (!token.partialAliasOf) {
501
- token.partialAliasOf = [];
502
- }
503
- const aliasOfID = resolveAlias(token.$value[i], { tokens, logger, filename, node, src });
504
- const { id: aliasID, mode: aliasMode } = parseAlias(token.$value[i]);
505
- token.partialAliasOf[i] = aliasID;
506
- token.$value[i] = tokens[aliasOfID].mode[aliasMode]?.$value || tokens[aliasOfID].$value;
507
- } else if (typeof token.$value[i] === 'object') {
508
- for (const property in token.$value[i]) {
509
- if (isAlias(token.$value[i][property])) {
510
- if (!token.partialAliasOf) {
511
- token.partialAliasOf = [];
512
- }
513
- if (!token.partialAliasOf[i]) {
514
- token.partialAliasOf[i] = {};
515
- }
516
- const aliasOfID = resolveAlias(token.$value[i][property], { tokens, logger, filename, node, src });
517
- const { id: aliasID, mode: aliasMode } = parseAlias(token.$value[i][property]);
518
- const aliasToken = tokens[aliasOfID];
519
-
520
- if (expectedAliasTypes?.[property] && !expectedAliasTypes[property].includes(aliasToken.$type)) {
521
- const elementNode = $valueNode.elements[i].value;
522
- logger.error({
523
- message: `Invalid alias: expected $type: "${expectedAliasTypes[property].join('" or "')}", received $type: "${aliasToken.$type}".`,
524
- node: elementNode.members.find((m) => m.name.value === property).value,
525
- filename,
526
- src,
527
- });
528
- }
529
-
530
- token.partialAliasOf[i][property] = aliasID; // also keep the shallow alias here, too!
531
- token.$value[i][property] = aliasToken.mode[aliasMode]?.$value || aliasToken.$value;
532
- }
533
- }
534
- }
535
- }
536
- }
537
- // handle aliases within object (composite) values (e.g. border, typography, transition)
538
- else if (typeof token.$value === 'object') {
539
- for (const property in token.$value) {
540
- if (!Object.hasOwn(token.$value, property)) {
541
- continue;
542
- }
543
-
544
- if (isAlias(token.$value[property])) {
545
- if (!token.partialAliasOf) {
546
- token.partialAliasOf = {};
547
- }
548
- const aliasOfID = resolveAlias(token.$value[property], { tokens, logger, filename, node, src });
549
- const { id: aliasID, mode: aliasMode } = parseAlias(token.$value[property]);
550
- token.partialAliasOf[property] = aliasID; // keep the shallow alias!
551
- const aliasToken = tokens[aliasOfID];
552
- if (expectedAliasTypes?.[property] && !expectedAliasTypes[property].includes(aliasToken.$type)) {
553
- logger.error({
554
- message: `Invalid alias: expected $type: "${expectedAliasTypes[property].join('" or "')}", received $type: "${aliasToken.$type}".`,
555
- node: $valueNode.members.find((m) => m.name.value === property).value,
556
- filename,
557
- src,
558
- });
559
- }
560
- token.$value[property] = aliasToken.mode[aliasMode]?.$value || aliasToken.$value;
561
- }
562
-
563
- // strokeStyle has an array within an object
564
- else if (Array.isArray(token.$value[property])) {
565
- for (let i = 0; i < token.$value[property].length; i++) {
566
- if (isAlias(token.$value[property][i])) {
567
- const aliasOfID = resolveAlias(token.$value[property][i], { tokens, logger, filename, node, src });
568
- if (!token.partialAliasOf) {
569
- token.partialAliasOf = {};
570
- }
571
- if (!token.partialAliasOf[property]) {
572
- token.partialAliasOf[property] = [];
573
- }
574
- const { id: aliasID, mode: aliasMode } = parseAlias(token.$value[property][i]);
575
- token.partialAliasOf[property][i] = aliasID; // keep the shallow alias!
576
- const aliasToken = tokens[aliasOfID];
577
- if (expectedAliasTypes?.[property] && !expectedAliasTypes[property].includes(aliasToken.$type)) {
578
- const arrayNode = $valueNode.members.find((m) => m.name.value === property).value;
579
- logger.error({
580
- message: `Invalid alias: expected $type: "${expectedAliasTypes[property].join('" or "')}", received $type: "${aliasToken.$type}".`,
581
- node: arrayNode.elements[i],
582
- filename,
583
- src,
584
- });
585
- }
586
- token.$value[property][i] = tokens[aliasOfID].mode[aliasMode]?.$value || tokens[aliasOfID].$value;
587
- }
588
- }
589
- }
590
- }
591
- }
592
- }
package/parse/json.d.ts DELETED
@@ -1,30 +0,0 @@
1
- import type { AnyNode, MemberNode, ObjectNode, ValueNode } from '@humanwhocodes/momoa';
2
-
3
- export interface Visitor {
4
- enter?: (node: AnyNode, parent: AnyNode | undefined, path: string[]) => void;
5
- exit?: (node: AnyNode, parent: AnyNode | undefined, path: string[]) => void;
6
- }
7
-
8
- declare const CHILD_KEYS: {
9
- Document: readonly ['body'];
10
- Object: readonly ['members'];
11
- Member: readonly ['name', 'value'];
12
- Element: readonly ['value'];
13
- Array: readonly ['elements'];
14
- String: readonly [];
15
- Number: readonly [];
16
- Boolean: readonly [];
17
- Null: readonly [];
18
- };
19
-
20
- /** Determines if a given value is an AST node. */
21
- export function isNode(value: unknown): boolean;
22
-
23
- /** Get ObjectNode members as object */
24
- export function getObjMembers(node: ObjectNode): Record<string | number, ValueNode | undefined>;
25
-
26
- /** Inject members to ObjectNode and return a clone */
27
- export function injectObjMembers(node: ObjectNode, members: MemberNode[]): ObjectNode;
28
-
29
- /** Variation of Momoa’s traverse(), which keeps track of global path */
30
- export function traverse(root: AnyNode, visitor: Visitor): void;