@terrazzo/parser 0.10.4 → 2.0.0-alpha.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 (43) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/dist/index.d.ts +112 -325
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +2194 -3621
  5. package/dist/index.js.map +1 -1
  6. package/package.json +4 -3
  7. package/src/build/index.ts +42 -42
  8. package/src/config.ts +13 -6
  9. package/src/lib/code-frame.ts +3 -0
  10. package/src/lib/momoa.ts +10 -0
  11. package/src/lint/index.ts +41 -37
  12. package/src/lint/plugin-core/index.ts +73 -16
  13. package/src/lint/plugin-core/rules/colorspace.ts +4 -0
  14. package/src/lint/plugin-core/rules/duplicate-values.ts +2 -0
  15. package/src/lint/plugin-core/rules/max-gamut.ts +24 -4
  16. package/src/lint/plugin-core/rules/no-type-on-alias.ts +29 -0
  17. package/src/lint/plugin-core/rules/required-modes.ts +2 -0
  18. package/src/lint/plugin-core/rules/required-typography-properties.ts +13 -3
  19. package/src/lint/plugin-core/rules/valid-boolean.ts +41 -0
  20. package/src/lint/plugin-core/rules/valid-border.ts +57 -0
  21. package/src/lint/plugin-core/rules/valid-color.ts +265 -0
  22. package/src/lint/plugin-core/rules/valid-cubic-bezier.ts +83 -0
  23. package/src/lint/plugin-core/rules/valid-dimension.ts +199 -0
  24. package/src/lint/plugin-core/rules/valid-duration.ts +123 -0
  25. package/src/lint/plugin-core/rules/valid-font-family.ts +68 -0
  26. package/src/lint/plugin-core/rules/valid-font-weight.ts +89 -0
  27. package/src/lint/plugin-core/rules/valid-gradient.ts +79 -0
  28. package/src/lint/plugin-core/rules/valid-link.ts +41 -0
  29. package/src/lint/plugin-core/rules/valid-number.ts +63 -0
  30. package/src/lint/plugin-core/rules/valid-shadow.ts +67 -0
  31. package/src/lint/plugin-core/rules/valid-string.ts +41 -0
  32. package/src/lint/plugin-core/rules/valid-stroke-style.ts +104 -0
  33. package/src/lint/plugin-core/rules/valid-transition.ts +61 -0
  34. package/src/lint/plugin-core/rules/valid-typography.ts +67 -0
  35. package/src/logger.ts +70 -59
  36. package/src/parse/index.ts +23 -328
  37. package/src/parse/load.ts +257 -0
  38. package/src/parse/normalize.ts +134 -170
  39. package/src/parse/token.ts +530 -0
  40. package/src/types.ts +106 -28
  41. package/src/parse/alias.ts +0 -369
  42. package/src/parse/json.ts +0 -211
  43. package/src/parse/validate.ts +0 -961
package/src/types.ts CHANGED
@@ -1,8 +1,15 @@
1
- import type { AnyNode, DocumentNode } from '@humanwhocodes/momoa';
1
+ import type * as momoa from '@humanwhocodes/momoa';
2
2
  import type { TokenNormalized } from '@terrazzo/token-tools';
3
+ import type ytm from 'yaml-to-momoa';
3
4
  import type Logger from './logger.js';
4
5
 
