@terrazzo/parser 0.1.3 → 0.2.1

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 +23 -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 -24
  99. package/{parse/validate.js → src/parse/validate.ts} +154 -236
  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
@@ -0,0 +1,53 @@
1
+ import { isTokenMatch } from '@terrazzo/token-tools';
2
+ import type { LintRule } from '../../../types.js';
3
+ import { docsLink } from '../lib/docs.js';
4
+
5
+ export const REQUIRED_TYPOGRAPHY_PROPERTIES = 'core/required-typography-properties';
6
+
7
+ export interface RuleRequiredTypographyPropertiesOptions {
8
+ /** Required typography properties */
9
+ properties: string[];
10
+ /** Token globs to ignore */
11
+ ignore?: string[];
12
+ }
13
+
14
+ const rule: LintRule<never, RuleRequiredTypographyPropertiesOptions> = {
15
+ meta: {
16
+ docs: {
17
+ description: 'Enforce typography tokens have required properties.',
18
+ url: docsLink(REQUIRED_TYPOGRAPHY_PROPERTIES),
19
+ },
20
+ },
21
+ defaultOptions: { properties: [] },
22
+ create({ tokens, options, report }) {
23
+ if (!options) {
24
+ return;
25
+ }
26
+
27
+ if (!options.properties.length) {
28
+ throw new Error(`"properties" can’t be empty`);
29
+ }
30
+
31
+ for (const t of Object.values(tokens)) {
32
+ if (options.ignore && isTokenMatch(t.id, options.ignore)) {
33
+ continue;
34
+ }
35
+
36
+ if (t.$type !== 'typography') {
37
+ continue;
38
+ }
39
+
40
+ if (t.aliasOf) {
41
+ continue;
42
+ }
43
+
44
+ for (const p of options.properties) {
45
+ if (!t.partialAliasOf?.[p] && !(p in t.$value)) {
46
+ report({ message: `${t.id} missing required typographic property "${p}"`, node: t.source.node });
47
+ }
48
+ }
49
+ }
50
+ },
51
+ };
52
+
53
+ export default rule;
@@ -1,12 +1,51 @@
1
+ import type { AnyNode } from '@humanwhocodes/momoa';
1
2
  import color from 'picocolors';
2
3
  import wcmatch from 'wildcard-match';
3
4
  import { codeFrameColumns } from './lib/code-frame.js';
4
5
 
5
- export const LOG_ORDER = ['error', 'warn', 'info', 'debug'];
6
+ export const LOG_ORDER = ['error', 'warn', 'info', 'debug'] as const;
7
+
8
+ export type LogSeverity = 'error' | 'warn' | 'info' | 'debug';
9
+
10
+ export type LogLevel = LogSeverity | 'silent';
11
+
12
+ export type LogGroup = 'parser' | 'plugin';
13
+
14
+ export interface LogEntry {
15
+ /** Error message to be logged */
16
+ message: string;
17
+ /** Prefix message with label */
18
+ label?: string;
19
+ /** File in disk */
20
+ filename?: URL;
21
+ /**
22
+ * Continue on error?
23
+ * @default false
24
+ */
25
+ continueOnError?: boolean;
26
+ /** Show a code frame for the erring node */
27
+ node?: AnyNode;
28
+ /** Originator of log message */
29
+ group?: LogGroup;
30
+ /** To show a code frame, provide the original source code */
31
+ src?: string;
32
+ }
33
+
34
+ export interface DebugEntry {
35
+ group: LogGroup;
36
+ /** Error message to be logged */
37
+ message: string;
38
+ /** Current subtask or submodule */
39
+ label?: string;
40
+ /** Show code below message */
41
+ codeFrame?: { src: string; line: number; column: number };
42
+ /** Display performance timing */
43
+ timing?: number;
44
+ }
6
45
 
