@terrazzo/parser 0.5.0 → 0.6.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.
- package/CHANGELOG.md +40 -0
- package/dist/build/index.d.ts.map +1 -1
- package/dist/build/index.js +2 -4
- package/dist/build/index.js.map +1 -1
- package/dist/lint/plugin-core/rules/duplicate-values.js +1 -1
- package/dist/lint/plugin-core/rules/duplicate-values.js.map +1 -1
- package/dist/parse/alias.d.ts +29 -56
- package/dist/parse/alias.d.ts.map +1 -1
- package/dist/parse/alias.js +278 -223
- package/dist/parse/alias.js.map +1 -1
- package/dist/parse/index.d.ts +1 -1
- package/dist/parse/index.d.ts.map +1 -1
- package/dist/parse/index.js +17 -60
- package/dist/parse/index.js.map +1 -1
- package/dist/parse/normalize.d.ts.map +1 -1
- package/dist/parse/normalize.js +5 -1
- package/dist/parse/normalize.js.map +1 -1
- package/dist/parse/validate.d.ts +5 -0
- package/dist/parse/validate.d.ts.map +1 -1
- package/dist/parse/validate.js +12 -16
- package/dist/parse/validate.js.map +1 -1
- package/package.json +2 -2
- package/src/build/index.ts +2 -4
- package/src/lint/plugin-core/rules/duplicate-values.ts +1 -1
- package/src/parse/alias.ts +324 -257
- package/src/parse/index.ts +17 -61
- package/src/parse/normalize.ts +6 -1
- package/src/parse/validate.ts +12 -19
package/src/parse/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type ytm from 'yaml-to-momoa';
|
|
|
4
4
|
import lintRunner from '../lint/index.js';
|
|
5
5
|
import Logger from '../logger.js';
|
|
6
6
|
import type { ConfigInit, InputSource } from '../types.js';
|
|
7
|
-
import
|
|
7
|
+
import applyAliases from './alias.js';
|
|
8
8
|
import { getObjMembers, toMomoa, traverse } from './json.js';
|
|
9
9
|
import normalize from './normalize.js';
|
|
10
10
|
import validateTokenNode from './validate.js';
|
|
@@ -41,7 +41,7 @@ export interface ParseResult {
|
|
|
41
41
|
|
|
42
42
|
/** Parse */
|
|
43
43
|
export default async function parse(
|
|
44
|
-
|
|
44
|
+
_input: Omit<InputSource, 'document'> | Omit<InputSource, 'document'>[],
|
|
45
45
|
{
|
|
46
46
|
logger = new Logger(),
|
|
47
47
|
skipLint = false,
|
|
@@ -51,7 +51,8 @@ export default async function parse(
|
|
|
51
51
|
_sources = {},
|
|
52
52
|
}: ParseOptions = {} as ParseOptions,
|
|
53
53
|
): Promise<ParseResult> {
|
|
54
|
-
|
|
54
|
+
const input = Array.isArray(_input) ? _input : [_input];
|
|
55
|
+
let tokensSet: Record<string, TokenNormalized> = {};
|
|
55
56
|
|
|
56
57
|
if (!Array.isArray(input)) {
|
|
57
58
|
logger.error({ group: 'parser', label: 'init', message: 'Input must be an array of input objects.' });
|
|
@@ -91,7 +92,7 @@ export default async function parse(
|
|
|
91
92
|
continueOnError,
|
|
92
93
|
yamlToMomoa,
|
|
93
94
|
});
|
|
94
|
-
|
|
95
|
+
tokensSet = Object.assign(tokensSet, result.tokens);
|
|
95
96
|
if (src.filename) {
|
|
96
97
|
_sources[src.filename.href] = {
|
|
97
98
|
filename: src.filename,
|
|
@@ -107,35 +108,18 @@ export default async function parse(
|
|
|
107
108
|
// 5. Resolve aliases and populate groups
|
|
108
109
|
const aliasesStart = performance.now();
|
|
109
110
|
let aliasCount = 0;
|
|
110
|
-
for (const [id, token] of Object.entries(
|
|
111
|
-
if (!Object.hasOwn(tokens, id)) {
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
111
|
+
for (const [id, token] of Object.entries(tokensSet)) {
|
|
114
112
|
applyAliases(token, {
|
|
115
|
-
|
|
113
|
+
tokensSet,
|
|
116
114
|
filename: _sources[token.source.loc!]?.filename!,
|
|
117
115
|
src: _sources[token.source.loc!]?.src as string,
|
|
118
|
-
node: token.source.node,
|
|
116
|
+
node: (getObjMembers(token.source.node).$value as any) || token.source.node,
|
|
119
117
|
logger,
|
|
120
118
|
});
|
|
121
|
-
token.mode['.']!.$type = token.$type;
|
|
122
|
-
token.mode['.']!.$value = token.$value;
|
|
123
|
-
if (token.aliasOf) {
|
|
124
|
-
token.mode['.']!.aliasOf = token.aliasOf;
|
|
125
|
-
}
|
|
126
|
-
if (token.aliasChain) {
|
|
127
|
-
token.mode['.']!.aliasChain = token.aliasChain;
|
|
128
|
-
}
|
|
129
|
-
if (token.aliasedBy) {
|
|
130
|
-
token.mode['.']!.aliasedBy = token.aliasedBy;
|
|
131
|
-
}
|
|
132
|
-
if (token.partialAliasOf) {
|
|
133
|
-
token.mode['.']!.partialAliasOf = token.partialAliasOf;
|
|
134
|
-
}
|
|
135
119
|
aliasCount++;
|
|
136
120
|
const { group: parentGroup } = splitID(id);
|
|
137
121
|
if (parentGroup) {
|
|
138
|
-
for (const siblingID of Object.keys(
|
|
122
|
+
for (const siblingID of Object.keys(tokensSet)) {
|
|
139
123
|
const { group: siblingGroup } = splitID(siblingID);
|
|
140
124
|
if (siblingGroup?.startsWith(parentGroup)) {
|
|
141
125
|
token.group.tokens.push(siblingID);
|
|
@@ -150,34 +134,6 @@ export default async function parse(
|
|
|
150
134
|
timing: performance.now() - aliasesStart,
|
|
151
135
|
});
|
|
152
136
|
|
|
153
|
-
// 7. resolve mode aliases
|
|
154
|
-
const modesStart = performance.now();
|
|
155
|
-
let modeAliasCount = 0;
|
|
156
|
-
for (const [id, token] of Object.entries(tokens)) {
|
|
157
|
-
if (!Object.hasOwn(tokens, id)) {
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
for (const [mode, modeValue] of Object.entries(token.mode)) {
|
|
161
|
-
if (mode === '.') {
|
|
162
|
-
continue; // skip shadow of root value
|
|
163
|
-
}
|
|
164
|
-
modeAliasCount++;
|
|
165
|
-
applyAliases(modeValue, {
|
|
166
|
-
tokens,
|
|
167
|
-
node: modeValue.source.node,
|
|
168
|
-
logger,
|
|
169
|
-
src: _sources[token.source.loc!]?.src as string,
|
|
170
|
-
skipReverseAlias: true,
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
logger.debug({
|
|
175
|
-
message: `Resolved ${modeAliasCount} mode aliases`,
|
|
176
|
-
group: 'parser',
|
|
177
|
-
label: 'alias',
|
|
178
|
-
timing: performance.now() - modesStart,
|
|
179
|
-
});
|
|
180
|
-
|
|
181
137
|
logger.debug({
|
|
182
138
|
message: 'Finish all parser tasks',
|
|
183
139
|
group: 'parser',
|
|
@@ -196,7 +152,7 @@ export default async function parse(
|
|
|
196
152
|
}
|
|
197
153
|
|
|
198
154
|
return {
|
|
199
|
-
tokens,
|
|
155
|
+
tokens: tokensSet,
|
|
200
156
|
sources: Object.values(_sources),
|
|
201
157
|
};
|
|
202
158
|
}
|
|
@@ -229,7 +185,7 @@ async function parseSingle(
|
|
|
229
185
|
message: 'Finish JSON parsing',
|
|
230
186
|
timing: performance.now() - startParsing,
|
|
231
187
|
});
|
|
232
|
-
const
|
|
188
|
+
const tokensSet: Record<string, TokenNormalized> = {};
|
|
233
189
|
|
|
234
190
|
// 2. Walk AST to validate tokens
|
|
235
191
|
let tokenCount = 0;
|
|
@@ -250,7 +206,7 @@ async function parseSingle(
|
|
|
250
206
|
if (node.type === 'Member') {
|
|
251
207
|
const token = validateTokenNode(node, { filename, src, config, logger, parent, subpath, $typeInheritance });
|
|
252
208
|
if (token) {
|
|
253
|
-
|
|
209
|
+
tokensSet[token.id] = token;
|
|
254
210
|
tokenCount++;
|
|
255
211
|
}
|
|
256
212
|
}
|
|
@@ -265,9 +221,9 @@ async function parseSingle(
|
|
|
265
221
|
|
|
266
222
|
// 3. normalize values
|
|
267
223
|
const normalizeStart = performance.now();
|
|
268
|
-
for (const [id, token] of Object.entries(
|
|
224
|
+
for (const [id, token] of Object.entries(tokensSet)) {
|
|
269
225
|
try {
|
|
270
|
-
|
|
226
|
+
tokensSet[id]!.$value = normalize(token);
|
|
271
227
|
} catch (err) {
|
|
272
228
|
let { node } = token.source;
|
|
273
229
|
const members = getObjMembers(node);
|
|
@@ -289,7 +245,7 @@ async function parseSingle(
|
|
|
289
245
|
continue;
|
|
290
246
|
}
|
|
291
247
|
try {
|
|
292
|
-
|
|
248
|
+
tokensSet[id]!.mode[mode]!.$value = normalize({ $type: token.$type, ...modeValue });
|
|
293
249
|
} catch (err) {
|
|
294
250
|
let { node } = token.source;
|
|
295
251
|
const members = getObjMembers(node);
|
|
@@ -318,7 +274,7 @@ async function parseSingle(
|
|
|
318
274
|
// 4. Execute lint runner with loaded plugins
|
|
319
275
|
if (!skipLint && config?.plugins?.length) {
|
|
320
276
|
const lintStart = performance.now();
|
|
321
|
-
await lintRunner({ tokens, src, config, logger });
|
|
277
|
+
await lintRunner({ tokens: tokensSet, src, config, logger });
|
|
322
278
|
logger.debug({
|
|
323
279
|
message: `Linted ${tokenCount} tokens`,
|
|
324
280
|
group: 'parser',
|
|
@@ -330,7 +286,7 @@ async function parseSingle(
|
|
|
330
286
|
}
|
|
331
287
|
|
|
332
288
|
return {
|
|
333
|
-
tokens,
|
|
289
|
+
tokens: tokensSet,
|
|
334
290
|
document,
|
|
335
291
|
src,
|
|
336
292
|
};
|
package/src/parse/normalize.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type CubicBezierValue,
|
|
3
3
|
type DimensionValue,
|
|
4
|
+
type FontFamilyValue,
|
|
4
5
|
type GradientStopNormalized,
|
|
5
6
|
type GradientValueNormalized,
|
|
6
7
|
type ShadowValueNormalized,
|
|
@@ -39,7 +40,7 @@ const NUMBER_WITH_UNIT_RE = /(-?\d*\.?\d+)(.*)/;
|
|
|
39
40
|
|
|
40
41
|
/** Fill in defaults, and return predictable shapes for tokens */
|
|
41
42
|
export default function normalizeValue<T extends Token>(token: T): T['$value'] {
|
|
42
|
-
if (isAlias(token.$value)) {
|
|
43
|
+
if (typeof token.$value === 'string' && isAlias(token.$value)) {
|
|
43
44
|
return token.$value;
|
|
44
45
|
}
|
|
45
46
|
switch (token.$type) {
|
|
@@ -168,6 +169,10 @@ export default function normalizeValue<T extends Token>(token: T): T['$value'] {
|
|
|
168
169
|
const output: TypographyValueNormalized = {};
|
|
169
170
|
for (const [k, $value] of Object.entries(token.$value)) {
|
|
170
171
|
switch (k) {
|
|
172
|
+
case 'fontFamily': {
|
|
173
|
+
output[k] = normalizeValue({ $type: 'fontFamily', $value: $value as FontFamilyValue });
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
171
176
|
case 'fontSize':
|
|
172
177
|
case 'letterSpacing': {
|
|
173
178
|
output[k] = normalizeValue({ $type: 'dimension', $value: $value as DimensionValue });
|
package/src/parse/validate.ts
CHANGED
|
@@ -254,10 +254,8 @@ export function validateColor($value: ValueNode, node: AnyNode, { filename, src,
|
|
|
254
254
|
export function validateCubicBezier($value: ValueNode, _node: AnyNode, { filename, src, logger }: ValidateOptions) {
|
|
255
255
|
const baseMessage = { group: 'parser' as const, label: 'validate', filename, node: $value, src };
|
|
256
256
|
if ($value.type !== 'Array') {
|
|
257
|
-
logger.error({ ...baseMessage, message: `Expected array of
|
|
258
|
-
} else if (
|
|
259
|
-
!$value.elements.every((e) => e.value.type === 'Number' || (e.value.type === 'String' && isAlias(e.value.value)))
|
|
260
|
-
) {
|
|
257
|
+
logger.error({ ...baseMessage, message: `Expected array of numbers, received ${print($value)}` });
|
|
258
|
+
} else if (!$value.elements.every((e) => e.value.type === 'Number')) {
|
|
261
259
|
logger.error({ ...baseMessage, message: 'Expected an array of 4 numbers, received some non-numbers' });
|
|
262
260
|
} else if ($value.elements.length !== 4) {
|
|
263
261
|
logger.error({ ...baseMessage, message: `Expected an array of 4 numbers, received ${$value.elements.length}` });
|
|
@@ -750,6 +748,11 @@ export interface ValidateTokenNodeOptions {
|
|
|
750
748
|
$typeInheritance?: Record<string, Token['$type']>;
|
|
751
749
|
}
|
|
752
750
|
|
|
751
|
+
/**
|
|
752
|
+
* Validate does a little more than validate; it also converts to TokenNormalized
|
|
753
|
+
* and sets up the basic data structure. But aliases are unresolved, and we need
|
|
754
|
+
* a 2nd normalization pass afterward.
|
|
755
|
+
*/
|
|
753
756
|
export default function validateTokenNode(
|
|
754
757
|
node: MemberNode,
|
|
755
758
|
{ config, filename, logger, parent, src, subpath, $typeInheritance }: ValidateTokenNodeOptions,
|
|
@@ -861,28 +864,18 @@ export default function validateTokenNode(
|
|
|
861
864
|
|
|
862
865
|
// handle modes
|
|
863
866
|
// note that circular refs are avoided here, such as not duplicating `modes`
|
|
864
|
-
const modeValues = extensions?.mode
|
|
865
|
-
? getObjMembers(
|
|
866
|
-
// @ts-ignore
|
|
867
|
-
extensions.mode,
|
|
868
|
-
)
|
|
869
|
-
: {};
|
|
867
|
+
const modeValues = extensions?.mode ? getObjMembers(extensions.mode as any) : {};
|
|
870
868
|
for (const mode of ['.', ...Object.keys(modeValues)]) {
|
|
869
|
+
const modeValue = mode === '.' ? token.$value : (evaluate((modeValues as any)[mode]) as any);
|
|
871
870
|
token.mode[mode] = {
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
$type: token.$type,
|
|
875
|
-
// @ts-ignore
|
|
876
|
-
$value: mode === '.' ? token.$value : evaluate(modeValues[mode]),
|
|
871
|
+
$value: modeValue,
|
|
872
|
+
originalValue: modeValue,
|
|
877
873
|
source: {
|
|
878
874
|
loc: filename ? filename.href : undefined,
|
|
879
875
|
// @ts-ignore
|
|
880
|
-
node:
|
|
876
|
+
node: modeValues[mode],
|
|
881
877
|
},
|
|
882
878
|
};
|
|
883
|
-
if (token.$description) {
|
|
884
|
-
token.mode[mode]!.$description = token.$description;
|
|
885
|
-
}
|
|
886
879
|
}
|
|
887
880
|
|
|
888
881
|
// logger.debug({
|