6
+ export interface PluginHookContext {
7
+ logger: Logger;
8
+ }
9
+
5
10
  export interface BuildHookOptions {
11
+ /** Plugin hook context (provides access to shared logger) */
12
+ context: PluginHookContext;
6
13
  /** Map of tokens */
7
14
  tokens: Record<string, TokenNormalized>;
8
15
  /** Query transformed values */
@@ -18,10 +25,12 @@ export interface BuildHookOptions {
18
25
  }
19
26
 
20
27
  export interface BuildRunnerResult {
21
- outputFiles: OutputFile[];
28
+ outputFiles: OutputFileExpanded[];
22
29
  }
23
30
 
24
31
  export interface BuildEndHookOptions {
32
+ /** Plugin hook context (provides access to shared logger) */
33
+ context: PluginHookContext;
25
34
  /** Map of tokens */
26
35
  tokens: Record<string, TokenNormalized>;
27
36
  /** Query transformed values */
@@ -68,6 +77,40 @@ export interface Config {
68
77
  };
69
78
  }
70
79
 
80
+ export interface VisitorContext {
81
+ parent?: momoa.AnyNode;
82
+ filename: URL;
83
+ path: string[];
84
+ }
85
+
86
+ export type Visitor<T extends momoa.AnyNode = momoa.ObjectNode | momoa.DocumentNode> = (
87
+ node: T,
88
+ context: VisitorContext,
89
+ // biome-ignore lint/suspicious/noConfusingVoidType: TS requires void
90
+ ) => T | void | null | undefined;
91
+
92
+ export interface TransformVisitors {
93
+ boolean?: Visitor;
94
+ border?: Visitor;
95
+ color?: Visitor;
96
+ cubicBezier?: Visitor;
97
+ dimension?: Visitor;
98
+ duration?: Visitor;
99
+ fontFamily?: Visitor;
100
+ fontWeight?: Visitor;
101
+ gradient?: Visitor;
102
+ group?: Visitor;
103
+ link?: Visitor;
104
+ number?: Visitor;
105
+ root?: Visitor;
106
+ shadow?: Visitor;
107
+ strokeStyle?: Visitor;
108
+ token?: Visitor;
109
+ transition?: Visitor;
110
+ typography?: Visitor;
111
+ [key: string]: Visitor | undefined;
112
+ }
113
+
71
114
  // normalized, finalized config
72
115
  export interface ConfigInit {
73
116
  tokens: URL[];
@@ -92,14 +135,14 @@ export interface ConfigOptions {
92
135
  export interface InputSource {
93
136
  filename?: URL;
94
137
  src: any;
95
- document: DocumentNode;
138
+ document: momoa.DocumentNode;
96
139
  }
97
140
 
98
141
  export interface LintNotice {
99
142
  /** Lint message shown to the user */
100
143
  message: string;
101
144
  /** Erring node (used to point to a specific line) */
102
- node?: AnyNode;
145
+ node?: momoa.AnyNode;
103
146
  }
104
147
 
105
148
  export type LintRuleSeverity = 'error' | 'warn' | 'off';
@@ -113,10 +156,10 @@ export interface LintRuleNormalized<O = any> {
113
156
  }
114
157
 
115
158
  export type LintReportDescriptor<MessageIds extends string> = {
116
- /** To error on a specific token source file, provide an erring node */
117
- node?: AnyNode;
118
- /** To error on a specific token source file, also provide the source */
119
- source?: InputSource;
159
+ /** To error on a specific token source file, provide a Momoa node */
160
+ node?: momoa.AnyNode;
161
+ /** To provide correct line numbers, specify the filename (usually found on `token.source.loc`) */
162
+ filename?: string;
120
163
  /** Provide data for messages */
121
164
  data?: Record<string, unknown>;
122
165
  } & (
@@ -139,7 +182,7 @@ export type LintReportDescriptor<MessageIds extends string> = {
139
182
 
140
183
  export interface LintRule<
141
184
  MessageIds extends string,
142
- LintRuleOptions extends object | undefined = undefined,
185
+ LintRuleOptions extends Record<string, any> = Record<string, never>,
143
186
  LintRuleDocs = unknown,
144
187
  > {
145
188
  meta?: LintRuleMetaData<MessageIds, LintRuleOptions, LintRuleDocs>;
@@ -164,8 +207,11 @@ export interface LintRuleContext<MessageIds extends string, LintRuleOptions exte
164
207
  options: LintRuleOptions;
165
208
  /** The current working directory. */
166
209
  cwd?: URL;
167
- /** Source file the token came from. */
168
- src: string;
210
+ /**
211
+ * All source files present in this run. To find the original source, match a
212
+ * token’s `source.loc` filename to one of the source’s `filename`s.
213
+ */
214
+ sources: InputSource[];
169
215
  /** Source file location. */
170
216
  filename?: URL;
171
217
  /** ID:Token map of all tokens. */
@@ -221,6 +267,30 @@ export interface OutputFileExpanded extends OutputFile {
221
267
  time: number;
222
268
  }
223
269
 
270
+ export interface ParseOptions {
271
+ logger?: Logger;
272
+ config: ConfigInit;
273
+ /**
274
+ * Skip lint step
275
+ * @default false
276
+ */
277
+ skipLint?: boolean;
278
+ /**
279
+ * Continue on error? (Useful for `tz check`)
280
+ * @default false
281
+ */
282
+ continueOnError?: boolean;
283
+ /** Provide yamlToMomoa module to parse YAML (by default, this isn’t shipped to cut down on package weight) */
284
+ yamlToMomoa?: typeof ytm;
285
+ /**
286
+ * Transform API
287
+ * @see https://terrazzo.app/docs/api/js#transform-api
288
+ */
289
+ transform?: TransformVisitors;
290
+ /** (internal cache; do not use) */
291
+ _sources?: Record<string, InputSource>;
292
+ }
293
+
224
294
  export interface Plugin {
225
295
  name: string;
226
296
  /** Read config, and optionally modify */
@@ -237,17 +307,14 @@ export interface Plugin {
237
307
  lint?(): Record<string, LintRule<any, any, any>>;
238
308
  transform?(options: TransformHookOptions): Promise<void>;
239
309
  build?(options: BuildHookOptions): Promise<void>;
240
- buildEnd?(result: BuildRunnerResult): Promise<void>;
310
+ buildEnd?(options: BuildEndHookOptions): Promise<void>;
241
311
  }
242
312
 
243
- /** Transformed token with a single value. Note that this may be any type! */
244
- export interface TokenTransformedSingleValue {
313
+ interface TokenTransformedBase {
245
314
  /** Original Token ID */
246
315
  id: string;
247
316
  /** ID unique to this format. */
248
317
  localID?: string;
249
- type: 'SINGLE_VALUE';
250
- value: string;
251
318
  /**
252
319
  * The mode of this value
253
320
  * @default "."
@@ -255,23 +322,27 @@ export interface TokenTransformedSingleValue {
255
322
  mode: string;
256
323
  /** The original token. */
257
324
  token: TokenNormalized;
325
+ /** Arbitrary metadata set by plugins. */
326
+ meta?: Record<string | number | symbol, unknown> & {
327
+ /**
328
+ * Metadata for the token-listing plugin. Plugins can
329
+ * set this to be the name of a token as it appears in code,
330
+ * and the token-listing plugin will pick it up and use it.
331
+ */
332
+ 'token-listing'?: { name: string | undefined };
333
+ };
334
+ }
335
+
336
+ /** Transformed token with a single value. Note that this may be any type! */
337
+ export interface TokenTransformedSingleValue extends TokenTransformedBase {
338
+ type: 'SINGLE_VALUE';
339
+ value: string;
258
340
  }
259
341
 
260
342
  /** Transformed token with multiple values. Note that this may be any type! */
261
- export interface TokenTransformedMultiValue {
262
- /** Original Token ID */
263
- id: string;
264
- /** ID unique to this format.*/
265
- localID?: string;
343
+ export interface TokenTransformedMultiValue extends TokenTransformedBase {
266
344
  type: 'MULTI_VALUE';
267
345
  value: Record<string, string>;
268
- /**
269
- * The mode of this value
270
- * @default "."
271
- */
272
- mode: string;
273
- /** The original token */
274
- token: TokenNormalized;
275
346
  }
276
347
 
277
348
  export type TokenTransformed = TokenTransformedSingleValue | TokenTransformedMultiValue;
@@ -291,6 +362,8 @@ export interface TransformParams {
291
362
  }
292
363
 
293
364
  export interface TransformHookOptions {
365
+ /** Plugin hook context (provides access to shared logger) */
366
+ context: PluginHookContext;
294
367
  /** Map of tokens */
295
368
  tokens: Record<string, TokenNormalized>;
296
369
  /** Query transformed values */
@@ -303,8 +376,13 @@ export interface TransformHookOptions {
303
376
  localID?: string;
304
377
  value: string | Record<string, string>; // allow looser type for input (`undefined` will just get stripped)
305
378
  mode?: string;
379
+ meta?: TokenTransformedBase['meta'];
306
380
  },
307
381
  ): void;
308
382
  /** Momoa documents */
309
383
  sources: InputSource[];
310
384
  }
385
+
386
+ export interface ReferenceObject {
387
+ $ref: string;
388
+ }
@@ -1,369 +0,0 @@
1
- import type { AnyNode, ArrayNode, ObjectNode } from '@humanwhocodes/momoa';
2
- import {
3
- type BorderTokenNormalized,
4
- type GradientTokenNormalized,
5
- isAlias,
6
- parseAlias,
7
- type ShadowTokenNormalized,
8
- type StrokeStyleTokenNormalized,
9
- type TokenNormalized,
10
- type TokenNormalizedSet,
11
- type TransitionTokenNormalized,
12
- type TypographyTokenNormalized,
13
- } from '@terrazzo/token-tools';
14
- import type Logger from '../logger.js';
15
- import { getObjMembers } from './json.js';
16
-
17
- export interface ApplyAliasOptions {
18
- tokensSet: TokenNormalizedSet;
19
- filename: URL;
20
- src: string;
21
- node: ObjectNode;
22
- logger: Logger;
23
- }
24
-
25
- export type PreAliased<T extends TokenNormalized> = {
26
- $value: T['$value'] | string;
27
- mode: Record<string, T['mode'][string] & { $value: T['$value'] | string }>;
28
- };
29
-
30
- /**
31
- * Resolve aliases and update the token nodes.
32
- *
33
- * Data structures are in an awkward in-between phase, where they have
34
- * placeholders for data but we still need to resolve everything. As such,
35
- * TypeScript will raise errors expecting the final shape.
36
- *
37
- * This is also a bit tricky because different token types alias slightly
38
- * differently. For example, color tokens and other “primitive” tokens behave
39
- * as-expected. But composite tokens like Typography, Gradient, Border, etc. can
40
- * either fully- or partially-alias their values. Then we add modes to the mix,
41
- * and we have to do the work all over again for each mode declared.
42
- *
43
- * All that to say, there are a generous amount of TypeScript overrides here rather
44
- * than try to codify indeterminate shapes.
45
- */
46
- export default function applyAliases(token: TokenNormalized, options: ApplyAliasOptions): void {
47
- // prepopulate default mode (if not set)
48
- token.mode['.'] ??= {} as any;
49
- token.mode['.'].$value = token.$value;
50
- token.mode['.'].originalValue ??= token.originalValue.$value;
51
- token.mode['.'].source ??= token.source;
52
-
53
- // resolve root
54
- if (typeof token.$value === 'string' && isAlias(token.$value)) {
55
- const { aliasChain, resolvedToken } = resolveAlias(token.$value, { ...options, token });
56
- token.aliasOf = resolvedToken.id;
57
- token.aliasChain = aliasChain;
58
- (token as any).$value = resolvedToken.$value;
59
- }
60
-
61
- // resolve modes
62
- for (const mode of Object.keys(token.mode)) {
63
- const modeValue = token.mode[mode]!.$value;
64
-
65
- // if the entire mode value is a simple alias, resolve & continue
66
- if (typeof modeValue === 'string' && isAlias(modeValue)) {
67
- const expectedType = [token.$type];
68
- const { aliasChain, resolvedToken } = resolveAlias(modeValue, {
69
- ...options,
70
- token,
71
- expectedType,
72
- node: token.mode[mode]!.source?.node || options.node,
73
- });
74
- token.mode[mode]!.aliasOf = resolvedToken.id;
75
- token.mode[mode]!.aliasChain = aliasChain;
76
- (token.mode[mode] as any).$value = resolvedToken.$value;
77
- continue;
78
- }
79
-
80
- // object types: expand default $value into current mode
81
- if (
82
- typeof token.$value === 'object' &&
83
- typeof token.mode[mode]!.$value === 'object' &&
84
- !Array.isArray(token.$value)
85
- ) {
86
- for (const [k, v] of Object.entries(token.$value)) {
87
- if (!(k in token.mode[mode]!.$value)) {
88
- (token.mode[mode]!.$value as any)[k] = v;
89
- }
90
- }
91
- }
92
-
93
- // if the mode is an object or array that’s partially aliased, do work per-token type
94
- const node = (getObjMembers(options.node).$value as any) || options.node;
95
- switch (token.$type) {
96
- case 'border': {
97
- applyBorderPartialAlias(token, mode, { ...options, node });
98
- break;
99
- }
100
- case 'gradient': {
101
- applyGradientPartialAlias(token, mode, { ...options, node });
102
- break;
103
- }
104
- case 'shadow': {
105
- applyShadowPartialAlias(token, mode, { ...options, node });
106
- break;
107
- }
108
- case 'strokeStyle': {
109
- applyStrokeStylePartialAlias(token, mode, { ...options, node });
110
- break;
111
- }
112
- case 'transition': {
113
- applyTransitionPartialAlias(token, mode, { ...options, node });
114
- break;
115
- }
116
- case 'typography': {
117
- applyTypographyPartialAlias(token, mode, { ...options, node });
118
- break;
119
- }
120
- }
121
- }
122
- }
123
-
124
- const LIST_FORMAT = new Intl.ListFormat('en-us', { type: 'disjunction' });
125
-
126
- /**
127
- * Resolve alias. Also add info on root node if it’s the root token (has .id)
128
- */
129
- function resolveAlias(alias: string, options: { token: TokenNormalized; expectedType?: string[] } & ApplyAliasOptions) {
130
- const baseMessage = {
131
- group: 'parser' as const,
132
- label: 'alias',
133
- node: options?.node,
134
- filename: options.filename,
135
- src: options.src,
136
- };
137
- const { logger, token, tokensSet } = options;
138
- const shallowAliasID = parseAlias(alias);
139
- const { token: resolvedToken, chain } = _resolveAliasInner(shallowAliasID, options);
140
-
141
- // Apply missing $types while resolving
142
- if (!tokensSet[token.id]!.$type) {
143
- tokensSet[token.id]!.$type = resolvedToken!.$type;
144
- }
145
-
146
- // throw error if expectedType differed
147
- const expectedType = [...(options.expectedType ?? [])];
148
- if (token.$type && !expectedType?.length) {
149
- expectedType.push(token.$type);
150
- }
151
- if (expectedType?.length && !expectedType.includes(resolvedToken!.$type)) {
152
- logger.error({
153
- ...baseMessage,
154
- message: `Invalid alias: expected $type: ${LIST_FORMAT.format(expectedType!)}, received $type: ${resolvedToken!.$type}.`,
155
- node: (options.node?.type === 'Object' && getObjMembers(options.node).$value) || baseMessage.node,
156
- });
157
- }
158
-
159
- // Apply reverse aliases as we’re traversing the graph
160
- if (chain?.length && resolvedToken) {
161
- let needsSort = false;
162
- for (const id of chain) {
163
- if (id !== resolvedToken.id && !resolvedToken.aliasedBy?.includes(id)) {
164
- resolvedToken.aliasedBy ??= [];
165
- resolvedToken.aliasedBy!.push(id);
166
- needsSort = true;
167
- }
168
- if (token && !resolvedToken.aliasedBy?.includes(token.id)) {
169
- resolvedToken.aliasedBy ??= [];
170
- resolvedToken.aliasedBy!.push(token.id);
171
- needsSort = true;
172
- }
173
- }
174
- if (needsSort) {
175
- resolvedToken.aliasedBy!.sort((a, b) => a.localeCompare(b, 'en-us', { numeric: true }));
176
- }
177
- }
178
- return { resolvedToken: resolvedToken!, aliasChain: chain };
179
- }
180
-
181
- function _resolveAliasInner(
182
- alias: string,
183
- {
184
- scanned = [],
185
- ...options
186
- }: {
187
- tokensSet: Record<string, TokenNormalized>;
188
- logger: Logger;
189
- filename?: URL;
190
- src: string;
191
- node: AnyNode;
192
- scanned?: string[];
193
- },
194
- ): { token: TokenNormalized; chain: string[] } {
195
- const { logger, filename, src, node, tokensSet } = options;
196
- const baseMessage = { group: 'parser' as const, label: 'alias', filename, src, node };
197
- const id = parseAlias(alias);
198
- if (!tokensSet[id]) {
199
- logger.error({ ...baseMessage, message: `Alias {${alias}} not found.` });
200
- }
201
- if (scanned.includes(id)) {
202
- logger.error({ ...baseMessage, message: `Circular alias detected from ${alias}.` });
203
- }
204
-
205
- const token = tokensSet[id]!;
206
- scanned.push(id);
207
-
208
- // important: use originalValue to trace the full alias path correctly
209
- // finish resolution
210
- if (typeof token.originalValue.$value !== 'string' || !isAlias(token.originalValue.$value)) {
211
- return { token, chain: scanned };
212
- }
213
-
214
- // continue resolving
215
- return _resolveAliasInner(token.originalValue.$value as string, { ...options, scanned });
216
- }
217
-
218
- function applyBorderPartialAlias(token: BorderTokenNormalized, mode: string, options: ApplyAliasOptions): void {
219
- for (const [k, v] of Object.entries(token.mode[mode]!.$value)) {
220
- if (typeof v === 'string' && isAlias(v)) {
221
- token.mode[mode]!.partialAliasOf ??= {};
222
- const node = (getObjMembers(options.node)[k] as any) || options.node;
223
- const { resolvedToken } = resolveAlias(v, {
224
- ...options,
225
- token,
226
- expectedType: { color: ['color'], width: ['dimension'], style: ['strokeStyle'] }[k],
227
- node,
228
- });
229
- (token.mode[mode]!.partialAliasOf as any)[k] = parseAlias(v);
230
- if (mode === '.') {
231
- token.partialAliasOf ??= {};
232
- (token.partialAliasOf as any)[k] = parseAlias(v);
233
- }
234
- (token.mode[mode]!.$value as any)[k] = resolvedToken.$value;
235
- }
236
- }
237
- }
238
-
239
- function applyGradientPartialAlias(token: GradientTokenNormalized, mode: string, options: ApplyAliasOptions): void {
240
- for (let i = 0; i < token.mode[mode]!.$value.length; i++) {
241
- const step = token.mode[mode]!.$value[i]!;
242
- for (const [k, v] of Object.entries(step)) {
243
- if (typeof v === 'string' && isAlias(v)) {
244
- token.mode[mode]!.partialAliasOf ??= [];
245
- (token.mode[mode]!.partialAliasOf as any)[i] ??= {};
246
- const expectedType = { color: ['color'], position: ['number'] }[k];
247
- let node = ((options.node as unknown as ArrayNode | undefined)?.elements?.[i]?.value as any) || options.node;
248
- if (node.type === 'Object') {
249
- node = getObjMembers(node)[k] || node;
250
- }
251
- const { resolvedToken } = resolveAlias(v, { ...options, token, expectedType, node });
252
- (token.mode[mode]!.partialAliasOf[i] as any)[k] = parseAlias(v);
253
- if (mode === '.') {
254
- token.partialAliasOf ??= [];
255
- (token.partialAliasOf as any)[i] ??= {};
256
- (token.partialAliasOf[i] as any)[k] = parseAlias(v);
257
- }
258
- (step as any)[k] = resolvedToken.$value;
259
- }
260
- }
261
- }
262
- }
263
-
264
- function applyShadowPartialAlias(token: ShadowTokenNormalized, mode: string, options: ApplyAliasOptions): void {
265
- // shadow-only fix: historically this token type may or may not allow an array
266
- // of values, and at this stage in parsing, they all might not have been
267
- // normalized yet.
268
- if (!Array.isArray(token.mode[mode]!.$value)) {
269
- token.mode[mode]!.$value = [token.mode[mode]!.$value];
270
- }
271
-
272
- for (let i = 0; i < token.mode[mode]!.$value.length; i++) {
273
- const layer = token.mode[mode]!.$value[i]!;
274
- for (const [k, v] of Object.entries(layer)) {
275
- if (typeof v === 'string' && isAlias(v)) {
276
- token.mode[mode]!.partialAliasOf ??= [];
277
- token.mode[mode]!.partialAliasOf[i] ??= {};
278
- const expectedType = {
279
- offsetX: ['dimension'],
280
- offsetY: ['dimension'],
281
- blur: ['dimension'],
282
- spread: ['dimension'],
283
- color: ['color'],
284
- inset: ['boolean'],
285
- }[k];
286
- let node = ((options.node as unknown as ArrayNode | undefined)?.elements?.[i] as any) || options.node;
287
- if (node.type === 'Object') {
288
- node = getObjMembers(node)[k] || node;
289
- }
290
- const { resolvedToken } = resolveAlias(v, { ...options, token, expectedType, node });
291
- (token.mode[mode]!.partialAliasOf[i] as any)[k] = parseAlias(v);
292
- if (mode === '.') {
293
- token.partialAliasOf ??= [];
294
- token.partialAliasOf[i] ??= {};
295
- (token.partialAliasOf[i] as any)[k] = parseAlias(v);
296
- }
297
- (layer as any)[k] = resolvedToken.$value;
298
- }
299
- }
300
- }
301
- }
302
-
303
- function applyStrokeStylePartialAlias(
304
- token: StrokeStyleTokenNormalized,
305
- mode: string,
306
- options: ApplyAliasOptions,
307
- ): void {
308
- // only dashArray can be aliased
309
- if (typeof token.mode[mode]!.$value !== 'object' || !('dashArray' in token.mode[mode]!.$value)) {
310
- return;
311
- }
312
-
313
- for (let i = 0; i < token.mode[mode]!.$value.dashArray.length; i++) {
314
- const dash = token.mode[mode]!.$value.dashArray[i]!;
315
- if (typeof dash === 'string' && isAlias(dash)) {
316
- let node = (getObjMembers(options.node).dashArray as any) || options.node;
317
- if (node.type === 'Array') {
318
- node = ((node as unknown as ArrayNode | undefined)?.elements?.[i]?.value as any) || node;
319
- }
320
- const { resolvedToken } = resolveAlias(dash, {
321
- ...options,
322
- token,
323
- expectedType: ['dimension'],
324
- node,
325
- });
326
- (token.mode[mode]!.$value as any).dashArray[i] = resolvedToken.$value;
327
- }
328
- }
329
- }
330
-
331
- function applyTransitionPartialAlias(token: TransitionTokenNormalized, mode: string, options: ApplyAliasOptions): void {
332
- for (const [k, v] of Object.entries(token.mode[mode]!.$value)) {
333
- if (typeof v === 'string' && isAlias(v)) {
334
- token.mode[mode]!.partialAliasOf ??= {};
335
- const expectedType = { duration: ['duration'], delay: ['duration'], timingFunction: ['cubicBezier'] }[k];
336
- const node = (getObjMembers(options.node)[k] as any) || options.node;
337
- const { resolvedToken } = resolveAlias(v, { ...options, token, expectedType, node });
338
- (token.mode[mode]!.partialAliasOf as any)[k] = parseAlias(v);
339
- if (mode === '.') {
340
- token.partialAliasOf ??= {};
341
- (token.partialAliasOf as any)[k] = parseAlias(v);
342
- }
343
- (token.mode[mode]!.$value as any)[k] = resolvedToken.$value;
344
- }
345
- }
346
- }
347
-
348
- function applyTypographyPartialAlias(token: TypographyTokenNormalized, mode: string, options: ApplyAliasOptions): void {
349
- for (const [k, v] of Object.entries(token.mode[mode]!.$value)) {
350
- if (typeof v === 'string' && isAlias(v)) {
351
- token.partialAliasOf ??= {};
352
- token.mode[mode]!.partialAliasOf ??= {};
353
- const expectedType = {
354
- fontFamily: ['fontFamily'],
355
- fontSize: ['dimension'],
356
- fontWeight: ['fontWeight'],
357
- letterSpacing: ['dimension'],
358
- lineHeight: ['dimension', 'number'],
359
- }[k] || ['string'];
360
- const node = (getObjMembers(options.node)[k] as any) || options.node;
361
- const { resolvedToken } = resolveAlias(v, { ...options, token, expectedType, node });
362
- (token.mode[mode]!.partialAliasOf as any)[k] = parseAlias(v);
363
- if (mode === '.') {
364
- token.partialAliasOf[k] = parseAlias(v);
365
- }
366
- (token.mode[mode]!.$value as any)[k] = resolvedToken.$value;
367
- }
368
- }
369
- }