7
- const DEBUG_GROUP_COLOR = { core: color.green, plugin: color.magenta };
46
+ const DEBUG_GROUP_COLOR: Record<string, typeof color.red | undefined> = { core: color.green, plugin: color.magenta };
8
47
 
9
- const MESSAGE_COLOR = { error: color.red, warn: color.yellow };
48
+ const MESSAGE_COLOR: Record<string, typeof color.red | undefined> = { error: color.red, warn: color.yellow };
10
49
 
11
50
  const timeFormatter = new Intl.DateTimeFormat('en-gb', { timeStyle: 'medium' });
12
51
 
@@ -15,16 +54,16 @@ const timeFormatter = new Intl.DateTimeFormat('en-gb', { timeStyle: 'medium' });
15
54
  * @param {Severity} severity
16
55
  * @return {string}
17
56
  */
18
- export function formatMessage(entry, severity) {
57
+ export function formatMessage(entry: LogEntry, severity: LogSeverity) {
19
58
  let message = entry.message;
20
59
  if (entry.label) {
21
60
  message = `${color.bold(`${entry.label}:`)} ${message}`;
22
61
  }
23
62
  if (severity in MESSAGE_COLOR) {
24
- message = MESSAGE_COLOR[severity](message);
63
+ message = MESSAGE_COLOR[severity]!(message);
25
64
  }
26
65
  if (entry.src) {
27
- const start = entry.node?.loc?.start;
66
+ const start = entry.node?.loc?.start ?? { line: 0, column: 0 };
28
67
  // strip "file://" protocol, but not href
29
68
  const loc = entry.filename
30
69
  ? `${entry.filename?.href.replace(/^file:\/\//, '')}:${start?.line ?? 0}:${start?.column ?? 0}\n\n`
@@ -36,14 +75,14 @@ export function formatMessage(entry, severity) {
36
75
  }
37
76
 
38
77
  export default class Logger {
39
- level = 'info';
78
+ level = 'info' as LogLevel;
40
79
  debugScope = '*';
41
80
  errorCount = 0;
42
81
  warnCount = 0;
43
82
  infoCount = 0;
44
83
  debugCount = 0;
45
84
 
46
- constructor(options) {
85
+ constructor(options?: { level?: LogLevel; debugScope?: string }) {
47
86
  if (options?.level) {
48
87
  this.level = options.level;
49
88
  }
@@ -52,12 +91,12 @@ export default class Logger {
52
91
  }
53
92
  }
54
93
 
55
- setLevel(level) {
94
+ setLevel(level: LogLevel) {
56
95
  this.level = level;
57
96
  }
58
97
 
59
98
  /** Log an error message (always; can’t be silenced) */
60
- error(entry) {
99
+ error(entry: LogEntry) {
61
100
  this.errorCount++;
62
101
  const message = formatMessage(entry, 'error');
63
102
  if (entry.continueOnError) {
@@ -72,7 +111,7 @@ export default class Logger {
72
111
  }
73
112
 
74
113
  /** Log an info message (if logging level permits) */
75
- info(entry) {
114
+ info(entry: LogEntry) {
76
115
  this.infoCount++;
77
116
  if (this.level === 'silent' || LOG_ORDER.indexOf(this.level) < LOG_ORDER.indexOf('info')) {
78
117
  return;
@@ -82,7 +121,7 @@ export default class Logger {
82
121
  }
83
122
 
84
123
  /** Log a warning message (if logging level permits) */
85
- warn(entry) {
124
+ warn(entry: LogEntry) {
86
125
  this.warnCount++;
87
126
  if (this.level === 'silent' || LOG_ORDER.indexOf(this.level) < LOG_ORDER.indexOf('warn')) {
88
127
  return;
@@ -91,7 +130,7 @@ export default class Logger {
91
130
  }
92
131
 
93
132
  /** Log a diagnostics message (if logging level permits) */
94
- debug(entry) {
133
+ debug(entry: DebugEntry) {
95
134
  this.debugCount++;
96
135
  if (this.level === 'silent' || LOG_ORDER.indexOf(this.level) < LOG_ORDER.indexOf('debug')) {
97
136
  return;
@@ -99,7 +138,7 @@ export default class Logger {
99
138
 
100
139
  let message = formatMessage(entry, 'debug');
101
140
 
102
- const debugPrefix = `${entry.group}:${entry.task}`;
141
+ const debugPrefix = entry.label ? `${entry.group}:${entry.label}` : entry.group;
103
142
  if (this.debugScope !== '*' && !wcmatch(this.debugScope)(debugPrefix)) {
104
143
  return;
105
144
  }
@@ -107,7 +146,7 @@ export default class Logger {
107
146
  timeFormatter.format(new Date()),
108
147
  )} ${message}`;
109
148
  if (typeof entry.timing === 'number') {
110
- let timing = Math.round(entry.timing);
149
+ let timing: string | number = Math.round(entry.timing);
111
150
  if (timing < 1_000) {
112
151
  timing = `${timing}ms`;
113
152
  } else if (timing < 60_000) {
@@ -132,7 +171,7 @@ export default class Logger {
132
171
  }
133
172
 
134
173
  export class TokensJSONError extends Error {
135
- constructor(message) {
174
+ constructor(message: string) {
136
175
  super(message);
137
176
  this.name = 'TokensJSONError';
138
177
  }
@@ -0,0 +1,224 @@
1
+ import type { AnyNode, ArrayNode, ObjectNode, StringNode } from '@humanwhocodes/momoa';
2
+ import { type TokenNormalized, isAlias, parseAlias } from '@terrazzo/token-tools';
3
+ import type Logger from '../logger.js';
4
+
5
+ /** Throw error if resolved alias for composite properties doesn’t match expected $type. */
6
+ export const COMPOSITE_TYPE_VALUES = {
7
+ border: {
8
+ color: ['color'],
9
+ width: ['dimension'],
10
+ strokeStyle: ['strokeStyle'],
11
+ },
12
+ gradient: {
13
+ color: ['color'],
14
+ position: ['number'],
15
+ },
16
+ shadow: {
17
+ color: ['color'],
18
+ position: ['dimension'],
19
+ },
20
+ strokeStyle: {
21
+ dashArray: ['dimension'],
22
+ },
23
+ transition: {
24
+ duration: ['duration'],
25
+ delay: ['duration'],
26
+ timingFunction: ['cubicBezier'],
27
+ },
28
+ typography: {
29
+ fontFamily: ['fontFamily'],
30
+ fontSize: ['dimension'],
31
+ fontWeight: ['fontWeight'],
32
+ letterSpacing: ['dimension'],
33
+ lineHeight: ['dimension', 'number'],
34
+ },
35
+ };
36
+
37
+ /** Resolve alias */
38
+ export function resolveAlias(
39
+ alias: string,
40
+ {
41
+ tokens,
42
+ logger,
43
+ filename,
44
+ src,
45
+ node,
46
+ scanned = [],
47
+ }: {
48
+ tokens: Record<string, TokenNormalized>;
49
+ logger: Logger;
50
+ filename?: URL;
51
+ src: string;
52
+ node: AnyNode;
53
+ scanned?: string[];
54
+ },
55
+ ) {
56
+ const { id } = parseAlias(alias);
57
+ if (!tokens[id]) {
58
+ logger.error({ message: `Alias "${alias}" not found.`, filename, src, node });
59
+ }
60
+ if (scanned.includes(id)) {
61
+ logger.error({ message: `Circular alias detected from "${alias}".`, filename, src, node });
62
+ }
63
+ const token = tokens[id]!;
64
+ if (!isAlias(token.$value)) {
65
+ return id;
66
+ }
67
+ return resolveAlias(token.$value as string, { tokens, logger, filename, node, src, scanned: [...scanned, id] });
68
+ }
69
+
70
+ /** Resolve aliases, update values, and mutate `token` to add `aliasOf` / `partialAliasOf` */
71
+ export function applyAliases(
72
+ token: TokenNormalized,
73
+ {
74
+ tokens,
75
+ logger,
76
+ filename,
77
+ src,
78
+ node,
79
+ }: { tokens: Record<string, TokenNormalized>; logger: Logger; filename?: URL; src: string; node: AnyNode },
80
+ ) {
81
+ const $valueNode =
82
+ (node.type === 'Object' && node.members.find((m) => (m.name as StringNode).value === '$value')?.value) || node;
83
+ const expectedAliasTypes =
84
+ token.$type in COMPOSITE_TYPE_VALUES && COMPOSITE_TYPE_VALUES[token.$type as keyof typeof COMPOSITE_TYPE_VALUES];
85
+
86
+ // handle simple aliases
87
+ if (isAlias(token.$value)) {
88
+ const aliasOfID = resolveAlias(token.$value as string, { tokens, logger, filename, node, src });
89
+ const { mode: aliasMode } = parseAlias(token.$value as string);
90
+ const aliasOf = tokens[aliasOfID]!;
91
+ token.aliasOf = aliasOfID;
92
+ token.$value = aliasOf!.mode[aliasMode!]?.$value || aliasOf.$value;
93
+ if (token.$type && token.$type !== aliasOf.$type) {
94
+ logger.error({
95
+ message: `Invalid alias: expected $type: "${token.$type}", received $type: "${aliasOf.$type}".`,
96
+ node: $valueNode,
97
+ filename,
98
+ src,
99
+ });
100
+ }
101
+ token.$type = aliasOf.$type;
102
+ }
103
+
104
+ // handle aliases within array values (e.g. cubicBezier, gradient)
105
+ else if (token.$type === 'cubicBezier' || token.$type === 'gradient' || token.$type === 'shadow') {
106
+ // some arrays are primitives, some are objects. handle both
107
+ for (let i = 0; i < token.$value.length; i++) {
108
+ // @ts-ignore
109
+ if (isAlias(token.$value[i])) {
110
+ if (!token.partialAliasOf) {
111
+ token.partialAliasOf = [];
112
+ }
113
+ // @ts-ignore
114
+ const aliasOfID = resolveAlias(token.$value[i], { tokens, logger, filename, node, src });
115
+ // @ts-ignore
116
+ const { id: aliasID, mode: aliasMode } = parseAlias(token.$value[i]);
117
+ token.partialAliasOf![i] = aliasID;
118
+ // @ts-ignore
119
+ token.$value[i] = (aliasMode && tokens[aliasOfID]!.mode[aliasMode]?.$value) || tokens[aliasOfID]!.$value;
120
+ } else if (typeof token.$value[i] === 'object') {
121
+ for (const [property, subvalue] of Object.entries(token.$value[i]!)) {
122
+ if (isAlias(subvalue)) {
123
+ if (!token.partialAliasOf) {
124
+ token.partialAliasOf = [];
125
+ }
126
+ if (!token.partialAliasOf[i]) {
127
+ token.partialAliasOf[i] = {};
128
+ }
129
+ const aliasOfID = resolveAlias(subvalue, { tokens, logger, filename, node, src });
130
+ const { id: aliasID, mode: aliasMode } = parseAlias(subvalue);
131
+ const aliasToken = tokens[aliasOfID]!;
132
+ const possibleTypes: string[] = expectedAliasTypes?.[property as keyof typeof expectedAliasTypes] || [];
133
+ if (possibleTypes.length && !possibleTypes.includes(aliasToken.$type)) {
134
+ const elementNode = ($valueNode as ArrayNode).elements[i]!.value;
135
+ logger.error({
136
+ message: `Invalid alias: expected $type: "${possibleTypes.join('" or "')}", received $type: "${aliasToken.$type}".`,
137
+ node: (elementNode as ObjectNode).members.find((m) => (m.name as StringNode).value === property)!.value,
138
+ filename,
139
+ src,
140
+ });
141
+ }
142
+
143
+ // @ts-ignore
144
+ token.partialAliasOf[i]![property] = aliasID; // also keep the shallow alias here, too!
145
+ // @ts-ignore
146
+ token.$value[i]![property] = (aliasMode && aliasToken.mode[aliasMode]?.$value) || aliasToken.$value;
147
+ }
148
+ }
149
+ }
150
+ }
151
+ }
152
+ // handle aliases within object (composite) values (e.g. border, typography, transition)
153
+ else if (typeof token.$value === 'object') {
154
+ for (const [property, subvalue] of Object.entries(token.$value)) {
155
+ if (!Object.hasOwn(token.$value, property)) {
156
+ continue;
157
+ }
158
+
159
+ if (isAlias(subvalue)) {
160
+ if (!token.partialAliasOf) {
161
+ token.partialAliasOf = {};
162
+ }
163
+ const aliasOfID = resolveAlias(subvalue, { tokens, logger, filename, node, src });
164
+ const { id: aliasID, mode: aliasMode } = parseAlias(subvalue);
165
+ // @ts-ignore
166
+ token.partialAliasOf[property] = aliasID; // keep the shallow alias!
167
+ const aliasToken = tokens[aliasOfID];
168
+ // @ts-ignore
169
+ if (expectedAliasTypes?.[property] && !expectedAliasTypes[property].includes(aliasToken!.$type)) {
170
+ logger.error({
171
+ // @ts-ignore
172
+ message: `Invalid alias: expected $type: "${expectedAliasTypes[property].join('" or "')}", received $type: "${aliasToken.$type}".`,
173
+ // @ts-ignore
174
+ node: $valueNode.members.find((m) => m.name.value === property).value,
175
+ filename,
176
+ src,
177
+ });
178
+ }
179
+ // @ts-ignore
180
+ token.$value[property] = aliasToken!.mode[aliasMode]?.$value || aliasToken!.$value;
181
+ }
182
+
183
+ // strokeStyle has an array within an object
184
+ // @ts-ignore
185
+ else if (Array.isArray(token.$value[property]!)) {
186
+ // @ts-ignore
187
+ for (let i = 0; i < token.$value[property]!.length; i++) {
188
+ // @ts-ignore
189
+ if (isAlias(token.$value[property]![i])) {
190
+ // @ts-ignore
191
+ const aliasOfID = resolveAlias(token.$value[property][i], { tokens, logger, filename, node, src });
192
+ if (!token.partialAliasOf) {
193
+ token.partialAliasOf = {};
194
+ }
195
+ // @ts-ignore
196
+ if (!token.partialAliasOf[property]) {
197
+ // @ts-ignore
198
+ token.partialAliasOf[property] = [];
199
+ }
200
+ // @ts-ignore
201
+ const { id: aliasID, mode: aliasMode } = parseAlias(token.$value[property][i]);
202
+ // @ts-ignore
203
+ token.partialAliasOf[property][i] = aliasID; // keep the shallow alias!
204
+ const aliasToken = tokens[aliasOfID];
205
+ // @ts-ignore
206
+ if (expectedAliasTypes?.[property] && !expectedAliasTypes[property].includes(aliasToken.$type)) {
207
+ // @ts-ignore
208
+ const arrayNode = $valueNode.members.find((m) => m.name.value === property).value;
209
+ logger.error({
210
+ // @ts-ignore
211
+ message: `Invalid alias: expected $type: "${expectedAliasTypes[property].join('" or "')}", received $type: "${aliasToken.$type}".`,
212
+ node: arrayNode.elements[i],
213
+ filename,
214
+ src,
215
+ });
216
+ }
217
+ // @ts-ignore
218
+ token.$value[property]![i] = tokens[aliasOfID]!.mode[aliasMode]?.$value || tokens[aliasOfID]!.$value;
219
+ }
220
+ }
221
+ }
222
+ }
223
+ }
224
+ }