@terrazzo/parser 0.4.0 → 0.5.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/CHANGELOG.md +31 -12
- package/dist/build/index.d.ts +1 -0
- package/dist/build/index.d.ts.map +1 -0
- package/dist/build/index.js +11 -10
- package/dist/build/index.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +42 -21
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/lib/code-frame.d.ts +1 -0
- package/dist/lib/code-frame.d.ts.map +1 -0
- package/dist/lint/index.d.ts +1 -0
- package/dist/lint/index.d.ts.map +1 -0
- package/dist/lint/index.js +8 -5
- package/dist/lint/index.js.map +1 -1
- package/dist/lint/plugin-core/index.d.ts +1 -0
- package/dist/lint/plugin-core/index.d.ts.map +1 -0
- package/dist/lint/plugin-core/lib/docs.d.ts +1 -0
- package/dist/lint/plugin-core/lib/docs.d.ts.map +1 -0
- package/dist/lint/plugin-core/rules/a11y-min-contrast.d.ts +1 -0
- package/dist/lint/plugin-core/rules/a11y-min-contrast.d.ts.map +1 -0
- package/dist/lint/plugin-core/rules/a11y-min-font-size.d.ts +1 -0
- package/dist/lint/plugin-core/rules/a11y-min-font-size.d.ts.map +1 -0
- package/dist/lint/plugin-core/rules/colorspace.d.ts +1 -0
- package/dist/lint/plugin-core/rules/colorspace.d.ts.map +1 -0
- package/dist/lint/plugin-core/rules/consistent-naming.d.ts +1 -0
- package/dist/lint/plugin-core/rules/consistent-naming.d.ts.map +1 -0
- package/dist/lint/plugin-core/rules/descriptions.d.ts +1 -0
- package/dist/lint/plugin-core/rules/descriptions.d.ts.map +1 -0
- package/dist/lint/plugin-core/rules/duplicate-values.d.ts +1 -0
- package/dist/lint/plugin-core/rules/duplicate-values.d.ts.map +1 -0
- package/dist/lint/plugin-core/rules/max-gamut.d.ts +1 -0
- package/dist/lint/plugin-core/rules/max-gamut.d.ts.map +1 -0
- package/dist/lint/plugin-core/rules/required-children.d.ts +1 -0
- package/dist/lint/plugin-core/rules/required-children.d.ts.map +1 -0
- package/dist/lint/plugin-core/rules/required-modes.d.ts +1 -0
- package/dist/lint/plugin-core/rules/required-modes.d.ts.map +1 -0
- package/dist/lint/plugin-core/rules/required-typography-properties.d.ts +1 -0
- package/dist/lint/plugin-core/rules/required-typography-properties.d.ts.map +1 -0
- package/dist/logger.d.ts +4 -3
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +25 -14
- package/dist/logger.js.map +1 -1
- package/dist/parse/alias.d.ts +13 -3
- package/dist/parse/alias.d.ts.map +1 -0
- package/dist/parse/alias.js +94 -43
- package/dist/parse/alias.js.map +1 -1
- package/dist/parse/index.d.ts +1 -0
- package/dist/parse/index.d.ts.map +1 -0
- package/dist/parse/index.js +58 -25
- package/dist/parse/index.js.map +1 -1
- package/dist/parse/json.d.ts +3 -3
- package/dist/parse/json.d.ts.map +1 -0
- package/dist/parse/json.js +5 -7
- package/dist/parse/json.js.map +1 -1
- package/dist/parse/normalize.d.ts +1 -0
- package/dist/parse/normalize.d.ts.map +1 -0
- package/dist/parse/normalize.js +8 -6
- package/dist/parse/normalize.js.map +1 -1
- package/dist/parse/validate.d.ts +1 -0
- package/dist/parse/validate.d.ts.map +1 -0
- package/dist/parse/validate.js +194 -110
- package/dist/parse/validate.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/build/index.ts +11 -10
- package/src/config.ts +42 -22
- package/src/lint/index.ts +8 -6
- package/src/logger.ts +30 -20
- package/src/parse/alias.ts +114 -45
- package/src/parse/index.ts +59 -28
- package/src/parse/json.ts +6 -8
- package/src/parse/normalize.ts +8 -6
- package/src/parse/validate.ts +204 -110
package/src/parse/alias.ts
CHANGED
|
@@ -34,7 +34,12 @@ export const COMPOSITE_TYPE_VALUES = {
|
|
|
34
34
|
},
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Resolve alias
|
|
39
|
+
* Note: to save work during resolution (and reduce memory), this will inject
|
|
40
|
+
* `aliasedBy` while it traverses. Resolution isn’t normally associated with
|
|
41
|
+
* mutation, but for our usecase it is preferable.
|
|
42
|
+
*/
|
|
38
43
|
export function resolveAlias(
|
|
39
44
|
alias: string,
|
|
40
45
|
{
|
|
@@ -52,19 +57,34 @@ export function resolveAlias(
|
|
|
52
57
|
node: AnyNode;
|
|
53
58
|
scanned?: string[];
|
|
54
59
|
},
|
|
55
|
-
) {
|
|
60
|
+
): { id: string; chain: string[] } {
|
|
56
61
|
const { id } = parseAlias(alias);
|
|
57
62
|
if (!tokens[id]) {
|
|
58
|
-
logger.error({ message: `Alias "${alias}" not found.`, filename, src, node });
|
|
63
|
+
logger.error({ group: 'parser', label: 'alias', message: `Alias "${alias}" not found.`, filename, src, node });
|
|
59
64
|
}
|
|
60
65
|
if (scanned.includes(id)) {
|
|
61
|
-
logger.error({
|
|
66
|
+
logger.error({
|
|
67
|
+
group: 'parser',
|
|
68
|
+
label: 'alias',
|
|
69
|
+
message: `Circular alias detected from "${alias}".`,
|
|
70
|
+
filename,
|
|
71
|
+
src,
|
|
72
|
+
node,
|
|
73
|
+
});
|
|
62
74
|
}
|
|
63
75
|
const token = tokens[id]!;
|
|
64
|
-
|
|
65
|
-
|
|
76
|
+
// important: use originalValue to trace the full alias path correctly
|
|
77
|
+
if (!isAlias(token.originalValue.$value)) {
|
|
78
|
+
return { id, chain: scanned.concat(id) };
|
|
66
79
|
}
|
|
67
|
-
return resolveAlias(token.$value as string, {
|
|
80
|
+
return resolveAlias(token.originalValue.$value as string, {
|
|
81
|
+
tokens,
|
|
82
|
+
logger,
|
|
83
|
+
filename,
|
|
84
|
+
node,
|
|
85
|
+
src,
|
|
86
|
+
scanned: scanned.concat(id),
|
|
87
|
+
});
|
|
68
88
|
}
|
|
69
89
|
|
|
70
90
|
/** Resolve aliases, update values, and mutate `token` to add `aliasOf` / `partialAliasOf` */
|
|
@@ -76,8 +96,37 @@ export function applyAliases(
|
|
|
76
96
|
filename,
|
|
77
97
|
src,
|
|
78
98
|
node,
|
|
79
|
-
|
|
99
|
+
skipReverseAlias,
|
|
100
|
+
}: {
|
|
101
|
+
tokens: Record<string, TokenNormalized>;
|
|
102
|
+
logger: Logger;
|
|
103
|
+
filename?: URL;
|
|
104
|
+
src: string;
|
|
105
|
+
node: AnyNode;
|
|
106
|
+
skipReverseAlias?: boolean;
|
|
107
|
+
},
|
|
80
108
|
) {
|
|
109
|
+
/**
|
|
110
|
+
* Add reverse alias lookups. Note this intentionally mutates the root
|
|
111
|
+
* references in `tokens` to save work. This is essential to doing everything
|
|
112
|
+
* in one pass while still being accurate.
|
|
113
|
+
*/
|
|
114
|
+
function addReverseAliases(resolvedID: string, ids: string[]) {
|
|
115
|
+
if (skipReverseAlias || !tokens[resolvedID]) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// populate entire chain of aliases, so we can redeclare tokens when needed
|
|
120
|
+
if (!tokens[resolvedID]!.aliasedBy) {
|
|
121
|
+
tokens[resolvedID]!.aliasedBy = [];
|
|
122
|
+
}
|
|
123
|
+
for (const link of [token.id, ...ids]) {
|
|
124
|
+
if (link !== resolvedID && !tokens[resolvedID]!.aliasedBy!.includes(link)) {
|
|
125
|
+
tokens[resolvedID]!.aliasedBy.push(link);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
81
130
|
const $valueNode =
|
|
82
131
|
(node.type === 'Object' && node.members.find((m) => (m.name as StringNode).value === '$value')?.value) || node;
|
|
83
132
|
const expectedAliasTypes =
|
|
@@ -85,29 +134,28 @@ export function applyAliases(
|
|
|
85
134
|
|
|
86
135
|
// handle simple aliases
|
|
87
136
|
if (isAlias(token.$value)) {
|
|
88
|
-
const
|
|
137
|
+
const { id: deepAliasID, chain } = resolveAlias(token.$value as string, { tokens, logger, filename, node, src });
|
|
89
138
|
const { mode: aliasMode } = parseAlias(token.$value as string);
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
139
|
+
const resolvedToken = tokens[deepAliasID]!;
|
|
140
|
+
|
|
141
|
+
// resolve value in root token, and add aliasOf
|
|
142
|
+
token.aliasOf = deepAliasID;
|
|
143
|
+
token.aliasChain = chain;
|
|
144
|
+
token.$value = resolvedToken!.mode[aliasMode!]?.$value || resolvedToken.$value;
|
|
145
|
+
|
|
146
|
+
addReverseAliases(deepAliasID, chain);
|
|
147
|
+
|
|
148
|
+
if (token.$type && token.$type !== resolvedToken.$type) {
|
|
94
149
|
logger.error({
|
|
95
|
-
|
|
150
|
+
group: 'parser',
|
|
151
|
+
label: 'alias',
|
|
152
|
+
message: `Invalid alias: expected $type: "${token.$type}", received $type: "${resolvedToken.$type}".`,
|
|
96
153
|
node: $valueNode,
|
|
97
154
|
filename,
|
|
98
155
|
src,
|
|
99
156
|
});
|
|
100
157
|
}
|
|
101
|
-
token.$type =
|
|
102
|
-
|
|
103
|
-
// also update aliased token’s .aliasedBy value
|
|
104
|
-
if (!tokens[aliasOfID]!.aliasedBy) {
|
|
105
|
-
tokens[aliasOfID]!.aliasedBy = [];
|
|
106
|
-
}
|
|
107
|
-
if (!tokens[aliasOfID]!.aliasedBy.includes(token.id)) {
|
|
108
|
-
tokens[aliasOfID]!.aliasedBy.push(token.id);
|
|
109
|
-
tokens[aliasOfID]!.aliasedBy.sort((a, b) => a.localeCompare(b, 'en-us', { numeric: true })); // sort to improve downstream plugins’ determinism
|
|
110
|
-
}
|
|
158
|
+
token.$type = resolvedToken.$type;
|
|
111
159
|
}
|
|
112
160
|
|
|
113
161
|
// handle aliases within array values (e.g. cubicBezier, gradient)
|
|
@@ -120,12 +168,12 @@ export function applyAliases(
|
|
|
120
168
|
token.partialAliasOf = [];
|
|
121
169
|
}
|
|
122
170
|
// @ts-ignore
|
|
123
|
-
const
|
|
171
|
+
const { id: deepAliasID } = resolveAlias(token.$value[i], { tokens, logger, filename, node, src });
|
|
124
172
|
// @ts-ignore
|
|
125
173
|
const { id: aliasID, mode: aliasMode } = parseAlias(token.$value[i]);
|
|
126
174
|
token.partialAliasOf![i] = aliasID;
|
|
127
175
|
// @ts-ignore
|
|
128
|
-
token.$value[i] = (aliasMode && tokens[
|
|
176
|
+
token.$value[i] = (aliasMode && tokens[deepAliasID].mode[aliasMode]?.$value) || tokens[deepAliasID].$value;
|
|
129
177
|
} else if (typeof token.$value[i] === 'object') {
|
|
130
178
|
for (const [property, subvalue] of Object.entries(token.$value[i]!)) {
|
|
131
179
|
if (isAlias(subvalue)) {
|
|
@@ -135,14 +183,19 @@ export function applyAliases(
|
|
|
135
183
|
if (!token.partialAliasOf[i]) {
|
|
136
184
|
token.partialAliasOf[i] = {};
|
|
137
185
|
}
|
|
138
|
-
const
|
|
139
|
-
const { id:
|
|
140
|
-
const
|
|
186
|
+
const { id: deepAliasID, chain } = resolveAlias(subvalue, { tokens, logger, filename, node, src });
|
|
187
|
+
const { id: shallowAliasID, mode: aliasMode } = parseAlias(subvalue);
|
|
188
|
+
const resolvedToken = tokens[deepAliasID]!;
|
|
189
|
+
|
|
190
|
+
addReverseAliases(deepAliasID, chain);
|
|
191
|
+
|
|
141
192
|
const possibleTypes: string[] = expectedAliasTypes?.[property as keyof typeof expectedAliasTypes] || [];
|
|
142
|
-
if (possibleTypes.length && !possibleTypes.includes(
|
|
193
|
+
if (possibleTypes.length && !possibleTypes.includes(resolvedToken.$type)) {
|
|
143
194
|
const elementNode = ($valueNode as ArrayNode).elements[i]!.value;
|
|
144
195
|
logger.error({
|
|
145
|
-
|
|
196
|
+
group: 'parser',
|
|
197
|
+
label: 'alias',
|
|
198
|
+
message: `Invalid alias: expected $type: "${possibleTypes.join('" or "')}", received $type: "${resolvedToken.$type}".`,
|
|
146
199
|
node: (elementNode as ObjectNode).members.find((m) => (m.name as StringNode).value === property)!.value,
|
|
147
200
|
filename,
|
|
148
201
|
src,
|
|
@@ -150,9 +203,9 @@ export function applyAliases(
|
|
|
150
203
|
}
|
|
151
204
|
|
|
152
205
|
// @ts-ignore
|
|
153
|
-
token.partialAliasOf[i][property] =
|
|
206
|
+
token.partialAliasOf[i][property] = shallowAliasID; // also keep the shallow alias here, too!
|
|
154
207
|
// @ts-ignore
|
|
155
|
-
token.$value[i][property] = (aliasMode &&
|
|
208
|
+
token.$value[i][property] = (aliasMode && resolvedToken.mode[aliasMode]?.$value) || resolvedToken.$value;
|
|
156
209
|
}
|
|
157
210
|
}
|
|
158
211
|
}
|
|
@@ -169,16 +222,21 @@ export function applyAliases(
|
|
|
169
222
|
if (!token.partialAliasOf) {
|
|
170
223
|
token.partialAliasOf = {};
|
|
171
224
|
}
|
|
172
|
-
const
|
|
173
|
-
const { id:
|
|
225
|
+
const { id: deepAliasID, chain } = resolveAlias(subvalue, { tokens, logger, filename, node, src });
|
|
226
|
+
const { id: shallowAliasID, mode: aliasMode } = parseAlias(subvalue);
|
|
227
|
+
|
|
228
|
+
addReverseAliases(deepAliasID, chain);
|
|
229
|
+
|
|
174
230
|
// @ts-ignore
|
|
175
|
-
token.partialAliasOf[property] =
|
|
176
|
-
const
|
|
231
|
+
token.partialAliasOf[property] = shallowAliasID; // keep the shallow alias!
|
|
232
|
+
const resolvedToken = tokens[deepAliasID];
|
|
177
233
|
// @ts-ignore
|
|
178
|
-
if (expectedAliasTypes?.[property] && !expectedAliasTypes[property].includes(
|
|
234
|
+
if (expectedAliasTypes?.[property] && !expectedAliasTypes[property].includes(resolvedToken!.$type)) {
|
|
179
235
|
logger.error({
|
|
236
|
+
group: 'parser',
|
|
237
|
+
label: 'alias',
|
|
180
238
|
// @ts-ignore
|
|
181
|
-
message: `Invalid alias: expected $type: "${expectedAliasTypes[property].join('" or "')}", received $type: "${
|
|
239
|
+
message: `Invalid alias: expected $type: "${expectedAliasTypes[property].join('" or "')}", received $type: "${resolvedToken.$type}".`,
|
|
182
240
|
// @ts-ignore
|
|
183
241
|
node: $valueNode.members.find((m) => m.name.value === property).value,
|
|
184
242
|
filename,
|
|
@@ -186,7 +244,7 @@ export function applyAliases(
|
|
|
186
244
|
});
|
|
187
245
|
}
|
|
188
246
|
// @ts-ignore
|
|
189
|
-
token.$value[property] =
|
|
247
|
+
token.$value[property] = resolvedToken.mode[aliasMode]?.$value || resolvedToken.$value;
|
|
190
248
|
}
|
|
191
249
|
|
|
192
250
|
// strokeStyle has an array within an object
|
|
@@ -197,7 +255,16 @@ export function applyAliases(
|
|
|
197
255
|
// @ts-ignore
|
|
198
256
|
if (isAlias(token.$value[property][i])) {
|
|
199
257
|
// @ts-ignore
|
|
200
|
-
const
|
|
258
|
+
const { id: deepAliasID, chain } = resolveAlias(token.$value[property][i], {
|
|
259
|
+
tokens,
|
|
260
|
+
logger,
|
|
261
|
+
filename,
|
|
262
|
+
node,
|
|
263
|
+
src,
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
addReverseAliases(deepAliasID, chain);
|
|
267
|
+
|
|
201
268
|
if (!token.partialAliasOf) {
|
|
202
269
|
token.partialAliasOf = {};
|
|
203
270
|
}
|
|
@@ -210,21 +277,23 @@ export function applyAliases(
|
|
|
210
277
|
const { id: aliasID, mode: aliasMode } = parseAlias(token.$value[property][i]);
|
|
211
278
|
// @ts-ignore
|
|
212
279
|
token.partialAliasOf[property][i] = aliasID; // keep the shallow alias!
|
|
213
|
-
const
|
|
280
|
+
const resolvedToken = tokens[deepAliasID];
|
|
214
281
|
// @ts-ignore
|
|
215
|
-
if (expectedAliasTypes?.[property] && !expectedAliasTypes[property].includes(
|
|
282
|
+
if (expectedAliasTypes?.[property] && !expectedAliasTypes[property].includes(resolvedToken.$type)) {
|
|
216
283
|
// @ts-ignore
|
|
217
284
|
const arrayNode = $valueNode.members.find((m) => m.name.value === property).value;
|
|
218
285
|
logger.error({
|
|
286
|
+
group: 'parser',
|
|
287
|
+
label: 'alias',
|
|
219
288
|
// @ts-ignore
|
|
220
|
-
message: `Invalid alias: expected $type: "${expectedAliasTypes[property].join('" or "')}", received $type: "${
|
|
289
|
+
message: `Invalid alias: expected $type: "${expectedAliasTypes[property].join('" or "')}", received $type: "${resolvedToken.$type}".`,
|
|
221
290
|
node: arrayNode.elements[i],
|
|
222
291
|
filename,
|
|
223
292
|
src,
|
|
224
293
|
});
|
|
225
294
|
}
|
|
226
295
|
// @ts-ignore
|
|
227
|
-
token.$value[property][i] = tokens[
|
|
296
|
+
token.$value[property][i] = tokens[deepAliasID].mode[aliasMode]?.$value || tokens[deepAliasID].$value;
|
|
228
297
|
}
|
|
229
298
|
}
|
|
230
299
|
}
|
package/src/parse/index.ts
CHANGED
|
@@ -105,6 +105,8 @@ export default async function parse(
|
|
|
105
105
|
const totalStart = performance.now();
|
|
106
106
|
|
|
107
107
|
// 5. Resolve aliases and populate groups
|
|
108
|
+
const aliasesStart = performance.now();
|
|
109
|
+
let aliasCount = 0;
|
|
108
110
|
for (const [id, token] of Object.entries(tokens)) {
|
|
109
111
|
if (!Object.hasOwn(tokens, id)) {
|
|
110
112
|
continue;
|
|
@@ -116,13 +118,21 @@ export default async function parse(
|
|
|
116
118
|
node: token.source.node,
|
|
117
119
|
logger,
|
|
118
120
|
});
|
|
121
|
+
token.mode['.']!.$type = token.$type;
|
|
119
122
|
token.mode['.']!.$value = token.$value;
|
|
120
123
|
if (token.aliasOf) {
|
|
121
124
|
token.mode['.']!.aliasOf = token.aliasOf;
|
|
122
125
|
}
|
|
126
|
+
if (token.aliasChain) {
|
|
127
|
+
token.mode['.']!.aliasChain = token.aliasChain;
|
|
128
|
+
}
|
|
129
|
+
if (token.aliasedBy) {
|
|
130
|
+
token.mode['.']!.aliasedBy = token.aliasedBy;
|
|
131
|
+
}
|
|
123
132
|
if (token.partialAliasOf) {
|
|
124
133
|
token.mode['.']!.partialAliasOf = token.partialAliasOf;
|
|
125
134
|
}
|
|
135
|
+
aliasCount++;
|
|
126
136
|
const { group: parentGroup } = splitID(id);
|
|
127
137
|
if (parentGroup) {
|
|
128
138
|
for (const siblingID of Object.keys(tokens)) {
|
|
@@ -133,14 +143,16 @@ export default async function parse(
|
|
|
133
143
|
}
|
|
134
144
|
}
|
|
135
145
|
}
|
|
136
|
-
|
|
137
|
-
// 6. resolve mode aliases
|
|
138
|
-
const modesStart = performance.now();
|
|
139
146
|
logger.debug({
|
|
140
|
-
message:
|
|
147
|
+
message: `Resolved ${aliasCount} aliases`,
|
|
141
148
|
group: 'parser',
|
|
142
|
-
label: '
|
|
149
|
+
label: 'alias',
|
|
150
|
+
timing: performance.now() - aliasesStart,
|
|
143
151
|
});
|
|
152
|
+
|
|
153
|
+
// 7. resolve mode aliases
|
|
154
|
+
const modesStart = performance.now();
|
|
155
|
+
let modeAliasCount = 0;
|
|
144
156
|
for (const [id, token] of Object.entries(tokens)) {
|
|
145
157
|
if (!Object.hasOwn(tokens, id)) {
|
|
146
158
|
continue;
|
|
@@ -149,18 +161,20 @@ export default async function parse(
|
|
|
149
161
|
if (mode === '.') {
|
|
150
162
|
continue; // skip shadow of root value
|
|
151
163
|
}
|
|
164
|
+
modeAliasCount++;
|
|
152
165
|
applyAliases(modeValue, {
|
|
153
166
|
tokens,
|
|
154
167
|
node: modeValue.source.node,
|
|
155
168
|
logger,
|
|
156
169
|
src: _sources[token.source.loc!]?.src as string,
|
|
170
|
+
skipReverseAlias: true,
|
|
157
171
|
});
|
|
158
172
|
}
|
|
159
173
|
}
|
|
160
174
|
logger.debug({
|
|
175
|
+
message: `Resolved ${modeAliasCount} mode aliases`,
|
|
161
176
|
group: 'parser',
|
|
162
|
-
label: '
|
|
163
|
-
message: 'Finish token modes',
|
|
177
|
+
label: 'alias',
|
|
164
178
|
timing: performance.now() - modesStart,
|
|
165
179
|
});
|
|
166
180
|
|
|
@@ -175,6 +189,7 @@ export default async function parse(
|
|
|
175
189
|
const { errorCount } = logger.stats();
|
|
176
190
|
if (errorCount > 0) {
|
|
177
191
|
logger.error({
|
|
192
|
+
group: 'parser',
|
|
178
193
|
message: `Parser encountered ${errorCount} ${pluralize(errorCount, 'error', 'errors')}. Exiting.`,
|
|
179
194
|
});
|
|
180
195
|
}
|
|
@@ -207,20 +222,19 @@ async function parseSingle(
|
|
|
207
222
|
): Promise<{ tokens: Record<string, Token>; document: DocumentNode; src?: string }> {
|
|
208
223
|
// 1. Build AST
|
|
209
224
|
const startParsing = performance.now();
|
|
210
|
-
logger.debug({ group: 'parser', label: 'parse', message: 'Start JSON parsing' });
|
|
211
225
|
const { src, document } = toMomoa(input, { filename, logger, continueOnError, yamlToMomoa });
|
|
212
226
|
logger.debug({
|
|
213
227
|
group: 'parser',
|
|
214
|
-
label: '
|
|
228
|
+
label: 'json',
|
|
215
229
|
message: 'Finish JSON parsing',
|
|
216
230
|
timing: performance.now() - startParsing,
|
|
217
231
|
});
|
|
218
232
|
const tokens: Record<string, TokenNormalized> = {};
|
|
219
233
|
|
|
220
234
|
// 2. Walk AST to validate tokens
|
|
235
|
+
let tokenCount = 0;
|
|
221
236
|
const startValidate = performance.now();
|
|
222
237
|
const $typeInheritance: Record<string, Token['$type']> = {};
|
|
223
|
-
logger.debug({ message: 'Start token validation', group: 'parser', label: 'validate' });
|
|
224
238
|
traverse(document, {
|
|
225
239
|
enter(node, parent, subpath) {
|
|
226
240
|
// if $type appears at root of tokens.json, collect it
|
|
@@ -237,19 +251,20 @@ async function parseSingle(
|
|
|
237
251
|
const token = validateTokenNode(node, { filename, src, config, logger, parent, subpath, $typeInheritance });
|
|
238
252
|
if (token) {
|
|
239
253
|
tokens[token.id] = token;
|
|
254
|
+
tokenCount++;
|
|
240
255
|
}
|
|
241
256
|
}
|
|
242
257
|
},
|
|
243
258
|
});
|
|
244
|
-
logger.debug({ message: 'Finish token validation', group: 'parser', label: 'validate', timing: startValidate });
|
|
245
|
-
|
|
246
|
-
// 3. normalize values
|
|
247
|
-
const normalizeStart = performance.now();
|
|
248
259
|
logger.debug({
|
|
249
|
-
message:
|
|
260
|
+
message: `Validated ${tokenCount} tokens`,
|
|
250
261
|
group: 'parser',
|
|
251
|
-
label: '
|
|
262
|
+
label: 'validate',
|
|
263
|
+
timing: performance.now() - startValidate,
|
|
252
264
|
});
|
|
265
|
+
|
|
266
|
+
// 3. normalize values
|
|
267
|
+
const normalizeStart = performance.now();
|
|
253
268
|
for (const [id, token] of Object.entries(tokens)) {
|
|
254
269
|
try {
|
|
255
270
|
tokens[id]!.$value = normalize(token);
|
|
@@ -259,7 +274,15 @@ async function parseSingle(
|
|
|
259
274
|
if (members.$value) {
|
|
260
275
|
node = members.$value as ObjectNode;
|
|
261
276
|
}
|
|
262
|
-
logger.error({
|
|
277
|
+
logger.error({
|
|
278
|
+
group: 'parser',
|
|
279
|
+
label: 'normalize',
|
|
280
|
+
message: (err as Error).message,
|
|
281
|
+
filename,
|
|
282
|
+
src,
|
|
283
|
+
node,
|
|
284
|
+
continueOnError,
|
|
285
|
+
});
|
|
263
286
|
}
|
|
264
287
|
for (const [mode, modeValue] of Object.entries(token.mode)) {
|
|
265
288
|
if (mode === '.') {
|
|
@@ -273,31 +296,39 @@ async function parseSingle(
|
|
|
273
296
|
if (members.$value) {
|
|
274
297
|
node = members.$value as ObjectNode;
|
|
275
298
|
}
|
|
276
|
-
logger.error({
|
|
299
|
+
logger.error({
|
|
300
|
+
group: 'parser',
|
|
301
|
+
label: 'normalize',
|
|
302
|
+
message: (err as Error).message,
|
|
303
|
+
filename,
|
|
304
|
+
src,
|
|
305
|
+
node: modeValue.source.node,
|
|
306
|
+
continueOnError,
|
|
307
|
+
});
|
|
277
308
|
}
|
|
278
309
|
}
|
|
279
310
|
}
|
|
311
|
+
logger.debug({
|
|
312
|
+
message: `Normalized ${tokenCount} tokens`,
|
|
313
|
+
group: 'parser',
|
|
314
|
+
label: 'normalize',
|
|
315
|
+
timing: performance.now() - normalizeStart,
|
|
316
|
+
});
|
|
280
317
|
|
|
281
318
|
// 4. Execute lint runner with loaded plugins
|
|
282
319
|
if (!skipLint && config?.plugins?.length) {
|
|
283
320
|
const lintStart = performance.now();
|
|
284
|
-
logger.debug({ message: 'Start token linting', group: 'parser', label: 'validate' });
|
|
285
321
|
await lintRunner({ tokens, src, config, logger });
|
|
286
322
|
logger.debug({
|
|
287
|
-
message:
|
|
323
|
+
message: `Linted ${tokenCount} tokens`,
|
|
288
324
|
group: 'parser',
|
|
289
|
-
label: '
|
|
325
|
+
label: 'lint',
|
|
290
326
|
timing: performance.now() - lintStart,
|
|
291
327
|
});
|
|
328
|
+
} else {
|
|
329
|
+
logger.debug({ message: 'Linting skipped', group: 'parser', label: 'lint' });
|
|
292
330
|
}
|
|
293
331
|
|
|
294
|
-
logger.debug({
|
|
295
|
-
message: 'Finish token normalization',
|
|
296
|
-
group: 'parser',
|
|
297
|
-
label: 'normalize',
|
|
298
|
-
timing: performance.now() - normalizeStart,
|
|
299
|
-
});
|
|
300
|
-
|
|
301
332
|
return {
|
|
302
333
|
tokens,
|
|
303
334
|
document,
|
package/src/parse/json.ts
CHANGED
|
@@ -55,18 +55,15 @@ export function getObjMembers(node: ObjectNode): Record<string | number, ValueNo
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
* Inject members to ObjectNode
|
|
58
|
+
* Inject members to ObjectNode
|
|
59
59
|
* @param {ObjectNode} node
|
|
60
60
|
* @param {MemberNode[]} members
|
|
61
|
-
* @return {ObjectNode}
|
|
62
61
|
*/
|
|
63
|
-
export function injectObjMembers(node: ObjectNode, members: MemberNode[] = [])
|
|
62
|
+
export function injectObjMembers(node: ObjectNode, members: MemberNode[] = []) {
|
|
64
63
|
if (node.type !== 'Object') {
|
|
65
|
-
return
|
|
64
|
+
return;
|
|
66
65
|
}
|
|
67
|
-
|
|
68
|
-
newNode.members.push(...members);
|
|
69
|
-
return newNode;
|
|
66
|
+
node.members.push(...members);
|
|
70
67
|
}
|
|
71
68
|
|
|
72
69
|
/**
|
|
@@ -176,11 +173,12 @@ export function toMomoa(
|
|
|
176
173
|
try {
|
|
177
174
|
document = yamlToMomoa(input); // if string, but not JSON, attempt YAML
|
|
178
175
|
} catch (err) {
|
|
179
|
-
logger.error({ message: String(err), filename, src: input, continueOnError });
|
|
176
|
+
logger.error({ group: 'parser', label: 'json', message: String(err), filename, src: input, continueOnError });
|
|
180
177
|
}
|
|
181
178
|
} else {
|
|
182
179
|
logger.error({
|
|
183
180
|
group: 'parser',
|
|
181
|
+
label: 'yaml',
|
|
184
182
|
message: `Install \`yaml-to-momoa\` package to parse YAML, and pass in as option, e.g.:
|
|
185
183
|
|
|
186
184
|
import { parse } from '@terrazzo/parser';
|
package/src/parse/normalize.ts
CHANGED
|
@@ -110,7 +110,7 @@ export default function normalizeValue<T extends Token>(token: T): T['$value'] {
|
|
|
110
110
|
}
|
|
111
111
|
const output: GradientValueNormalized = [];
|
|
112
112
|
for (let i = 0; i < token.$value.length; i++) {
|
|
113
|
-
const stop =
|
|
113
|
+
const stop = structuredClone(token.$value[i] as GradientStopNormalized);
|
|
114
114
|
stop.color = normalizeValue({ $type: 'color', $value: stop.color! });
|
|
115
115
|
if (stop.position === undefined) {
|
|
116
116
|
stop.position = i / (token.$value.length - 1);
|
|
@@ -166,22 +166,24 @@ export default function normalizeValue<T extends Token>(token: T): T['$value'] {
|
|
|
166
166
|
return token.$value;
|
|
167
167
|
}
|
|
168
168
|
const output: TypographyValueNormalized = {};
|
|
169
|
-
for (const k
|
|
169
|
+
for (const [k, $value] of Object.entries(token.$value)) {
|
|
170
170
|
switch (k) {
|
|
171
171
|
case 'fontSize':
|
|
172
172
|
case 'letterSpacing': {
|
|
173
|
-
output[k] = normalizeValue({ $type: 'dimension', $value:
|
|
173
|
+
output[k] = normalizeValue({ $type: 'dimension', $value: $value as DimensionValue });
|
|
174
174
|
break;
|
|
175
175
|
}
|
|
176
176
|
case 'lineHeight': {
|
|
177
177
|
output[k] = normalizeValue({
|
|
178
178
|
$type: typeof token.$value === 'number' ? 'number' : 'dimension',
|
|
179
|
-
$value:
|
|
179
|
+
$value: $value as any,
|
|
180
180
|
});
|
|
181
181
|
break;
|
|
182
182
|
}
|
|
183
|
-
default:
|
|
184
|
-
output[k] =
|
|
183
|
+
default: {
|
|
184
|
+
output[k] = $value;
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
185
187
|
}
|
|
186
188
|
}
|
|
187
189
|
return output;
|