@terrazzo/parser 0.1.3 → 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.
- package/.turbo/turbo-build.log +4 -0
- package/CHANGELOG.md +17 -0
- package/CONTRIBUTING.md +0 -12
- package/dist/build/index.d.ts +19 -0
- package/dist/build/index.js +165 -0
- package/dist/build/index.js.map +1 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.js +269 -0
- package/dist/config.js.map +1 -0
- package/{index.d.ts → dist/index.d.ts} +1 -5
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/code-frame.d.ts +30 -0
- package/dist/lib/code-frame.js +108 -0
- package/dist/lib/code-frame.js.map +1 -0
- package/dist/lint/index.d.ts +11 -0
- package/dist/lint/index.js +102 -0
- package/dist/lint/index.js.map +1 -0
- package/dist/lint/plugin-core/index.d.ts +12 -0
- package/dist/lint/plugin-core/index.js +40 -0
- package/dist/lint/plugin-core/index.js.map +1 -0
- package/dist/lint/plugin-core/lib/docs.d.ts +1 -0
- package/dist/lint/plugin-core/lib/docs.js +4 -0
- package/dist/lint/plugin-core/lib/docs.js.map +1 -0
- package/dist/lint/plugin-core/rules/a11y-min-contrast.d.ts +39 -0
- package/dist/lint/plugin-core/rules/a11y-min-contrast.js +58 -0
- package/dist/lint/plugin-core/rules/a11y-min-contrast.js.map +1 -0
- package/dist/lint/plugin-core/rules/a11y-min-font-size.d.ts +13 -0
- package/dist/lint/plugin-core/rules/a11y-min-font-size.js +45 -0
- package/dist/lint/plugin-core/rules/a11y-min-font-size.js.map +1 -0
- package/dist/lint/plugin-core/rules/colorspace.d.ts +14 -0
- package/dist/lint/plugin-core/rules/colorspace.js +85 -0
- package/dist/lint/plugin-core/rules/colorspace.js.map +1 -0
- package/dist/lint/plugin-core/rules/consistent-naming.d.ts +11 -0
- package/dist/lint/plugin-core/rules/consistent-naming.js +49 -0
- package/dist/lint/plugin-core/rules/consistent-naming.js.map +1 -0
- package/dist/lint/plugin-core/rules/descriptions.d.ts +9 -0
- package/dist/lint/plugin-core/rules/descriptions.js +32 -0
- package/dist/lint/plugin-core/rules/descriptions.js.map +1 -0
- package/dist/lint/plugin-core/rules/duplicate-values.d.ts +9 -0
- package/dist/lint/plugin-core/rules/duplicate-values.js +65 -0
- package/dist/lint/plugin-core/rules/duplicate-values.js.map +1 -0
- package/dist/lint/plugin-core/rules/max-gamut.d.ts +14 -0
- package/dist/lint/plugin-core/rules/max-gamut.js +101 -0
- package/dist/lint/plugin-core/rules/max-gamut.js.map +1 -0
- package/dist/lint/plugin-core/rules/required-children.d.ts +18 -0
- package/dist/lint/plugin-core/rules/required-children.js +78 -0
- package/dist/lint/plugin-core/rules/required-children.js.map +1 -0
- package/dist/lint/plugin-core/rules/required-modes.d.ts +13 -0
- package/dist/lint/plugin-core/rules/required-modes.js +52 -0
- package/dist/lint/plugin-core/rules/required-modes.js.map +1 -0
- package/dist/lint/plugin-core/rules/required-typography-properties.d.ts +10 -0
- package/dist/lint/plugin-core/rules/required-typography-properties.js +38 -0
- package/dist/lint/plugin-core/rules/required-typography-properties.js.map +1 -0
- package/dist/logger.d.ts +76 -0
- package/dist/logger.js +123 -0
- package/dist/logger.js.map +1 -0
- package/dist/parse/alias.d.ts +51 -0
- package/dist/parse/alias.js +188 -0
- package/dist/parse/alias.js.map +1 -0
- package/dist/parse/index.d.ts +27 -0
- package/dist/parse/index.js +379 -0
- package/dist/parse/index.js.map +1 -0
- package/dist/parse/json.d.ts +36 -0
- package/dist/parse/json.js +88 -0
- package/dist/parse/json.js.map +1 -0
- package/dist/parse/normalize.d.ts +23 -0
- package/dist/parse/normalize.js +163 -0
- package/dist/parse/normalize.js.map +1 -0
- package/dist/parse/validate.d.ts +45 -0
- package/dist/parse/validate.js +601 -0
- package/dist/parse/validate.js.map +1 -0
- package/dist/types.d.ts +264 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +7 -7
- package/{build/index.js → src/build/index.ts} +47 -63
- package/src/config.ts +280 -0
- package/src/index.ts +18 -0
- package/{lib/code-frame.js → src/lib/code-frame.ts} +41 -8
- package/src/lint/index.ts +135 -0
- package/src/lint/plugin-core/index.ts +47 -0
- package/src/lint/plugin-core/lib/docs.ts +3 -0
- package/src/lint/plugin-core/rules/a11y-min-contrast.ts +91 -0
- package/src/lint/plugin-core/rules/a11y-min-font-size.ts +64 -0
- package/src/lint/plugin-core/rules/colorspace.ts +101 -0
- package/src/lint/plugin-core/rules/consistent-naming.ts +65 -0
- package/src/lint/plugin-core/rules/descriptions.ts +41 -0
- package/src/lint/plugin-core/rules/duplicate-values.ts +80 -0
- package/src/lint/plugin-core/rules/max-gamut.ts +121 -0
- package/src/lint/plugin-core/rules/required-children.ts +104 -0
- package/src/lint/plugin-core/rules/required-modes.ts +71 -0
- package/src/lint/plugin-core/rules/required-typography-properties.ts +53 -0
- package/{logger.js → src/logger.ts} +55 -16
- package/src/parse/alias.ts +224 -0
- package/src/parse/index.ts +457 -0
- package/src/parse/json.ts +106 -0
- package/{parse/normalize.js → src/parse/normalize.ts} +70 -24
- package/{parse/validate.js → src/parse/validate.ts} +154 -236
- package/src/types.ts +310 -0
- package/build/index.d.ts +0 -113
- package/config.d.ts +0 -64
- package/config.js +0 -206
- package/index.js +0 -35
- package/lib/code-frame.d.ts +0 -56
- package/lint/index.d.ts +0 -44
- package/lint/index.js +0 -59
- package/lint/plugin-core/index.d.ts +0 -3
- package/lint/plugin-core/index.js +0 -12
- package/lint/plugin-core/rules/duplicate-values.d.ts +0 -10
- package/lint/plugin-core/rules/duplicate-values.js +0 -68
- package/logger.d.ts +0 -71
- package/parse/index.d.ts +0 -45
- package/parse/index.js +0 -592
- package/parse/json.d.ts +0 -30
- package/parse/json.js +0 -94
- package/parse/normalize.d.ts +0 -3
- 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.
|
|
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
|
+
}
|