@terrazzo/parser 2.0.0-beta.4 → 2.0.0-beta.6
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/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +253 -117
- package/dist/index.js.map +1 -1
- package/package.json +2 -3
package/dist/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { BORDER_REQUIRED_PROPERTIES, COLOR_SPACE, FONT_WEIGHTS, GRADIENT_REQUIRED_STOP_PROPERTIES, SHADOW_REQUIRED_PROPERTIES, STROKE_STYLE_LINE_CAP_VALUES, STROKE_STYLE_OBJECT_REQUIRED_PROPERTIES, STROKE_STYLE_STRING_VALUES, TRANSITION_REQUIRED_PROPERTIES,
|
|
2
|
-
import wcmatch from "wildcard-match";
|
|
1
|
+
import { BORDER_REQUIRED_PROPERTIES, COLOR_SPACE, CachedWildcardMatcher, FONT_WEIGHTS, GRADIENT_REQUIRED_STOP_PROPERTIES, SHADOW_REQUIRED_PROPERTIES, STROKE_STYLE_LINE_CAP_VALUES, STROKE_STYLE_OBJECT_REQUIRED_PROPERTIES, STROKE_STYLE_STRING_VALUES, TRANSITION_REQUIRED_PROPERTIES, isAlias, parseAlias, parseColor, pluralize, tokenToColor } from "@terrazzo/token-tools";
|
|
3
2
|
import * as momoa from "@humanwhocodes/momoa";
|
|
4
3
|
import pc from "picocolors";
|
|
5
4
|
import { merge } from "merge-anything";
|
|
@@ -140,6 +139,7 @@ function formatMessage(entry, severity) {
|
|
|
140
139
|
}
|
|
141
140
|
return message;
|
|
142
141
|
}
|
|
142
|
+
const debugMatch = new CachedWildcardMatcher();
|
|
143
143
|
var Logger = class {
|
|
144
144
|
level = "info";
|
|
145
145
|
debugScope = "*";
|
|
@@ -191,7 +191,7 @@ var Logger = class {
|
|
|
191
191
|
this.debugCount++;
|
|
192
192
|
let message = formatMessage(entry, "debug");
|
|
193
193
|
const debugPrefix = entry.label ? `${entry.group}:${entry.label}` : entry.group;
|
|
194
|
-
if (this.debugScope !== "*" && !
|
|
194
|
+
if (this.debugScope !== "*" && !debugMatch.match(this.debugScope)(debugPrefix)) return;
|
|
195
195
|
message.replace(/\[config[^\]]+\]/, (match) => pc.green(match)).replace(/\[parser[^\]]+\]/, (match) => pc.magenta(match)).replace(/\[lint[^\]]+\]/, (match) => pc.yellow(match)).replace(/\[plugin[^\]]+\]/, (match) => pc.cyan(match));
|
|
196
196
|
message = `${pc.dim(timeFormatter.format(performance.now()))} ${message}`;
|
|
197
197
|
if (typeof entry.timing === "number") message = `${message} ${formatTiming(entry.timing)}`;
|
|
@@ -243,6 +243,7 @@ function validateTransformParams({ params, logger, pluginName }) {
|
|
|
243
243
|
});
|
|
244
244
|
}
|
|
245
245
|
const FALLBACK_PERMUTATION_ID = JSON.stringify({ tzMode: "*" });
|
|
246
|
+
const cachedMatcher$1 = new CachedWildcardMatcher();
|
|
246
247
|
/** Run build stage */
|
|
247
248
|
async function build(tokens, { resolver, sources, logger = new Logger(), config }) {
|
|
248
249
|
const formats = {};
|
|
@@ -257,84 +258,98 @@ async function build(tokens, { resolver, sources, logger = new Logger(), config
|
|
|
257
258
|
});
|
|
258
259
|
return [];
|
|
259
260
|
}
|
|
260
|
-
const
|
|
261
|
-
const
|
|
262
|
-
const
|
|
261
|
+
const isLegacyModes = params.input && Object.keys(params.input).length === 1 && "tzMode" in params.input;
|
|
262
|
+
const permutationID = params.input && !isLegacyModes ? resolver.getPermutationID(params.input) : FALLBACK_PERMUTATION_ID;
|
|
263
|
+
const mode = params.mode || isLegacyModes && params.input.tzMode || void 0;
|
|
264
|
+
const singleTokenID = typeof params.id === "string" && tokens[params.id]?.id || Array.isArray(params.id) && params.id.length === 1 && tokens[params.id[0]]?.id || void 0;
|
|
265
|
+
const $type = typeof params.$type === "string" && [params.$type] || Array.isArray(params.$type) && params.$type || void 0;
|
|
266
|
+
const idMatcher = params.id && !singleTokenID && !isFullWildcard(params.id) ? cachedMatcher$1.tokenIDMatch(params.id) : null;
|
|
267
|
+
const modeMatcher = mode && mode !== "." && !isFullWildcard(mode) ? cachedMatcher$1.match(mode) : null;
|
|
263
268
|
return (formats[params.format]?.[permutationID] ?? []).filter((token) => {
|
|
264
|
-
if (
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}
|
|
268
|
-
if (tokenMatcher && !tokenMatcher(token.token.id)) return false;
|
|
269
|
-
if (params.input && token.permutationID !== resolver.getPermutationID(params.input)) return false;
|
|
270
|
-
if (modeMatcher && !modeMatcher(token.mode)) return false;
|
|
269
|
+
if (singleTokenID && token.id !== singleTokenID || idMatcher && !idMatcher(token.id)) return false;
|
|
270
|
+
if (params.$type && !$type?.some((value) => token.token.$type === value)) return false;
|
|
271
|
+
if (mode === "." && token.mode !== "." || modeMatcher && !modeMatcher(token.mode)) return false;
|
|
271
272
|
return true;
|
|
272
273
|
});
|
|
273
274
|
};
|
|
274
275
|
}
|
|
275
276
|
let transformsLocked = false;
|
|
276
277
|
const startTransform = performance.now();
|
|
277
|
-
for (const plugin of config.plugins) if (typeof plugin.transform === "function")
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
278
|
+
for (const plugin of config.plugins) if (typeof plugin.transform === "function") {
|
|
279
|
+
const pt = performance.now();
|
|
280
|
+
await plugin.transform({
|
|
281
|
+
context: { logger },
|
|
282
|
+
tokens,
|
|
283
|
+
sources,
|
|
284
|
+
getTransforms: getTransforms(plugin.name),
|
|
285
|
+
setTransform(id, params) {
|
|
286
|
+
if (transformsLocked) {
|
|
287
|
+
logger.warn({
|
|
288
|
+
message: "Attempted to call setTransform() after transform step has completed.",
|
|
289
|
+
group: "plugin",
|
|
290
|
+
label: plugin.name
|
|
291
|
+
});
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
const token = tokens[id];
|
|
295
|
+
if (!token) logger.error({
|
|
286
296
|
group: "plugin",
|
|
287
|
-
label: plugin.name
|
|
297
|
+
label: plugin.name,
|
|
298
|
+
message: `No token "${id}"`
|
|
288
299
|
});
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
if (!formats[params.format]) formats[params.format] = {};
|
|
303
|
-
if (!formats[params.format][permutationID]) formats[params.format][permutationID] = [];
|
|
304
|
-
let foundTokenI = -1;
|
|
305
|
-
if (params.mode) foundTokenI = formats[params.format][permutationID].findIndex((t) => id === t.id && (!params.localID || params.localID === t.localID) && params.mode === t.mode);
|
|
306
|
-
else if (params.input) {
|
|
300
|
+
const isLegacyModes = params.input && Object.keys(params.input).length === 1 && "tzMode" in params.input;
|
|
301
|
+
const permutationID = params.input && !isLegacyModes ? resolver.getPermutationID(params.input) : FALLBACK_PERMUTATION_ID;
|
|
302
|
+
const mode = params.mode || isLegacyModes && params.input.tzMode || void 0;
|
|
303
|
+
const cleanValue = typeof params.value === "string" ? params.value : { ...params.value };
|
|
304
|
+
validateTransformParams({
|
|
305
|
+
logger,
|
|
306
|
+
params: {
|
|
307
|
+
...params,
|
|
308
|
+
value: cleanValue
|
|
309
|
+
},
|
|
310
|
+
pluginName: plugin.name
|
|
311
|
+
});
|
|
312
|
+
if (!formats[params.format]) formats[params.format] = { [FALLBACK_PERMUTATION_ID]: [] };
|
|
307
313
|
if (!formats[params.format][permutationID]) formats[params.format][permutationID] = [];
|
|
308
|
-
foundTokenI = formats[params.format][permutationID].findIndex((t) => id === t.id && (!params.localID || params.localID === t.localID) &&
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
314
|
+
const foundTokenI = formats[params.format][permutationID].findIndex((t) => id === t.id && (!params.localID || params.localID === t.localID) && (!mode || t.mode === mode));
|
|
315
|
+
if (foundTokenI === -1) {
|
|
316
|
+
const transformedToken = {
|
|
317
|
+
...params,
|
|
318
|
+
id,
|
|
319
|
+
value: cleanValue,
|
|
320
|
+
type: typeof cleanValue === "string" ? SINGLE_VALUE : MULTI_VALUE,
|
|
321
|
+
mode: mode || ".",
|
|
322
|
+
token: makeReadOnlyToken(token),
|
|
323
|
+
permutationID,
|
|
324
|
+
input: JSON.parse(permutationID)
|
|
325
|
+
};
|
|
326
|
+
formats[params.format][permutationID].push(transformedToken);
|
|
327
|
+
if (params.input && !Object.keys(params.input).length && permutationID !== FALLBACK_PERMUTATION_ID) formats[params.format][FALLBACK_PERMUTATION_ID].push(transformedToken);
|
|
328
|
+
} else {
|
|
329
|
+
formats[params.format][permutationID][foundTokenI].value = cleanValue;
|
|
330
|
+
formats[params.format][permutationID][foundTokenI].type = typeof cleanValue === "string" ? SINGLE_VALUE : MULTI_VALUE;
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
resolver
|
|
334
|
+
});
|
|
335
|
+
logger.debug({
|
|
336
|
+
group: "plugin",
|
|
337
|
+
label: plugin.name,
|
|
338
|
+
message: "transform()",
|
|
339
|
+
timing: performance.now() - pt
|
|
340
|
+
});
|
|
341
|
+
}
|
|
327
342
|
transformsLocked = true;
|
|
328
343
|
logger.debug({
|
|
329
344
|
group: "parser",
|
|
330
345
|
label: "transform",
|
|
331
|
-
message: "transform()
|
|
346
|
+
message: "All plugins finished transform()",
|
|
332
347
|
timing: performance.now() - startTransform
|
|
333
348
|
});
|
|
334
349
|
const startBuild = performance.now();
|
|
335
350
|
await Promise.all(config.plugins.map(async (plugin) => {
|
|
336
351
|
if (typeof plugin.build === "function") {
|
|
337
|
-
const
|
|
352
|
+
const pb = performance.now();
|
|
338
353
|
await plugin.build({
|
|
339
354
|
context: { logger },
|
|
340
355
|
tokens,
|
|
@@ -352,18 +367,25 @@ async function build(tokens, { resolver, sources, logger = new Logger(), config
|
|
|
352
367
|
filename,
|
|
353
368
|
contents,
|
|
354
369
|
plugin: plugin.name,
|
|
355
|
-
time: performance.now() -
|
|
370
|
+
time: performance.now() - pb
|
|
356
371
|
});
|
|
357
372
|
}
|
|
358
373
|
});
|
|
374
|
+
logger.debug({
|
|
375
|
+
group: "plugin",
|
|
376
|
+
label: plugin.name,
|
|
377
|
+
message: "build()",
|
|
378
|
+
timing: performance.now() - pb
|
|
379
|
+
});
|
|
359
380
|
}
|
|
360
381
|
}));
|
|
361
382
|
logger.debug({
|
|
362
383
|
group: "parser",
|
|
363
384
|
label: "build",
|
|
364
|
-
message: "build()
|
|
385
|
+
message: "All plugins finished build()",
|
|
365
386
|
timing: performance.now() - startBuild
|
|
366
387
|
});
|
|
388
|
+
cachedMatcher$1.reset();
|
|
367
389
|
const startBuildEnd = performance.now();
|
|
368
390
|
await Promise.all(config.plugins.map(async (plugin) => plugin.buildEnd?.({
|
|
369
391
|
context: { logger },
|
|
@@ -380,6 +402,65 @@ async function build(tokens, { resolver, sources, logger = new Logger(), config
|
|
|
380
402
|
});
|
|
381
403
|
return result;
|
|
382
404
|
}
|
|
405
|
+
function isFullWildcard(value) {
|
|
406
|
+
return typeof value === "string" && (value === "*" || value === "**") || Array.isArray(value) && value.some((v) => v === "*" || v === "**");
|
|
407
|
+
}
|
|
408
|
+
/** Generate getters for transformed tokens. Reduces memory usage while improving accuracy. Provides some safety for read-only values. */
|
|
409
|
+
function makeReadOnlyToken(token) {
|
|
410
|
+
return {
|
|
411
|
+
get id() {
|
|
412
|
+
return token.id;
|
|
413
|
+
},
|
|
414
|
+
get $value() {
|
|
415
|
+
return token.$value;
|
|
416
|
+
},
|
|
417
|
+
get $type() {
|
|
418
|
+
return token.$type;
|
|
419
|
+
},
|
|
420
|
+
get $description() {
|
|
421
|
+
return token.$description;
|
|
422
|
+
},
|
|
423
|
+
get $deprecated() {
|
|
424
|
+
return token.$deprecated;
|
|
425
|
+
},
|
|
426
|
+
get $extends() {
|
|
427
|
+
return token.$extends;
|
|
428
|
+
},
|
|
429
|
+
get $extensions() {
|
|
430
|
+
return token.$extensions;
|
|
431
|
+
},
|
|
432
|
+
get mode() {
|
|
433
|
+
return token.mode;
|
|
434
|
+
},
|
|
435
|
+
get originalValue() {
|
|
436
|
+
return token.originalValue;
|
|
437
|
+
},
|
|
438
|
+
get aliasChain() {
|
|
439
|
+
return token.aliasChain;
|
|
440
|
+
},
|
|
441
|
+
get aliasOf() {
|
|
442
|
+
return token.aliasOf;
|
|
443
|
+
},
|
|
444
|
+
get partialAliasOf() {
|
|
445
|
+
return token.partialAliasOf;
|
|
446
|
+
},
|
|
447
|
+
get aliasedBy() {
|
|
448
|
+
return token.aliasedBy;
|
|
449
|
+
},
|
|
450
|
+
get group() {
|
|
451
|
+
return token.group;
|
|
452
|
+
},
|
|
453
|
+
get source() {
|
|
454
|
+
return token.source;
|
|
455
|
+
},
|
|
456
|
+
get jsonID() {
|
|
457
|
+
return token.jsonID;
|
|
458
|
+
},
|
|
459
|
+
get dependencies() {
|
|
460
|
+
return token.dependencies;
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
}
|
|
383
464
|
|
|
384
465
|
//#endregion
|
|
385
466
|
//#region src/lint/plugin-core/lib/docs.ts
|
|
@@ -435,6 +516,22 @@ const rule$26 = {
|
|
|
435
516
|
}
|
|
436
517
|
};
|
|
437
518
|
|
|
519
|
+
//#endregion
|
|
520
|
+
//#region src/lint/plugin-core/lib/matchers.ts
|
|
521
|
+
/**
|
|
522
|
+
* Share one cached matcher factory for all lint plugins.
|
|
523
|
+
*
|
|
524
|
+
* Creating matchers is CPU-intensive, however, if we made one matcher for very
|
|
525
|
+
* getTransform plugin query, we could end up with tens of thousands of
|
|
526
|
+
* matchers, all taking up space in memory, but without providing any caching
|
|
527
|
+
* benefits if a matcher is used only once. So a reasonable balance is we
|
|
528
|
+
* maintain one cache per task category, and we garbage-collect everything after
|
|
529
|
+
* it’s done. Lint tasks are likely to have frequently-occurring patterns. So
|
|
530
|
+
* we’d expect for most use cases a shared lint cache has benefits, but only
|
|
531
|
+
* so long as this doesn’t spread to other plugins and other task categories.
|
|
532
|
+
*/
|
|
533
|
+
const cachedLintMatcher = new CachedWildcardMatcher();
|
|
534
|
+
|
|
438
535
|
//#endregion
|
|
439
536
|
//#region src/lint/plugin-core/rules/a11y-min-font-size.ts
|
|
440
537
|
const A11Y_MIN_FONT_SIZE = "a11y/min-font-size";
|
|
@@ -450,7 +547,7 @@ const rule$25 = {
|
|
|
450
547
|
defaultOptions: {},
|
|
451
548
|
create({ tokens, options, report }) {
|
|
452
549
|
if (!options.minSizePx && !options.minSizeRem) throw new Error("Must specify at least one of minSizePx or minSizeRem");
|
|
453
|
-
const shouldIgnore = options.ignore ?
|
|
550
|
+
const shouldIgnore = options.ignore ? cachedLintMatcher.tokenIDMatch(options.ignore) : null;
|
|
454
551
|
for (const t of Object.values(tokens)) {
|
|
455
552
|
if (shouldIgnore?.(t.id)) continue;
|
|
456
553
|
if (t.aliasOf) continue;
|
|
@@ -491,7 +588,7 @@ const rule$24 = {
|
|
|
491
588
|
defaultOptions: { colorSpace: "srgb" },
|
|
492
589
|
create({ tokens, options, report }) {
|
|
493
590
|
if (!options.colorSpace) return;
|
|
494
|
-
const shouldIgnore = options.ignore ?
|
|
591
|
+
const shouldIgnore = options.ignore ? cachedLintMatcher.tokenIDMatch(options.ignore) : null;
|
|
495
592
|
for (const t of Object.values(tokens)) {
|
|
496
593
|
if (shouldIgnore?.(t.id)) continue;
|
|
497
594
|
if (t.aliasOf) continue;
|
|
@@ -602,7 +699,7 @@ const rule$22 = {
|
|
|
602
699
|
},
|
|
603
700
|
defaultOptions: {},
|
|
604
701
|
create({ tokens, options, report }) {
|
|
605
|
-
const shouldIgnore = options.ignore ?
|
|
702
|
+
const shouldIgnore = options.ignore ? cachedLintMatcher.tokenIDMatch(options.ignore) : null;
|
|
606
703
|
for (const t of Object.values(tokens)) {
|
|
607
704
|
if (shouldIgnore?.(t.id)) continue;
|
|
608
705
|
if (!t.$description) report({
|
|
@@ -629,7 +726,7 @@ const rule$21 = {
|
|
|
629
726
|
defaultOptions: {},
|
|
630
727
|
create({ report, tokens, options }) {
|
|
631
728
|
const values = {};
|
|
632
|
-
const shouldIgnore = options.ignore ?
|
|
729
|
+
const shouldIgnore = options.ignore ? cachedLintMatcher.tokenIDMatch(options.ignore) : null;
|
|
633
730
|
for (const t of Object.values(tokens)) {
|
|
634
731
|
if (shouldIgnore?.(t.id)) continue;
|
|
635
732
|
if (!values[t.$type]) values[t.$type] = /* @__PURE__ */ new Set();
|
|
@@ -682,7 +779,7 @@ const rule$20 = {
|
|
|
682
779
|
create({ tokens, options, report }) {
|
|
683
780
|
if (!options?.gamut) return;
|
|
684
781
|
if (options.gamut !== "srgb" && options.gamut !== "p3" && options.gamut !== "rec2020") throw new Error(`Unknown gamut "${options.gamut}". Options are "srgb", "p3", or "rec2020"`);
|
|
685
|
-
const shouldIgnore = options.ignore ?
|
|
782
|
+
const shouldIgnore = options.ignore ? cachedLintMatcher.tokenIDMatch(options.ignore) : null;
|
|
686
783
|
for (const t of Object.values(tokens)) {
|
|
687
784
|
if (shouldIgnore?.(t.id)) continue;
|
|
688
785
|
if (t.aliasOf) continue;
|
|
@@ -761,7 +858,7 @@ const rule$19 = {
|
|
|
761
858
|
const { match, requiredTokens, requiredGroups } = options.matches[matchI];
|
|
762
859
|
if (!match.length) throw new Error(`Match ${matchI}: must declare \`match: […]\``);
|
|
763
860
|
if (!requiredTokens?.length && !requiredGroups?.length) throw new Error(`Match ${matchI}: must declare either \`requiredTokens: […]\` or \`requiredGroups: […]\``);
|
|
764
|
-
const matcher =
|
|
861
|
+
const matcher = cachedLintMatcher.tokenIDMatch(match);
|
|
765
862
|
const matchGroups = [];
|
|
766
863
|
const matchTokens = [];
|
|
767
864
|
let tokensMatched = false;
|
|
@@ -816,7 +913,7 @@ const rule$18 = {
|
|
|
816
913
|
const { match, modes } = options.matches[matchI];
|
|
817
914
|
if (!match.length) throw new Error(`Match ${matchI}: must declare \`match: […]\``);
|
|
818
915
|
if (!modes?.length) throw new Error(`Match ${matchI}: must declare \`modes: […]\``);
|
|
819
|
-
const matcher =
|
|
916
|
+
const matcher = cachedLintMatcher.tokenIDMatch(match);
|
|
820
917
|
let tokensMatched = false;
|
|
821
918
|
for (const t of Object.values(tokens)) {
|
|
822
919
|
if (!matcher(t.id)) continue;
|
|
@@ -877,7 +974,7 @@ const rule$16 = {
|
|
|
877
974
|
create({ tokens, options, report }) {
|
|
878
975
|
if (!options) return;
|
|
879
976
|
if (!options.properties.length) throw new Error(`"properties" can’t be empty`);
|
|
880
|
-
const shouldIgnore = options.ignore ?
|
|
977
|
+
const shouldIgnore = options.ignore ? cachedLintMatcher.tokenIDMatch(options.ignore) : null;
|
|
881
978
|
for (const t of Object.values(tokens)) {
|
|
882
979
|
if (shouldIgnore?.(t.id)) continue;
|
|
883
980
|
if (t.$type !== "typography") continue;
|
|
@@ -1388,7 +1485,7 @@ const rule$6 = {
|
|
|
1388
1485
|
"lineHeight"
|
|
1389
1486
|
] },
|
|
1390
1487
|
create({ tokens, options, report }) {
|
|
1391
|
-
const isIgnored = options.ignore ?
|
|
1488
|
+
const isIgnored = options.ignore ? cachedLintMatcher.tokenIDMatch(options.ignore) : () => false;
|
|
1392
1489
|
for (const t of Object.values(tokens)) {
|
|
1393
1490
|
if (t.aliasOf || !t.originalValue || t.$type !== "typography" || isIgnored(t.id)) continue;
|
|
1394
1491
|
validateTypography(t.originalValue.$value, {
|
|
@@ -2298,14 +2395,14 @@ async function lintRunner({ tokens, filename, config = {}, sources, logger }) {
|
|
|
2298
2395
|
let message = "";
|
|
2299
2396
|
if (!descriptor.message && !descriptor.messageId) logger.error({
|
|
2300
2397
|
group: "lint",
|
|
2301
|
-
label: `${plugin.name}
|
|
2398
|
+
label: `${plugin.name}:${id}`,
|
|
2302
2399
|
message: "Unable to report error: missing message or messageId"
|
|
2303
2400
|
});
|
|
2304
2401
|
if (descriptor.message) message = descriptor.message;
|
|
2305
2402
|
else {
|
|
2306
2403
|
if (!(descriptor.messageId in (rule.meta?.messages ?? {}))) logger.error({
|
|
2307
2404
|
group: "lint",
|
|
2308
|
-
label: `${plugin.name}
|
|
2405
|
+
label: `${plugin.name}:${id}`,
|
|
2309
2406
|
message: `messageId "${descriptor.messageId}" does not exist`
|
|
2310
2407
|
});
|
|
2311
2408
|
message = rule.meta?.messages?.[descriptor.messageId] ?? "";
|
|
@@ -2341,6 +2438,7 @@ async function lintRunner({ tokens, filename, config = {}, sources, logger }) {
|
|
|
2341
2438
|
message: "Finished",
|
|
2342
2439
|
timing: performance.now() - s
|
|
2343
2440
|
});
|
|
2441
|
+
cachedLintMatcher.reset();
|
|
2344
2442
|
}
|
|
2345
2443
|
const errCount = errors.length ? `${errors.length} ${pluralize(errors.length, "error", "errors")}` : "";
|
|
2346
2444
|
const warnCount = warnings.length ? `${warnings.length} ${pluralize(warnings.length, "warning", "warnings")}` : "";
|
|
@@ -2372,6 +2470,13 @@ function toMomoa(srcRaw) {
|
|
|
2372
2470
|
});
|
|
2373
2471
|
}
|
|
2374
2472
|
|
|
2473
|
+
//#endregion
|
|
2474
|
+
//#region src/lib/array.ts
|
|
2475
|
+
/** JS compiler-optimizable comparator */
|
|
2476
|
+
function alphaComparator(a, b) {
|
|
2477
|
+
return a.localeCompare(b, "en-us", { numeric: true });
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2375
2480
|
//#endregion
|
|
2376
2481
|
//#region src/lib/resolver-utils.ts
|
|
2377
2482
|
/**
|
|
@@ -2393,8 +2498,32 @@ function filterResolverPaths(path) {
|
|
|
2393
2498
|
}
|
|
2394
2499
|
/** Make a deterministic string from an object */
|
|
2395
2500
|
function getPermutationID(input) {
|
|
2396
|
-
const keys = Object.keys(input).sort(
|
|
2397
|
-
|
|
2501
|
+
const keys = Object.keys(input).sort(alphaComparator);
|
|
2502
|
+
const sortedInput = {};
|
|
2503
|
+
for (const k of keys) sortedInput[k] = input[k];
|
|
2504
|
+
return JSON.stringify(sortedInput);
|
|
2505
|
+
}
|
|
2506
|
+
/**
|
|
2507
|
+
* Destructively merge B into A, with B overwriting A
|
|
2508
|
+
*
|
|
2509
|
+
* This is needed for resolvers because we need a really performant way to merge
|
|
2510
|
+
* token sets. merge-anything is a package we use for merging more complex
|
|
2511
|
+
* configurations like terrazzo.config.ts files, but that’s too slow for tokens.
|
|
2512
|
+
*/
|
|
2513
|
+
function destructiveMerge(a, b) {
|
|
2514
|
+
if (!a || !b || typeof b !== "object") return;
|
|
2515
|
+
for (const k in b) {
|
|
2516
|
+
if (!Object.hasOwn(b, k)) continue;
|
|
2517
|
+
const b2 = b[k];
|
|
2518
|
+
if (b2 != null && typeof b2 === "object") if (Array.isArray(b2)) {
|
|
2519
|
+
a[k] = [];
|
|
2520
|
+
destructiveMerge(a[k], [...b2]);
|
|
2521
|
+
} else {
|
|
2522
|
+
if (!(k in a)) a[k] = {};
|
|
2523
|
+
destructiveMerge(a[k], { ...b2 });
|
|
2524
|
+
}
|
|
2525
|
+
else a[k] = b2;
|
|
2526
|
+
}
|
|
2398
2527
|
}
|
|
2399
2528
|
|
|
2400
2529
|
//#endregion
|
|
@@ -2455,15 +2584,12 @@ function normalize(token, { logger, src }) {
|
|
|
2455
2584
|
switch (token.$type) {
|
|
2456
2585
|
case "color":
|
|
2457
2586
|
for (const mode of Object.keys(token.mode)) token.mode[mode].$value = normalizeColor(token.mode[mode].$value, token.mode[mode].source.node);
|
|
2458
|
-
token.$value = token.mode["."].$value;
|
|
2459
2587
|
break;
|
|
2460
2588
|
case "fontFamily":
|
|
2461
2589
|
for (const mode of Object.keys(token.mode)) token.mode[mode].$value = normalizeFontFamily(token.mode[mode].$value);
|
|
2462
|
-
token.$value = token.mode["."].$value;
|
|
2463
2590
|
break;
|
|
2464
2591
|
case "fontWeight":
|
|
2465
2592
|
for (const mode of Object.keys(token.mode)) token.mode[mode].$value = normalizeFontWeight(token.mode[mode].$value);
|
|
2466
|
-
token.$value = token.mode["."].$value;
|
|
2467
2593
|
break;
|
|
2468
2594
|
case "border":
|
|
2469
2595
|
for (const mode of Object.keys(token.mode)) {
|
|
@@ -2471,7 +2597,6 @@ function normalize(token, { logger, src }) {
|
|
|
2471
2597
|
if (!border || typeof border !== "object") continue;
|
|
2472
2598
|
if (border.color) border.color = normalizeColor(border.color, getObjMember(token.mode[mode].source.node, "color"));
|
|
2473
2599
|
}
|
|
2474
|
-
token.$value = token.mode["."].$value;
|
|
2475
2600
|
break;
|
|
2476
2601
|
case "shadow":
|
|
2477
2602
|
for (const mode of Object.keys(token.mode)) {
|
|
@@ -2485,7 +2610,6 @@ function normalize(token, { logger, src }) {
|
|
|
2485
2610
|
if (!("inset" in shadow)) shadow.inset = false;
|
|
2486
2611
|
}
|
|
2487
2612
|
}
|
|
2488
|
-
token.$value = token.mode["."].$value;
|
|
2489
2613
|
break;
|
|
2490
2614
|
case "gradient":
|
|
2491
2615
|
for (const mode of Object.keys(token.mode)) {
|
|
@@ -2498,7 +2622,6 @@ function normalize(token, { logger, src }) {
|
|
|
2498
2622
|
if (stop.color) stop.color = normalizeColor(stop.color, getObjMember(stopNode, "color"));
|
|
2499
2623
|
}
|
|
2500
2624
|
}
|
|
2501
|
-
token.$value = token.mode["."].$value;
|
|
2502
2625
|
break;
|
|
2503
2626
|
case "typography":
|
|
2504
2627
|
for (const mode of Object.keys(token.mode)) {
|
|
@@ -2513,7 +2636,6 @@ function normalize(token, { logger, src }) {
|
|
|
2513
2636
|
break;
|
|
2514
2637
|
}
|
|
2515
2638
|
}
|
|
2516
|
-
token.$value = token.mode["."].$value;
|
|
2517
2639
|
break;
|
|
2518
2640
|
}
|
|
2519
2641
|
}
|
|
@@ -2532,6 +2654,7 @@ function aliasToTokenRef(alias, mode) {
|
|
|
2532
2654
|
if (id === alias) return;
|
|
2533
2655
|
return { $ref: `#/${id.replace(/~/g, "~0").replace(/\//g, "~1").replace(/\./g, "/")}${mode && mode !== "." ? `/$extensions/mode/${mode}` : ""}/$value` };
|
|
2534
2656
|
}
|
|
2657
|
+
const cachedMatcher = new CachedWildcardMatcher();
|
|
2535
2658
|
/** Generate a TokenNormalized from a Momoa node */
|
|
2536
2659
|
function tokenFromNode(node, { groups, path, source, ignore }) {
|
|
2537
2660
|
if (!(node.type === "Object" && !!getObjMember(node, "$value") && !path.includes("$extensions"))) return;
|
|
@@ -2549,33 +2672,45 @@ function tokenFromNode(node, { groups, path, source, ignore }) {
|
|
|
2549
2672
|
$type: originalToken.$type || group.$type,
|
|
2550
2673
|
$description: originalToken.$description || void 0,
|
|
2551
2674
|
$deprecated: originalToken.$deprecated ?? group.$deprecated ?? void 0,
|
|
2552
|
-
$value
|
|
2675
|
+
get $value() {
|
|
2676
|
+
return this.mode["."].$value;
|
|
2677
|
+
},
|
|
2553
2678
|
$extensions: originalToken.$extensions || void 0,
|
|
2554
2679
|
$extends: originalToken.$extends || void 0,
|
|
2555
|
-
aliasChain
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2680
|
+
get aliasChain() {
|
|
2681
|
+
return this.mode["."].aliasChain;
|
|
2682
|
+
},
|
|
2683
|
+
get aliasedBy() {
|
|
2684
|
+
return this.mode["."].aliasedBy;
|
|
2685
|
+
},
|
|
2686
|
+
get aliasOf() {
|
|
2687
|
+
return this.mode["."].aliasOf;
|
|
2688
|
+
},
|
|
2689
|
+
get partialAliasOf() {
|
|
2690
|
+
return this.mode["."].partialAliasOf;
|
|
2691
|
+
},
|
|
2692
|
+
get dependencies() {
|
|
2693
|
+
return this.mode["."].dependencies;
|
|
2694
|
+
},
|
|
2560
2695
|
group,
|
|
2561
2696
|
originalValue: void 0,
|
|
2562
2697
|
source: nodeSource,
|
|
2563
2698
|
jsonID,
|
|
2564
2699
|
mode: { ".": {
|
|
2565
2700
|
$value: originalToken.$value,
|
|
2566
|
-
aliasOf: void 0,
|
|
2567
2701
|
aliasChain: void 0,
|
|
2568
|
-
partialAliasOf: void 0,
|
|
2569
2702
|
aliasedBy: void 0,
|
|
2570
|
-
|
|
2703
|
+
aliasOf: void 0,
|
|
2704
|
+
partialAliasOf: void 0,
|
|
2571
2705
|
dependencies: void 0,
|
|
2706
|
+
originalValue: void 0,
|
|
2572
2707
|
source: {
|
|
2573
2708
|
...nodeSource,
|
|
2574
2709
|
node: getObjMember(nodeSource.node, "$value") ?? nodeSource.node
|
|
2575
2710
|
}
|
|
2576
2711
|
} }
|
|
2577
2712
|
};
|
|
2578
|
-
if (ignore?.deprecated && token.$deprecated || ignore?.tokens &&
|
|
2713
|
+
if (ignore?.deprecated && token.$deprecated || ignore?.tokens && cachedMatcher.tokenIDMatch(ignore.tokens)(token.id)) return;
|
|
2579
2714
|
const $extensions = getObjMember(node, "$extensions");
|
|
2580
2715
|
if ($extensions) {
|
|
2581
2716
|
const modeNode = getObjMember($extensions, "mode");
|
|
@@ -2684,7 +2819,7 @@ function graphAliases(refMap, { tokens, logger, sources }) {
|
|
|
2684
2819
|
if (!modeValue) continue;
|
|
2685
2820
|
if (!modeValue.dependencies) modeValue.dependencies = [];
|
|
2686
2821
|
modeValue.dependencies.push(...refChain.filter((r) => !modeValue.dependencies.includes(r)));
|
|
2687
|
-
modeValue.dependencies.sort(
|
|
2822
|
+
modeValue.dependencies.sort(alphaComparator);
|
|
2688
2823
|
if (jsonID.endsWith("/$value") || tokens[jsonID]) {
|
|
2689
2824
|
modeValue.aliasOf = refToTokenID(refChain.at(-1));
|
|
2690
2825
|
modeValue.aliasChain = [...refChain.map(refToTokenID)];
|
|
@@ -2728,26 +2863,16 @@ function graphAliases(refMap, { tokens, logger, sources }) {
|
|
|
2728
2863
|
const aliasedByRefs = [jsonID, ...refChain].reverse();
|
|
2729
2864
|
for (let i = 0; i < aliasedByRefs.length; i++) {
|
|
2730
2865
|
const baseRef = getTokenRef(aliasedByRefs[i]);
|
|
2731
|
-
const baseToken = tokens[baseRef]?.mode[mode] || tokens[baseRef];
|
|
2866
|
+
const baseToken = tokens[baseRef]?.mode[mode] || tokens[baseRef]?.mode["."];
|
|
2732
2867
|
if (!baseToken) continue;
|
|
2733
2868
|
const upstream = aliasedByRefs.slice(i + 1);
|
|
2734
2869
|
if (!upstream.length) break;
|
|
2735
2870
|
if (!baseToken.aliasedBy) baseToken.aliasedBy = [];
|
|
2736
2871
|
for (let j = 0; j < upstream.length; j++) {
|
|
2737
2872
|
const downstream = refToTokenID(upstream[j]);
|
|
2738
|
-
if (!baseToken.aliasedBy.includes(downstream))
|
|
2739
|
-
baseToken.aliasedBy.push(downstream);
|
|
2740
|
-
if (mode === ".") tokens[baseRef].aliasedBy = baseToken.aliasedBy;
|
|
2741
|
-
}
|
|
2873
|
+
if (!baseToken.aliasedBy.includes(downstream)) baseToken.aliasedBy.push(downstream);
|
|
2742
2874
|
}
|
|
2743
|
-
baseToken.aliasedBy.sort(
|
|
2744
|
-
}
|
|
2745
|
-
if (mode === ".") {
|
|
2746
|
-
tokens[rootRef].aliasChain = modeValue.aliasChain;
|
|
2747
|
-
tokens[rootRef].aliasedBy = modeValue.aliasedBy;
|
|
2748
|
-
tokens[rootRef].aliasOf = modeValue.aliasOf;
|
|
2749
|
-
tokens[rootRef].dependencies = modeValue.dependencies;
|
|
2750
|
-
tokens[rootRef].partialAliasOf = modeValue.partialAliasOf;
|
|
2875
|
+
baseToken.aliasedBy.sort(alphaComparator);
|
|
2751
2876
|
}
|
|
2752
2877
|
}
|
|
2753
2878
|
}
|
|
@@ -2884,7 +3009,6 @@ function resolveAliases(tokens, { logger, refMap, sources }) {
|
|
|
2884
3009
|
});
|
|
2885
3010
|
if (!token.$type) token.$type = $type;
|
|
2886
3011
|
if ($value) token.mode[mode].$value = $value;
|
|
2887
|
-
if (mode === ".") token.$value = token.mode[mode].$value;
|
|
2888
3012
|
}
|
|
2889
3013
|
}
|
|
2890
3014
|
}
|
|
@@ -3015,7 +3139,7 @@ function processTokens(rootSource, { config, logger, sourceByFilename, isResolve
|
|
|
3015
3139
|
const tokenIDs = [];
|
|
3016
3140
|
const groups = {};
|
|
3017
3141
|
traverse(rootSource.document, { enter(node, _parent, rawPath) {
|
|
3018
|
-
if (node.type !== "Object") return;
|
|
3142
|
+
if (node.type !== "Object" || rawPath.includes("$value") || rawPath.includes("$extensions")) return;
|
|
3019
3143
|
groupFromNode(node, {
|
|
3020
3144
|
path: isResolver ? filterResolverPaths(rawPath) : rawPath,
|
|
3021
3145
|
groups
|
|
@@ -3045,6 +3169,7 @@ function processTokens(rootSource, { config, logger, sourceByFilename, isResolve
|
|
|
3045
3169
|
});
|
|
3046
3170
|
if (tokenRawValues && tokens[tokenRawValues?.jsonID]) {
|
|
3047
3171
|
tokens[tokenRawValues.jsonID].originalValue = tokenRawValues.originalValue;
|
|
3172
|
+
tokens[tokenRawValues.jsonID].mode["."].originalValue = tokenRawValues.originalValue;
|
|
3048
3173
|
tokens[tokenRawValues.jsonID].source = tokenRawValues.source;
|
|
3049
3174
|
for (const mode of Object.keys(tokenRawValues.mode)) {
|
|
3050
3175
|
tokens[tokenRawValues.jsonID].mode[mode].originalValue = tokenRawValues.mode[mode].originalValue;
|
|
@@ -3089,12 +3214,12 @@ function processTokens(rootSource, { config, logger, sourceByFilename, isResolve
|
|
|
3089
3214
|
if (config.alphabetize === false) return tokens;
|
|
3090
3215
|
const sortStart = performance.now();
|
|
3091
3216
|
const tokensSorted = {};
|
|
3092
|
-
tokenIDs.sort(
|
|
3217
|
+
tokenIDs.sort(alphaComparator);
|
|
3093
3218
|
for (const path of tokenIDs) {
|
|
3094
3219
|
const id = refToTokenID(path);
|
|
3095
3220
|
tokensSorted[id] = tokens[path];
|
|
3096
3221
|
}
|
|
3097
|
-
for (const group of Object.values(groups)) group.tokens.sort(
|
|
3222
|
+
for (const group of Object.values(groups)) group.tokens.sort(alphaComparator);
|
|
3098
3223
|
logger.debug({
|
|
3099
3224
|
...entry,
|
|
3100
3225
|
message: "Sorted tokens",
|
|
@@ -3678,7 +3803,7 @@ function createResolver(resolverSource, { config, logger, sources }) {
|
|
|
3678
3803
|
}
|
|
3679
3804
|
return {
|
|
3680
3805
|
apply(inputRaw) {
|
|
3681
|
-
|
|
3806
|
+
const tokensRaw = {};
|
|
3682
3807
|
const input = {
|
|
3683
3808
|
...inputDefaults,
|
|
3684
3809
|
...inputRaw
|
|
@@ -3687,7 +3812,7 @@ function createResolver(resolverSource, { config, logger, sources }) {
|
|
|
3687
3812
|
if (resolverCache[permutationID]) return resolverCache[permutationID];
|
|
3688
3813
|
for (const item of resolverSource.resolutionOrder) switch (item.type) {
|
|
3689
3814
|
case "set":
|
|
3690
|
-
for (const s of item.sources)
|
|
3815
|
+
for (const s of item.sources) destructiveMerge(tokensRaw, s);
|
|
3691
3816
|
break;
|
|
3692
3817
|
case "modifier": {
|
|
3693
3818
|
const context = input[item.name];
|
|
@@ -3696,7 +3821,7 @@ function createResolver(resolverSource, { config, logger, sources }) {
|
|
|
3696
3821
|
group: "resolver",
|
|
3697
3822
|
message: `Modifier ${item.name} has no context ${JSON.stringify(context)}.`
|
|
3698
3823
|
});
|
|
3699
|
-
for (const s of sources ?? [])
|
|
3824
|
+
for (const s of sources ?? []) destructiveMerge(tokensRaw, s);
|
|
3700
3825
|
break;
|
|
3701
3826
|
}
|
|
3702
3827
|
}
|
|
@@ -3734,6 +3859,7 @@ function createResolver(resolverSource, { config, logger, sources }) {
|
|
|
3734
3859
|
return false;
|
|
3735
3860
|
}
|
|
3736
3861
|
for (const [name, contexts] of Object.entries(validContexts)) if (name in input) {
|
|
3862
|
+
if (name === "tzMode") continue;
|
|
3737
3863
|
if (!contexts.includes(input[name])) {
|
|
3738
3864
|
if (throwError) logger.error({
|
|
3739
3865
|
group: "resolver",
|
|
@@ -3783,7 +3909,6 @@ function calculatePermutations(options) {
|
|
|
3783
3909
|
async function createSyntheticResolver(tokens, { config, logger, req, sources }) {
|
|
3784
3910
|
const contexts = {};
|
|
3785
3911
|
for (const token of Object.values(tokens)) for (const [mode, value] of Object.entries(token.mode)) {
|
|
3786
|
-
if (mode === ".") continue;
|
|
3787
3912
|
if (!(mode in contexts)) contexts[mode] = [{}];
|
|
3788
3913
|
addToken(contexts[mode][0], {
|
|
3789
3914
|
...token,
|
|
@@ -3797,7 +3922,8 @@ async function createSyntheticResolver(tokens, { config, logger, req, sources })
|
|
|
3797
3922
|
sets: { allTokens: { sources: [simpleFlatten(tokens, { logger })] } },
|
|
3798
3923
|
modifiers: { tzMode: {
|
|
3799
3924
|
description: "Automatically built from $extensions.mode",
|
|
3800
|
-
contexts
|
|
3925
|
+
contexts,
|
|
3926
|
+
default: "."
|
|
3801
3927
|
} }
|
|
3802
3928
|
}, void 0, 2);
|
|
3803
3929
|
return createResolver(await normalizeResolver(momoa.parse(src), {
|
|
@@ -3954,6 +4080,16 @@ async function parse(_input, { logger = new Logger(), req = defaultReq, skipLint
|
|
|
3954
4080
|
let tokens = {};
|
|
3955
4081
|
let resolver;
|
|
3956
4082
|
let sources = [];
|
|
4083
|
+
if (inputs.length === 0) logger.error({
|
|
4084
|
+
group: "parser",
|
|
4085
|
+
label: "init",
|
|
4086
|
+
message: "Nothing to parse."
|
|
4087
|
+
});
|
|
4088
|
+
for (let i = 0; i < inputs.length; i++) if (!inputs[i] || typeof inputs[i] !== "object" || !inputs[i]?.src || inputs[i]?.filename && !(inputs[i].filename instanceof URL)) logger.error({
|
|
4089
|
+
group: "parser",
|
|
4090
|
+
label: "init",
|
|
4091
|
+
message: `Input ${i}: expected { src: any; filename: URL }`
|
|
4092
|
+
});
|
|
3957
4093
|
const totalStart = performance.now();
|
|
3958
4094
|
const initStart = performance.now();
|
|
3959
4095
|
const resolverResult = await loadResolver(inputs, {
|