@terrazzo/parser 0.0.10 → 0.0.12
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 +10 -0
- package/README.md +1 -1
- package/build/index.d.ts +7 -7
- package/build/index.js +5 -5
- package/lint/index.d.ts +4 -2
- package/logger.d.ts +4 -2
- package/logger.js +4 -2
- package/package.json +3 -3
- package/parse/index.d.ts +11 -3
- package/parse/index.js +200 -113
- package/parse/normalize.js +8 -2
- package/parse/validate.d.ts +4 -3
- package/parse/validate.js +134 -107
package/parse/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { evaluate, parse as parseJSON, print } from '@humanwhocodes/momoa';
|
|
2
2
|
import { isAlias, parseAlias, pluralize, splitID } from '@terrazzo/token-tools';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
3
4
|
import lintRunner from '../lint/index.js';
|
|
4
5
|
import Logger from '../logger.js';
|
|
5
6
|
import normalize from './normalize.js';
|
|
@@ -9,50 +10,189 @@ import { getObjMembers, injectObjMembers, traverse } from './json.js';
|
|
|
9
10
|
|
|
10
11
|
export * from './validate.js';
|
|
11
12
|
|
|
13
|
+
/** @typedef {import("@humanwhocodes/momoa").DocumentNode} DocumentNode */
|
|
14
|
+
/** @typedef {import("../config.js").Plugin} Plugin */
|
|
15
|
+
/** @typedef {import("../types.js").TokenNormalized} TokenNormalized */
|
|
12
16
|
/**
|
|
13
|
-
* @typedef {import("@humanwhocodes/momoa").DocumentNode} DocumentNode
|
|
14
|
-
* @typedef {import("../config.js").Plugin} Plugin
|
|
15
|
-
* @typedef {import("../types.js").TokenNormalized} TokenNormalized
|
|
16
|
-
* @typedef {object} ParseOptions
|
|
17
|
-
* @typedef {Logger} ParseOptions.logger
|
|
18
|
-
* @typedef {boolean=false} ParseOptions.skipLint
|
|
19
|
-
* @typedef {Plugin[]} ParseOptions.plugins
|
|
20
17
|
* @typedef {object} ParseResult
|
|
21
|
-
* @
|
|
22
|
-
* @
|
|
18
|
+
* @property {Record<string, TokenNormalized} tokens
|
|
19
|
+
* @property {Object[]} src
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {object} ParseInput
|
|
23
|
+
* @property {string | object} src
|
|
24
|
+
* @property {URL} [filename]
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {object} ParseOptions
|
|
28
|
+
* @property {Logger} logger
|
|
29
|
+
* @property {import("../config.js").Config} config
|
|
30
|
+
* @property {boolean} [skipLint=false]
|
|
31
|
+
* @property {boolean} [continueOnError=false]
|
|
23
32
|
*/
|
|
24
|
-
|
|
25
33
|
/**
|
|
26
34
|
* Parse
|
|
27
|
-
* @param {
|
|
28
|
-
* @param {ParseOptions} options
|
|
35
|
+
* @param {ParseInput[]} input
|
|
36
|
+
* @param {ParseOptions} [options]
|
|
29
37
|
* @return {Promise<ParseResult>}
|
|
30
38
|
*/
|
|
31
39
|
export default async function parse(
|
|
32
40
|
input,
|
|
33
|
-
{ logger = new Logger(), skipLint = false, config, continueOnError = false } = {},
|
|
41
|
+
{ logger = new Logger(), skipLint = false, config = {}, continueOnError = false } = {},
|
|
34
42
|
) {
|
|
35
|
-
|
|
43
|
+
let tokens = {};
|
|
44
|
+
// note: only keeps track of sources with locations on disk; in-memory sources are discarded
|
|
45
|
+
// (it’s only for reporting line numbers, which doesn’t mean as much for dynamic sources)
|
|
46
|
+
const sources = {};
|
|
47
|
+
|
|
48
|
+
if (!Array.isArray(input)) {
|
|
49
|
+
logger.error({ group: 'parser', task: 'init', message: 'Input must be an array of input objects.' });
|
|
50
|
+
}
|
|
51
|
+
for (let i = 0; i < input.length; i++) {
|
|
52
|
+
if (!input[i] || typeof input[i] !== 'object') {
|
|
53
|
+
logger.error({ group: 'parser', task: 'init', message: `Input (${i}) must be an object.` });
|
|
54
|
+
}
|
|
55
|
+
if (!input[i].src || (typeof input[i].src !== 'string' && typeof input[i].src !== 'object')) {
|
|
56
|
+
logger.error({
|
|
57
|
+
group: 'parser',
|
|
58
|
+
task: 'init',
|
|
59
|
+
message: `Input (${i}) missing "src" with a JSON/YAML string, or JSON object.`,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (input[i].filename && !(input[i].filename instanceof URL)) {
|
|
63
|
+
logger.error({
|
|
64
|
+
group: 'parser',
|
|
65
|
+
task: 'init',
|
|
66
|
+
message: `Input (${i}) "filename" must be a URL (remote or file URL).`,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const result = await parseSingle(input[i].src, {
|
|
71
|
+
filename: input[i].filename,
|
|
72
|
+
logger,
|
|
73
|
+
config,
|
|
74
|
+
skipLint,
|
|
75
|
+
continueOnError,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
tokens = Object.assign(tokens, result.tokens);
|
|
79
|
+
if (input[i].filename) {
|
|
80
|
+
sources[input[i].filename.protocol === 'file:' ? fileURLToPath(input[i].filename) : input[i].filename.href] = {
|
|
81
|
+
filename: input[i].filename,
|
|
82
|
+
src: result.src,
|
|
83
|
+
document: result.document,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
36
87
|
|
|
37
88
|
const totalStart = performance.now();
|
|
38
89
|
|
|
90
|
+
// 5. Resolve aliases and populate groups
|
|
91
|
+
for (const id in tokens) {
|
|
92
|
+
if (!Object.hasOwn(tokens, id)) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const token = tokens[id];
|
|
96
|
+
applyAliases(token, {
|
|
97
|
+
tokens,
|
|
98
|
+
filename: sources[token.source.loc]?.filename,
|
|
99
|
+
src: sources[token.source.loc]?.src,
|
|
100
|
+
node: token.source.node,
|
|
101
|
+
logger,
|
|
102
|
+
});
|
|
103
|
+
token.mode['.'].$value = token.$value;
|
|
104
|
+
if (token.aliasOf) {
|
|
105
|
+
token.mode['.'].aliasOf = token.aliasOf;
|
|
106
|
+
}
|
|
107
|
+
if (token.partialAliasOf) {
|
|
108
|
+
token.mode['.'].partialAliasOf = token.partialAliasOf;
|
|
109
|
+
}
|
|
110
|
+
const { group: parentGroup } = splitID(id);
|
|
111
|
+
for (const siblingID in tokens) {
|
|
112
|
+
const { group: siblingGroup } = splitID(siblingID);
|
|
113
|
+
if (siblingGroup?.startsWith(parentGroup)) {
|
|
114
|
+
token.group.tokens.push(siblingID);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 6. resolve mode aliases
|
|
120
|
+
const modesStart = performance.now();
|
|
121
|
+
logger.debug({
|
|
122
|
+
group: 'parser',
|
|
123
|
+
task: 'modes',
|
|
124
|
+
message: 'Start mode resolution',
|
|
125
|
+
});
|
|
126
|
+
for (const id in tokens) {
|
|
127
|
+
if (!Object.hasOwn(tokens, id)) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
for (const mode in tokens[id].mode) {
|
|
131
|
+
if (mode === '.') {
|
|
132
|
+
continue; // skip shadow of root value
|
|
133
|
+
}
|
|
134
|
+
applyAliases(tokens[id].mode[mode], { tokens, node: tokens[id].mode[mode].source.node, logger });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
logger.debug({
|
|
138
|
+
group: 'parser',
|
|
139
|
+
task: 'modes',
|
|
140
|
+
message: 'Finish token modes',
|
|
141
|
+
timing: performance.now() - modesStart,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
logger.debug({
|
|
145
|
+
group: 'parser',
|
|
146
|
+
task: 'core',
|
|
147
|
+
message: 'Finish all parser tasks',
|
|
148
|
+
timing: performance.now() - totalStart,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
if (continueOnError) {
|
|
152
|
+
const { errorCount } = logger.stats();
|
|
153
|
+
if (errorCount > 0) {
|
|
154
|
+
logger.error({
|
|
155
|
+
message: `Parser encountered ${errorCount} ${pluralize(errorCount, 'error', 'errors')}. Exiting.`,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
tokens,
|
|
162
|
+
sources,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Parse a single input
|
|
168
|
+
* @param {string | object} input
|
|
169
|
+
* @param {object} options
|
|
170
|
+
* @param {URL} [options.filename]
|
|
171
|
+
* @param {Logger} [options.logger]
|
|
172
|
+
* @param {import("../config.js").Config} [options.config]
|
|
173
|
+
* @param {boolean} [options.skipLint]
|
|
174
|
+
*/
|
|
175
|
+
async function parseSingle(input, { filename, logger, config, skipLint, continueOnError = false }) {
|
|
39
176
|
// 1. Build AST
|
|
40
|
-
let
|
|
177
|
+
let src;
|
|
41
178
|
if (typeof input === 'string') {
|
|
42
|
-
|
|
179
|
+
src = input;
|
|
43
180
|
}
|
|
44
181
|
const startParsing = performance.now();
|
|
45
182
|
logger.debug({ group: 'parser', task: 'parse', message: 'Start tokens parsing' });
|
|
46
|
-
let
|
|
183
|
+
let document;
|
|
47
184
|
if (typeof input === 'string' && !maybeJSONString(input)) {
|
|
48
|
-
|
|
185
|
+
document = parseYAML(input, { logger }); // if string, but not JSON, attempt YAML
|
|
49
186
|
} else {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
187
|
+
document = parseJSON(
|
|
188
|
+
typeof input === 'string' ? input : JSON.stringify(input, undefined, 2), // everything else: assert it’s JSON-serializable
|
|
189
|
+
{
|
|
190
|
+
mode: 'jsonc',
|
|
191
|
+
},
|
|
192
|
+
);
|
|
53
193
|
}
|
|
54
|
-
if (!
|
|
55
|
-
|
|
194
|
+
if (!src) {
|
|
195
|
+
src = print(document, { indent: 2 });
|
|
56
196
|
}
|
|
57
197
|
logger.debug({
|
|
58
198
|
group: 'parser',
|
|
@@ -67,7 +207,7 @@ export default async function parse(
|
|
|
67
207
|
const startValidation = performance.now();
|
|
68
208
|
logger.debug({ group: 'parser', task: 'validate', message: 'Start tokens validation' });
|
|
69
209
|
const $typeInheritance = {};
|
|
70
|
-
traverse(
|
|
210
|
+
traverse(document, {
|
|
71
211
|
enter(node, parent, path) {
|
|
72
212
|
if (node.type === 'Member' && node.value.type === 'Object' && node.value.members) {
|
|
73
213
|
const members = getObjMembers(node.value);
|
|
@@ -96,7 +236,8 @@ export default async function parse(
|
|
|
96
236
|
if (parent$type && !members.$type) {
|
|
97
237
|
sourceNode.value = injectObjMembers(sourceNode.value, [parent$type]);
|
|
98
238
|
}
|
|
99
|
-
|
|
239
|
+
|
|
240
|
+
validate(sourceNode, { filename, src, logger });
|
|
100
241
|
|
|
101
242
|
const group = { id: splitID(id).group, tokens: [] };
|
|
102
243
|
if (parent$type) {
|
|
@@ -117,7 +258,10 @@ export default async function parse(
|
|
|
117
258
|
mode: {},
|
|
118
259
|
originalValue: evaluate(node.value),
|
|
119
260
|
group,
|
|
120
|
-
|
|
261
|
+
source: {
|
|
262
|
+
loc: filename ? fileURLToPath(filename) : undefined,
|
|
263
|
+
node: sourceNode.value,
|
|
264
|
+
},
|
|
121
265
|
};
|
|
122
266
|
if (members.$description?.value) {
|
|
123
267
|
token.$description = members.$description.value;
|
|
@@ -131,7 +275,10 @@ export default async function parse(
|
|
|
131
275
|
id: token.id,
|
|
132
276
|
$type: token.$type,
|
|
133
277
|
$value: mode === '.' ? token.$value : evaluate(modeValues[mode]),
|
|
134
|
-
|
|
278
|
+
source: {
|
|
279
|
+
loc: filename ? fileURLToPath(filename) : undefined,
|
|
280
|
+
node: mode === '.' ? structuredClone(token.source.node) : modeValues[mode],
|
|
281
|
+
},
|
|
135
282
|
};
|
|
136
283
|
if (token.$description) {
|
|
137
284
|
token.mode[mode].$description = token.$description;
|
|
@@ -140,7 +287,7 @@ export default async function parse(
|
|
|
140
287
|
|
|
141
288
|
tokens[id] = token;
|
|
142
289
|
} else if (members.value) {
|
|
143
|
-
logger.warn({ message: `Group ${id} has "value". Did you mean "$value"?`, node,
|
|
290
|
+
logger.warn({ message: `Group ${id} has "value". Did you mean "$value"?`, filename, node, src });
|
|
144
291
|
}
|
|
145
292
|
}
|
|
146
293
|
|
|
@@ -161,14 +308,14 @@ export default async function parse(
|
|
|
161
308
|
});
|
|
162
309
|
|
|
163
310
|
// 3. Execute lint runner with loaded plugins
|
|
164
|
-
if (!skipLint && plugins?.length) {
|
|
311
|
+
if (!skipLint && config?.plugins?.length) {
|
|
165
312
|
const lintStart = performance.now();
|
|
166
313
|
logger.debug({
|
|
167
314
|
group: 'parser',
|
|
168
315
|
task: 'validate',
|
|
169
316
|
message: 'Start token linting',
|
|
170
317
|
});
|
|
171
|
-
await lintRunner({
|
|
318
|
+
await lintRunner({ document, filename, src, config, logger });
|
|
172
319
|
logger.debug({
|
|
173
320
|
group: 'parser',
|
|
174
321
|
task: 'validate',
|
|
@@ -191,12 +338,12 @@ export default async function parse(
|
|
|
191
338
|
try {
|
|
192
339
|
tokens[id].$value = normalize(tokens[id]);
|
|
193
340
|
} catch (err) {
|
|
194
|
-
let node = tokens[id].
|
|
341
|
+
let { node } = tokens[id].source;
|
|
195
342
|
const members = getObjMembers(node);
|
|
196
343
|
if (members.$value) {
|
|
197
344
|
node = members.$value;
|
|
198
345
|
}
|
|
199
|
-
logger.error({ message: err.message,
|
|
346
|
+
logger.error({ message: err.message, filename, src, node, continueOnError });
|
|
200
347
|
}
|
|
201
348
|
for (const mode in tokens[id].mode) {
|
|
202
349
|
if (mode === '.') {
|
|
@@ -205,12 +352,18 @@ export default async function parse(
|
|
|
205
352
|
try {
|
|
206
353
|
tokens[id].mode[mode].$value = normalize(tokens[id].mode[mode]);
|
|
207
354
|
} catch (err) {
|
|
208
|
-
let node = tokens[id].
|
|
355
|
+
let { node } = tokens[id].source;
|
|
209
356
|
const members = getObjMembers(node);
|
|
210
357
|
if (members.$value) {
|
|
211
358
|
node = members.$value;
|
|
212
359
|
}
|
|
213
|
-
logger.error({
|
|
360
|
+
logger.error({
|
|
361
|
+
message: err.message,
|
|
362
|
+
filename,
|
|
363
|
+
src,
|
|
364
|
+
node: tokens[id].mode[mode].source.node,
|
|
365
|
+
continueOnError,
|
|
366
|
+
});
|
|
214
367
|
}
|
|
215
368
|
}
|
|
216
369
|
}
|
|
@@ -221,74 +374,7 @@ export default async function parse(
|
|
|
221
374
|
timing: performance.now() - normalizeStart,
|
|
222
375
|
});
|
|
223
376
|
|
|
224
|
-
|
|
225
|
-
for (const id in tokens) {
|
|
226
|
-
if (!Object.hasOwn(tokens, id)) {
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
const token = tokens[id];
|
|
230
|
-
applyAliases(token, { tokens, source, node: token.sourceNode, logger });
|
|
231
|
-
token.mode['.'].$value = token.$value;
|
|
232
|
-
if (token.aliasOf) {
|
|
233
|
-
token.mode['.'].aliasOf = token.aliasOf;
|
|
234
|
-
}
|
|
235
|
-
if (token.partialAliasOf) {
|
|
236
|
-
token.mode['.'].partialAliasOf = token.partialAliasOf;
|
|
237
|
-
}
|
|
238
|
-
const { group: parentGroup } = splitID(id);
|
|
239
|
-
for (const siblingID in tokens) {
|
|
240
|
-
const { group: siblingGroup } = splitID(siblingID);
|
|
241
|
-
if (siblingGroup?.startsWith(parentGroup)) {
|
|
242
|
-
token.group.tokens.push(siblingID);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// 6. resolve mode aliases
|
|
248
|
-
const modesStart = performance.now();
|
|
249
|
-
logger.debug({
|
|
250
|
-
group: 'parser',
|
|
251
|
-
task: 'modes',
|
|
252
|
-
message: 'Start mode resolution',
|
|
253
|
-
});
|
|
254
|
-
for (const id in tokens) {
|
|
255
|
-
if (!Object.hasOwn(tokens, id)) {
|
|
256
|
-
continue;
|
|
257
|
-
}
|
|
258
|
-
for (const mode in tokens[id].mode) {
|
|
259
|
-
if (mode === '.') {
|
|
260
|
-
continue; // skip shadow of root value
|
|
261
|
-
}
|
|
262
|
-
applyAliases(tokens[id].mode[mode], { tokens, source, node: tokens[id].mode[mode].sourceNode, logger });
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
logger.debug({
|
|
266
|
-
group: 'parser',
|
|
267
|
-
task: 'modes',
|
|
268
|
-
message: 'Finish token modes',
|
|
269
|
-
timing: performance.now() - modesStart,
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
logger.debug({
|
|
273
|
-
group: 'parser',
|
|
274
|
-
task: 'core',
|
|
275
|
-
message: 'Finish all parser tasks',
|
|
276
|
-
timing: performance.now() - totalStart,
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
if (continueOnError) {
|
|
280
|
-
const { errorCount } = logger.stats();
|
|
281
|
-
if (errorCount > 0) {
|
|
282
|
-
logger.error({
|
|
283
|
-
message: `Parser encountered ${errorCount} ${pluralize(errorCount, 'error', 'errors')}. Exiting.`,
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
return {
|
|
289
|
-
tokens,
|
|
290
|
-
ast,
|
|
291
|
-
};
|
|
377
|
+
return { tokens, document, src };
|
|
292
378
|
}
|
|
293
379
|
|
|
294
380
|
/**
|
|
@@ -306,31 +392,32 @@ export function maybeJSONString(input) {
|
|
|
306
392
|
* @param {Object} options
|
|
307
393
|
* @param {Record<string, TokenNormalized>} options.tokens
|
|
308
394
|
* @param {Logger} options.logger
|
|
395
|
+
* @param {string} [options.filename]
|
|
309
396
|
* @param {AnyNode} [options.node]
|
|
310
397
|
* @param {string} [options.string]
|
|
311
|
-
* @param {string
|
|
398
|
+
* @param {string} [options.scanned=[]]
|
|
312
399
|
* @param {string}
|
|
313
400
|
*/
|
|
314
|
-
export function resolveAlias(alias, { tokens, logger,
|
|
401
|
+
export function resolveAlias(alias, { tokens, logger, filename, src, node, scanned = [] }) {
|
|
315
402
|
const { id } = parseAlias(alias);
|
|
316
403
|
if (!tokens[id]) {
|
|
317
|
-
logger.error({ message: `Alias "${alias}" not found`,
|
|
404
|
+
logger.error({ message: `Alias "${alias}" not found`, filename, src, node });
|
|
318
405
|
}
|
|
319
406
|
if (scanned.includes(id)) {
|
|
320
|
-
logger.error({ message: `Circular alias detected from "${alias}"`,
|
|
407
|
+
logger.error({ message: `Circular alias detected from "${alias}"`, filename, src, node });
|
|
321
408
|
}
|
|
322
409
|
const token = tokens[id];
|
|
323
410
|
if (!isAlias(token.$value)) {
|
|
324
411
|
return id;
|
|
325
412
|
}
|
|
326
|
-
return resolveAlias(token.$value, { tokens, logger, node,
|
|
413
|
+
return resolveAlias(token.$value, { tokens, logger, filename, node, src, scanned: [...scanned, id] });
|
|
327
414
|
}
|
|
328
415
|
|
|
329
416
|
/** Resolve aliases, update values, and mutate `token` to add `aliasOf` / `partialAliasOf` */
|
|
330
|
-
function applyAliases(token, { tokens, logger,
|
|
417
|
+
function applyAliases(token, { tokens, logger, filename, src, node }) {
|
|
331
418
|
// handle simple aliases
|
|
332
419
|
if (isAlias(token.$value)) {
|
|
333
|
-
const aliasOfID = resolveAlias(token.$value, { tokens, logger, node,
|
|
420
|
+
const aliasOfID = resolveAlias(token.$value, { tokens, logger, filename, node, src });
|
|
334
421
|
const { mode: aliasMode } = parseAlias(token.$value);
|
|
335
422
|
const aliasOf = tokens[aliasOfID];
|
|
336
423
|
token.aliasOf = aliasOfID;
|
|
@@ -353,7 +440,7 @@ function applyAliases(token, { tokens, logger, source, node }) {
|
|
|
353
440
|
if (!token.partialAliasOf) {
|
|
354
441
|
token.partialAliasOf = [];
|
|
355
442
|
}
|
|
356
|
-
const aliasOfID = resolveAlias(token.$value[i], { tokens, logger, node,
|
|
443
|
+
const aliasOfID = resolveAlias(token.$value[i], { tokens, logger, filename, node, src });
|
|
357
444
|
const { mode: aliasMode } = parseAlias(token.$value[i]);
|
|
358
445
|
token.partialAliasOf[i] = aliasOfID;
|
|
359
446
|
token.$value[i] = tokens[aliasOfID].mode[aliasMode]?.$value || tokens[aliasOfID].$value;
|
|
@@ -366,7 +453,7 @@ function applyAliases(token, { tokens, logger, source, node }) {
|
|
|
366
453
|
if (!token.partialAliasOf[i]) {
|
|
367
454
|
token.partialAliasOf[i] = {};
|
|
368
455
|
}
|
|
369
|
-
const aliasOfID = resolveAlias(token.$value[i][property], { tokens, logger, node,
|
|
456
|
+
const aliasOfID = resolveAlias(token.$value[i][property], { tokens, logger, filename, node, src });
|
|
370
457
|
const { mode: aliasMode } = parseAlias(token.$value[i][property]);
|
|
371
458
|
token.$value[i][property] = tokens[aliasOfID].mode[aliasMode]?.$value || tokens[aliasOfID].$value;
|
|
372
459
|
token.partialAliasOf[i][property] = aliasOfID;
|
|
@@ -386,7 +473,7 @@ function applyAliases(token, { tokens, logger, source, node }) {
|
|
|
386
473
|
if (!token.partialAliasOf) {
|
|
387
474
|
token.partialAliasOf = {};
|
|
388
475
|
}
|
|
389
|
-
const aliasOfID = resolveAlias(token.$value[property], { tokens, logger, node,
|
|
476
|
+
const aliasOfID = resolveAlias(token.$value[property], { tokens, logger, filename, node, src });
|
|
390
477
|
const { mode: aliasMode } = parseAlias(token.$value[property]);
|
|
391
478
|
token.partialAliasOf[property] = aliasOfID;
|
|
392
479
|
token.$value[property] = tokens[aliasOfID].mode[aliasMode]?.$value || tokens[aliasOfID].$value;
|
|
@@ -395,7 +482,7 @@ function applyAliases(token, { tokens, logger, source, node }) {
|
|
|
395
482
|
else if (Array.isArray(token.$value[property])) {
|
|
396
483
|
for (let i = 0; i < token.$value[property].length; i++) {
|
|
397
484
|
if (isAlias(token.$value[property][i])) {
|
|
398
|
-
const aliasOfID = resolveAlias(token.$value[property][i], { tokens, logger, node,
|
|
485
|
+
const aliasOfID = resolveAlias(token.$value[property][i], { tokens, logger, filename, node, src });
|
|
399
486
|
if (!token.partialAliasOf) {
|
|
400
487
|
token.partialAliasOf = {};
|
|
401
488
|
}
|
package/parse/normalize.js
CHANGED
|
@@ -49,7 +49,7 @@ export default function normalizeValue(token) {
|
|
|
49
49
|
}
|
|
50
50
|
case 'dimension': {
|
|
51
51
|
if (token.$value === 0) {
|
|
52
|
-
return 0;
|
|
52
|
+
return '0';
|
|
53
53
|
}
|
|
54
54
|
return typeof token.$value === 'number' ? `${token.$value}px` : token.$value;
|
|
55
55
|
}
|
|
@@ -84,7 +84,13 @@ export default function normalizeValue(token) {
|
|
|
84
84
|
return typeof token.$value === 'number' ? token.$value : Number.parseFloat(token.$value);
|
|
85
85
|
}
|
|
86
86
|
case 'shadow': {
|
|
87
|
-
return Array.isArray(token.$value) ? token.$value : [token.$value]
|
|
87
|
+
return (Array.isArray(token.$value) ? token.$value : [token.$value]).map((layer) => ({
|
|
88
|
+
color: normalizeValue({ $type: 'color', $value: layer.color }),
|
|
89
|
+
offsetX: normalizeValue({ $type: 'dimension', $value: layer.offsetX ?? 0 }),
|
|
90
|
+
offsetY: normalizeValue({ $type: 'dimension', $value: layer.offsetY ?? 0 }),
|
|
91
|
+
blur: normalizeValue({ $type: 'dimension', $value: layer.blur ?? 0 }),
|
|
92
|
+
spread: normalizeValue({ $type: 'dimension', $value: layer.spread ?? 0 }),
|
|
93
|
+
}));
|
|
88
94
|
}
|
|
89
95
|
case 'strokeStyle': {
|
|
90
96
|
return token.$value;
|
package/parse/validate.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AnyNode,
|
|
1
|
+
import type { AnyNode, MemberNode, ValueNode } from '@humanwhocodes/momoa';
|
|
2
2
|
import type Logger from '../logger.js';
|
|
3
3
|
|
|
4
4
|
declare const FONT_WEIGHT_VALUES: Set<string>;
|
|
@@ -7,7 +7,8 @@ declare const STROKE_STYLE_VALUES: Set<string>;
|
|
|
7
7
|
declare const STROKE_STYLE_LINE_CAP_VALUES: Set<string>;
|
|
8
8
|
|
|
9
9
|
export interface ValidateOptions {
|
|
10
|
-
|
|
10
|
+
filename?: URL;
|
|
11
|
+
src: string;
|
|
11
12
|
logger: Logger;
|
|
12
13
|
}
|
|
13
14
|
|
|
@@ -17,7 +18,7 @@ export function validateBorder($value: ValueNode, node: AnyNode, options: Valida
|
|
|
17
18
|
|
|
18
19
|
export function validateColor($value: ValueNode, node: AnyNode, options: ValidateOptions): void;
|
|
19
20
|
|
|
20
|
-
export function
|
|
21
|
+
export function validateCubicBezier($value: ValueNode, node: AnyNode, options: ValidateOptions): void;
|
|
21
22
|
|
|
22
23
|
export function validateDimension($value: ValueNode, node: AnyNode, options: ValidateOptions): void;
|
|
23
24
|
|