@terrazzo/parser 2.0.0-beta.4 → 2.0.0-beta.5
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 +247 -114
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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,95 @@ 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] = {};
|
|
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) formats[params.format][permutationID].push({
|
|
316
|
+
...params,
|
|
317
|
+
id,
|
|
318
|
+
value: cleanValue,
|
|
319
|
+
type: typeof cleanValue === "string" ? SINGLE_VALUE : MULTI_VALUE,
|
|
320
|
+
mode: mode || ".",
|
|
321
|
+
token: makeReadOnlyToken(token),
|
|
322
|
+
permutationID,
|
|
323
|
+
input: JSON.parse(permutationID)
|
|
324
|
+
});
|
|
325
|
+
else {
|
|
326
|
+
formats[params.format][permutationID][foundTokenI].value = cleanValue;
|
|
327
|
+
formats[params.format][permutationID][foundTokenI].type = typeof cleanValue === "string" ? SINGLE_VALUE : MULTI_VALUE;
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
resolver
|
|
331
|
+
});
|
|
332
|
+
logger.debug({
|
|
333
|
+
group: "plugin",
|
|
334
|
+
label: plugin.name,
|
|
335
|
+
message: "transform()",
|
|
336
|
+
timing: performance.now() - pt
|
|
337
|
+
});
|
|
338
|
+
}
|
|
327
339
|
transformsLocked = true;
|
|
328
340
|
logger.debug({
|
|
329
341
|
group: "parser",
|
|
330
342
|
label: "transform",
|
|
331
|
-
message: "transform()
|
|
343
|
+
message: "All plugins finished transform()",
|
|
332
344
|
timing: performance.now() - startTransform
|
|
333
345
|
});
|
|
334
346
|
const startBuild = performance.now();
|
|
335
347
|
await Promise.all(config.plugins.map(async (plugin) => {
|
|
336
348
|
if (typeof plugin.build === "function") {
|
|
337
|
-
const
|
|
349
|
+
const pb = performance.now();
|
|
338
350
|
await plugin.build({
|
|
339
351
|
context: { logger },
|
|
340
352
|
tokens,
|
|
@@ -352,18 +364,25 @@ async function build(tokens, { resolver, sources, logger = new Logger(), config
|
|
|
352
364
|
filename,
|
|
353
365
|
contents,
|
|
354
366
|
plugin: plugin.name,
|
|
355
|
-
time: performance.now() -
|
|
367
|
+
time: performance.now() - pb
|
|
356
368
|
});
|
|
357
369
|
}
|
|
358
370
|
});
|
|
371
|
+
logger.debug({
|
|
372
|
+
group: "plugin",
|
|
373
|
+
label: plugin.name,
|
|
374
|
+
message: "build()",
|
|
375
|
+
timing: performance.now() - pb
|
|
376
|
+
});
|
|
359
377
|
}
|
|
360
378
|
}));
|
|
361
379
|
logger.debug({
|
|
362
380
|
group: "parser",
|
|
363
381
|
label: "build",
|
|
364
|
-
message: "build()
|
|
382
|
+
message: "All plugins finished build()",
|
|
365
383
|
timing: performance.now() - startBuild
|
|
366
384
|
});
|
|
385
|
+
cachedMatcher$1.reset();
|
|
367
386
|
const startBuildEnd = performance.now();
|
|
368
387
|
await Promise.all(config.plugins.map(async (plugin) => plugin.buildEnd?.({
|
|
369
388
|
context: { logger },
|
|
@@ -380,6 +399,65 @@ async function build(tokens, { resolver, sources, logger = new Logger(), config
|
|
|
380
399
|
});
|
|
381
400
|
return result;
|
|
382
401
|
}
|
|
402
|
+
function isFullWildcard(value) {
|
|
403
|
+
return typeof value === "string" && (value === "*" || value === "**") || Array.isArray(value) && value.some((v) => v === "*" || v === "**");
|
|
404
|
+
}
|
|
405
|
+
/** Generate getters for transformed tokens. Reduces memory usage while improving accuracy. Provides some safety for read-only values. */
|
|
406
|
+
function makeReadOnlyToken(token) {
|
|
407
|
+
return {
|
|
408
|
+
get id() {
|
|
409
|
+
return token.id;
|
|
410
|
+
},
|
|
411
|
+
get $value() {
|
|
412
|
+
return token.$value;
|
|
413
|
+
},
|
|
414
|
+
get $type() {
|
|
415
|
+
return token.$type;
|
|
416
|
+
},
|
|
417
|
+
get $description() {
|
|
418
|
+
return token.$description;
|
|
419
|
+
},
|
|
420
|
+
get $deprecated() {
|
|
421
|
+
return token.$deprecated;
|
|
422
|
+
},
|
|
423
|
+
get $extends() {
|
|
424
|
+
return token.$extends;
|
|
425
|
+
},
|
|
426
|
+
get $extensions() {
|
|
427
|
+
return token.$extensions;
|
|
428
|
+
},
|
|
429
|
+
get mode() {
|
|
430
|
+
return token.mode;
|
|
431
|
+
},
|
|
432
|
+
get originalValue() {
|
|
433
|
+
return token.originalValue;
|
|
434
|
+
},
|
|
435
|
+
get aliasChain() {
|
|
436
|
+
return token.aliasChain;
|
|
437
|
+
},
|
|
438
|
+
get aliasOf() {
|
|
439
|
+
return token.aliasOf;
|
|
440
|
+
},
|
|
441
|
+
get partialAliasOf() {
|
|
442
|
+
return token.partialAliasOf;
|
|
443
|
+
},
|
|
444
|
+
get aliasedBy() {
|
|
445
|
+
return token.aliasedBy;
|
|
446
|
+
},
|
|
447
|
+
get group() {
|
|
448
|
+
return token.group;
|
|
449
|
+
},
|
|
450
|
+
get source() {
|
|
451
|
+
return token.source;
|
|
452
|
+
},
|
|
453
|
+
get jsonID() {
|
|
454
|
+
return token.jsonID;
|
|
455
|
+
},
|
|
456
|
+
get dependencies() {
|
|
457
|
+
return token.dependencies;
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
}
|
|
383
461
|
|
|
384
462
|
//#endregion
|
|
385
463
|
//#region src/lint/plugin-core/lib/docs.ts
|
|
@@ -435,6 +513,22 @@ const rule$26 = {
|
|
|
435
513
|
}
|
|
436
514
|
};
|
|
437
515
|
|
|
516
|
+
//#endregion
|
|
517
|
+
//#region src/lint/plugin-core/lib/matchers.ts
|
|
518
|
+
/**
|
|
519
|
+
* Share one cached matcher factory for all lint plugins.
|
|
520
|
+
*
|
|
521
|
+
* Creating matchers is CPU-intensive, however, if we made one matcher for very
|
|
522
|
+
* getTransform plugin query, we could end up with tens of thousands of
|
|
523
|
+
* matchers, all taking up space in memory, but without providing any caching
|
|
524
|
+
* benefits if a matcher is used only once. So a reasonable balance is we
|
|
525
|
+
* maintain one cache per task category, and we garbage-collect everything after
|
|
526
|
+
* it’s done. Lint tasks are likely to have frequently-occurring patterns. So
|
|
527
|
+
* we’d expect for most use cases a shared lint cache has benefits, but only
|
|
528
|
+
* so long as this doesn’t spread to other plugins and other task categories.
|
|
529
|
+
*/
|
|
530
|
+
const cachedLintMatcher = new CachedWildcardMatcher();
|
|
531
|
+
|
|
438
532
|
//#endregion
|
|
439
533
|
//#region src/lint/plugin-core/rules/a11y-min-font-size.ts
|
|
440
534
|
const A11Y_MIN_FONT_SIZE = "a11y/min-font-size";
|
|
@@ -450,7 +544,7 @@ const rule$25 = {
|
|
|
450
544
|
defaultOptions: {},
|
|
451
545
|
create({ tokens, options, report }) {
|
|
452
546
|
if (!options.minSizePx && !options.minSizeRem) throw new Error("Must specify at least one of minSizePx or minSizeRem");
|
|
453
|
-
const shouldIgnore = options.ignore ?
|
|
547
|
+
const shouldIgnore = options.ignore ? cachedLintMatcher.tokenIDMatch(options.ignore) : null;
|
|
454
548
|
for (const t of Object.values(tokens)) {
|
|
455
549
|
if (shouldIgnore?.(t.id)) continue;
|
|
456
550
|
if (t.aliasOf) continue;
|
|
@@ -491,7 +585,7 @@ const rule$24 = {
|
|
|
491
585
|
defaultOptions: { colorSpace: "srgb" },
|
|
492
586
|
create({ tokens, options, report }) {
|
|
493
587
|
if (!options.colorSpace) return;
|
|
494
|
-
const shouldIgnore = options.ignore ?
|
|
588
|
+
const shouldIgnore = options.ignore ? cachedLintMatcher.tokenIDMatch(options.ignore) : null;
|
|
495
589
|
for (const t of Object.values(tokens)) {
|
|
496
590
|
if (shouldIgnore?.(t.id)) continue;
|
|
497
591
|
if (t.aliasOf) continue;
|
|
@@ -602,7 +696,7 @@ const rule$22 = {
|
|
|
602
696
|
},
|
|
603
697
|
defaultOptions: {},
|
|
604
698
|
create({ tokens, options, report }) {
|
|
605
|
-
const shouldIgnore = options.ignore ?
|
|
699
|
+
const shouldIgnore = options.ignore ? cachedLintMatcher.tokenIDMatch(options.ignore) : null;
|
|
606
700
|
for (const t of Object.values(tokens)) {
|
|
607
701
|
if (shouldIgnore?.(t.id)) continue;
|
|
608
702
|
if (!t.$description) report({
|
|
@@ -629,7 +723,7 @@ const rule$21 = {
|
|
|
629
723
|
defaultOptions: {},
|
|
630
724
|
create({ report, tokens, options }) {
|
|
631
725
|
const values = {};
|
|
632
|
-
const shouldIgnore = options.ignore ?
|
|
726
|
+
const shouldIgnore = options.ignore ? cachedLintMatcher.tokenIDMatch(options.ignore) : null;
|
|
633
727
|
for (const t of Object.values(tokens)) {
|
|
634
728
|
if (shouldIgnore?.(t.id)) continue;
|
|
635
729
|
if (!values[t.$type]) values[t.$type] = /* @__PURE__ */ new Set();
|
|
@@ -682,7 +776,7 @@ const rule$20 = {
|
|
|
682
776
|
create({ tokens, options, report }) {
|
|
683
777
|
if (!options?.gamut) return;
|
|
684
778
|
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 ?
|
|
779
|
+
const shouldIgnore = options.ignore ? cachedLintMatcher.tokenIDMatch(options.ignore) : null;
|
|
686
780
|
for (const t of Object.values(tokens)) {
|
|
687
781
|
if (shouldIgnore?.(t.id)) continue;
|
|
688
782
|
if (t.aliasOf) continue;
|
|
@@ -761,7 +855,7 @@ const rule$19 = {
|
|
|
761
855
|
const { match, requiredTokens, requiredGroups } = options.matches[matchI];
|
|
762
856
|
if (!match.length) throw new Error(`Match ${matchI}: must declare \`match: […]\``);
|
|
763
857
|
if (!requiredTokens?.length && !requiredGroups?.length) throw new Error(`Match ${matchI}: must declare either \`requiredTokens: […]\` or \`requiredGroups: […]\``);
|
|
764
|
-
const matcher =
|
|
858
|
+
const matcher = cachedLintMatcher.tokenIDMatch(match);
|
|
765
859
|
const matchGroups = [];
|
|
766
860
|
const matchTokens = [];
|
|
767
861
|
let tokensMatched = false;
|
|
@@ -816,7 +910,7 @@ const rule$18 = {
|
|
|
816
910
|
const { match, modes } = options.matches[matchI];
|
|
817
911
|
if (!match.length) throw new Error(`Match ${matchI}: must declare \`match: […]\``);
|
|
818
912
|
if (!modes?.length) throw new Error(`Match ${matchI}: must declare \`modes: […]\``);
|
|
819
|
-
const matcher =
|
|
913
|
+
const matcher = cachedLintMatcher.tokenIDMatch(match);
|
|
820
914
|
let tokensMatched = false;
|
|
821
915
|
for (const t of Object.values(tokens)) {
|
|
822
916
|
if (!matcher(t.id)) continue;
|
|
@@ -877,7 +971,7 @@ const rule$16 = {
|
|
|
877
971
|
create({ tokens, options, report }) {
|
|
878
972
|
if (!options) return;
|
|
879
973
|
if (!options.properties.length) throw new Error(`"properties" can’t be empty`);
|
|
880
|
-
const shouldIgnore = options.ignore ?
|
|
974
|
+
const shouldIgnore = options.ignore ? cachedLintMatcher.tokenIDMatch(options.ignore) : null;
|
|
881
975
|
for (const t of Object.values(tokens)) {
|
|
882
976
|
if (shouldIgnore?.(t.id)) continue;
|
|
883
977
|
if (t.$type !== "typography") continue;
|
|
@@ -1388,7 +1482,7 @@ const rule$6 = {
|
|
|
1388
1482
|
"lineHeight"
|
|
1389
1483
|
] },
|
|
1390
1484
|
create({ tokens, options, report }) {
|
|
1391
|
-
const isIgnored = options.ignore ?
|
|
1485
|
+
const isIgnored = options.ignore ? cachedLintMatcher.tokenIDMatch(options.ignore) : () => false;
|
|
1392
1486
|
for (const t of Object.values(tokens)) {
|
|
1393
1487
|
if (t.aliasOf || !t.originalValue || t.$type !== "typography" || isIgnored(t.id)) continue;
|
|
1394
1488
|
validateTypography(t.originalValue.$value, {
|
|
@@ -2341,6 +2435,7 @@ async function lintRunner({ tokens, filename, config = {}, sources, logger }) {
|
|
|
2341
2435
|
message: "Finished",
|
|
2342
2436
|
timing: performance.now() - s
|
|
2343
2437
|
});
|
|
2438
|
+
cachedLintMatcher.reset();
|
|
2344
2439
|
}
|
|
2345
2440
|
const errCount = errors.length ? `${errors.length} ${pluralize(errors.length, "error", "errors")}` : "";
|
|
2346
2441
|
const warnCount = warnings.length ? `${warnings.length} ${pluralize(warnings.length, "warning", "warnings")}` : "";
|
|
@@ -2372,6 +2467,13 @@ function toMomoa(srcRaw) {
|
|
|
2372
2467
|
});
|
|
2373
2468
|
}
|
|
2374
2469
|
|
|
2470
|
+
//#endregion
|
|
2471
|
+
//#region src/lib/array.ts
|
|
2472
|
+
/** JS compiler-optimizable comparator */
|
|
2473
|
+
function alphaComparator(a, b) {
|
|
2474
|
+
return a.localeCompare(b, "en-us", { numeric: true });
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2375
2477
|
//#endregion
|
|
2376
2478
|
//#region src/lib/resolver-utils.ts
|
|
2377
2479
|
/**
|
|
@@ -2393,8 +2495,32 @@ function filterResolverPaths(path) {
|
|
|
2393
2495
|
}
|
|
2394
2496
|
/** Make a deterministic string from an object */
|
|
2395
2497
|
function getPermutationID(input) {
|
|
2396
|
-
const keys = Object.keys(input).sort(
|
|
2397
|
-
|
|
2498
|
+
const keys = Object.keys(input).sort(alphaComparator);
|
|
2499
|
+
const sortedInput = {};
|
|
2500
|
+
for (const k of keys) sortedInput[k] = input[k];
|
|
2501
|
+
return JSON.stringify(sortedInput);
|
|
2502
|
+
}
|
|
2503
|
+
/**
|
|
2504
|
+
* Destructively merge B into A, with B overwriting A
|
|
2505
|
+
*
|
|
2506
|
+
* This is needed for resolvers because we need a really performant way to merge
|
|
2507
|
+
* token sets. merge-anything is a package we use for merging more complex
|
|
2508
|
+
* configurations like terrazzo.config.ts files, but that’s too slow for tokens.
|
|
2509
|
+
*/
|
|
2510
|
+
function destructiveMerge(a, b) {
|
|
2511
|
+
if (!a || !b || typeof b !== "object") return;
|
|
2512
|
+
for (const k in b) {
|
|
2513
|
+
if (!Object.hasOwn(b, k)) continue;
|
|
2514
|
+
const b2 = b[k];
|
|
2515
|
+
if (b2 != null && typeof b2 === "object") if (Array.isArray(b2)) {
|
|
2516
|
+
a[k] = [];
|
|
2517
|
+
destructiveMerge(a[k], [...b2]);
|
|
2518
|
+
} else {
|
|
2519
|
+
if (!(k in a)) a[k] = {};
|
|
2520
|
+
destructiveMerge(a[k], { ...b2 });
|
|
2521
|
+
}
|
|
2522
|
+
else a[k] = b2;
|
|
2523
|
+
}
|
|
2398
2524
|
}
|
|
2399
2525
|
|
|
2400
2526
|
//#endregion
|
|
@@ -2455,15 +2581,12 @@ function normalize(token, { logger, src }) {
|
|
|
2455
2581
|
switch (token.$type) {
|
|
2456
2582
|
case "color":
|
|
2457
2583
|
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
2584
|
break;
|
|
2460
2585
|
case "fontFamily":
|
|
2461
2586
|
for (const mode of Object.keys(token.mode)) token.mode[mode].$value = normalizeFontFamily(token.mode[mode].$value);
|
|
2462
|
-
token.$value = token.mode["."].$value;
|
|
2463
2587
|
break;
|
|
2464
2588
|
case "fontWeight":
|
|
2465
2589
|
for (const mode of Object.keys(token.mode)) token.mode[mode].$value = normalizeFontWeight(token.mode[mode].$value);
|
|
2466
|
-
token.$value = token.mode["."].$value;
|
|
2467
2590
|
break;
|
|
2468
2591
|
case "border":
|
|
2469
2592
|
for (const mode of Object.keys(token.mode)) {
|
|
@@ -2471,7 +2594,6 @@ function normalize(token, { logger, src }) {
|
|
|
2471
2594
|
if (!border || typeof border !== "object") continue;
|
|
2472
2595
|
if (border.color) border.color = normalizeColor(border.color, getObjMember(token.mode[mode].source.node, "color"));
|
|
2473
2596
|
}
|
|
2474
|
-
token.$value = token.mode["."].$value;
|
|
2475
2597
|
break;
|
|
2476
2598
|
case "shadow":
|
|
2477
2599
|
for (const mode of Object.keys(token.mode)) {
|
|
@@ -2485,7 +2607,6 @@ function normalize(token, { logger, src }) {
|
|
|
2485
2607
|
if (!("inset" in shadow)) shadow.inset = false;
|
|
2486
2608
|
}
|
|
2487
2609
|
}
|
|
2488
|
-
token.$value = token.mode["."].$value;
|
|
2489
2610
|
break;
|
|
2490
2611
|
case "gradient":
|
|
2491
2612
|
for (const mode of Object.keys(token.mode)) {
|
|
@@ -2498,7 +2619,6 @@ function normalize(token, { logger, src }) {
|
|
|
2498
2619
|
if (stop.color) stop.color = normalizeColor(stop.color, getObjMember(stopNode, "color"));
|
|
2499
2620
|
}
|
|
2500
2621
|
}
|
|
2501
|
-
token.$value = token.mode["."].$value;
|
|
2502
2622
|
break;
|
|
2503
2623
|
case "typography":
|
|
2504
2624
|
for (const mode of Object.keys(token.mode)) {
|
|
@@ -2513,7 +2633,6 @@ function normalize(token, { logger, src }) {
|
|
|
2513
2633
|
break;
|
|
2514
2634
|
}
|
|
2515
2635
|
}
|
|
2516
|
-
token.$value = token.mode["."].$value;
|
|
2517
2636
|
break;
|
|
2518
2637
|
}
|
|
2519
2638
|
}
|
|
@@ -2532,6 +2651,7 @@ function aliasToTokenRef(alias, mode) {
|
|
|
2532
2651
|
if (id === alias) return;
|
|
2533
2652
|
return { $ref: `#/${id.replace(/~/g, "~0").replace(/\//g, "~1").replace(/\./g, "/")}${mode && mode !== "." ? `/$extensions/mode/${mode}` : ""}/$value` };
|
|
2534
2653
|
}
|
|
2654
|
+
const cachedMatcher = new CachedWildcardMatcher();
|
|
2535
2655
|
/** Generate a TokenNormalized from a Momoa node */
|
|
2536
2656
|
function tokenFromNode(node, { groups, path, source, ignore }) {
|
|
2537
2657
|
if (!(node.type === "Object" && !!getObjMember(node, "$value") && !path.includes("$extensions"))) return;
|
|
@@ -2549,33 +2669,45 @@ function tokenFromNode(node, { groups, path, source, ignore }) {
|
|
|
2549
2669
|
$type: originalToken.$type || group.$type,
|
|
2550
2670
|
$description: originalToken.$description || void 0,
|
|
2551
2671
|
$deprecated: originalToken.$deprecated ?? group.$deprecated ?? void 0,
|
|
2552
|
-
$value
|
|
2672
|
+
get $value() {
|
|
2673
|
+
return this.mode["."].$value;
|
|
2674
|
+
},
|
|
2553
2675
|
$extensions: originalToken.$extensions || void 0,
|
|
2554
2676
|
$extends: originalToken.$extends || void 0,
|
|
2555
|
-
aliasChain
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2677
|
+
get aliasChain() {
|
|
2678
|
+
return this.mode["."].aliasChain;
|
|
2679
|
+
},
|
|
2680
|
+
get aliasedBy() {
|
|
2681
|
+
return this.mode["."].aliasedBy;
|
|
2682
|
+
},
|
|
2683
|
+
get aliasOf() {
|
|
2684
|
+
return this.mode["."].aliasOf;
|
|
2685
|
+
},
|
|
2686
|
+
get partialAliasOf() {
|
|
2687
|
+
return this.mode["."].partialAliasOf;
|
|
2688
|
+
},
|
|
2689
|
+
get dependencies() {
|
|
2690
|
+
return this.mode["."].dependencies;
|
|
2691
|
+
},
|
|
2560
2692
|
group,
|
|
2561
2693
|
originalValue: void 0,
|
|
2562
2694
|
source: nodeSource,
|
|
2563
2695
|
jsonID,
|
|
2564
2696
|
mode: { ".": {
|
|
2565
2697
|
$value: originalToken.$value,
|
|
2566
|
-
aliasOf: void 0,
|
|
2567
2698
|
aliasChain: void 0,
|
|
2568
|
-
partialAliasOf: void 0,
|
|
2569
2699
|
aliasedBy: void 0,
|
|
2570
|
-
|
|
2700
|
+
aliasOf: void 0,
|
|
2701
|
+
partialAliasOf: void 0,
|
|
2571
2702
|
dependencies: void 0,
|
|
2703
|
+
originalValue: void 0,
|
|
2572
2704
|
source: {
|
|
2573
2705
|
...nodeSource,
|
|
2574
2706
|
node: getObjMember(nodeSource.node, "$value") ?? nodeSource.node
|
|
2575
2707
|
}
|
|
2576
2708
|
} }
|
|
2577
2709
|
};
|
|
2578
|
-
if (ignore?.deprecated && token.$deprecated || ignore?.tokens &&
|
|
2710
|
+
if (ignore?.deprecated && token.$deprecated || ignore?.tokens && cachedMatcher.tokenIDMatch(ignore.tokens)(token.id)) return;
|
|
2579
2711
|
const $extensions = getObjMember(node, "$extensions");
|
|
2580
2712
|
if ($extensions) {
|
|
2581
2713
|
const modeNode = getObjMember($extensions, "mode");
|
|
@@ -2684,7 +2816,7 @@ function graphAliases(refMap, { tokens, logger, sources }) {
|
|
|
2684
2816
|
if (!modeValue) continue;
|
|
2685
2817
|
if (!modeValue.dependencies) modeValue.dependencies = [];
|
|
2686
2818
|
modeValue.dependencies.push(...refChain.filter((r) => !modeValue.dependencies.includes(r)));
|
|
2687
|
-
modeValue.dependencies.sort(
|
|
2819
|
+
modeValue.dependencies.sort(alphaComparator);
|
|
2688
2820
|
if (jsonID.endsWith("/$value") || tokens[jsonID]) {
|
|
2689
2821
|
modeValue.aliasOf = refToTokenID(refChain.at(-1));
|
|
2690
2822
|
modeValue.aliasChain = [...refChain.map(refToTokenID)];
|
|
@@ -2728,26 +2860,16 @@ function graphAliases(refMap, { tokens, logger, sources }) {
|
|
|
2728
2860
|
const aliasedByRefs = [jsonID, ...refChain].reverse();
|
|
2729
2861
|
for (let i = 0; i < aliasedByRefs.length; i++) {
|
|
2730
2862
|
const baseRef = getTokenRef(aliasedByRefs[i]);
|
|
2731
|
-
const baseToken = tokens[baseRef]?.mode[mode] || tokens[baseRef];
|
|
2863
|
+
const baseToken = tokens[baseRef]?.mode[mode] || tokens[baseRef]?.mode["."];
|
|
2732
2864
|
if (!baseToken) continue;
|
|
2733
2865
|
const upstream = aliasedByRefs.slice(i + 1);
|
|
2734
2866
|
if (!upstream.length) break;
|
|
2735
2867
|
if (!baseToken.aliasedBy) baseToken.aliasedBy = [];
|
|
2736
2868
|
for (let j = 0; j < upstream.length; j++) {
|
|
2737
2869
|
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
|
-
}
|
|
2870
|
+
if (!baseToken.aliasedBy.includes(downstream)) baseToken.aliasedBy.push(downstream);
|
|
2742
2871
|
}
|
|
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;
|
|
2872
|
+
baseToken.aliasedBy.sort(alphaComparator);
|
|
2751
2873
|
}
|
|
2752
2874
|
}
|
|
2753
2875
|
}
|
|
@@ -2884,7 +3006,6 @@ function resolveAliases(tokens, { logger, refMap, sources }) {
|
|
|
2884
3006
|
});
|
|
2885
3007
|
if (!token.$type) token.$type = $type;
|
|
2886
3008
|
if ($value) token.mode[mode].$value = $value;
|
|
2887
|
-
if (mode === ".") token.$value = token.mode[mode].$value;
|
|
2888
3009
|
}
|
|
2889
3010
|
}
|
|
2890
3011
|
}
|
|
@@ -3045,6 +3166,7 @@ function processTokens(rootSource, { config, logger, sourceByFilename, isResolve
|
|
|
3045
3166
|
});
|
|
3046
3167
|
if (tokenRawValues && tokens[tokenRawValues?.jsonID]) {
|
|
3047
3168
|
tokens[tokenRawValues.jsonID].originalValue = tokenRawValues.originalValue;
|
|
3169
|
+
tokens[tokenRawValues.jsonID].mode["."].originalValue = tokenRawValues.originalValue;
|
|
3048
3170
|
tokens[tokenRawValues.jsonID].source = tokenRawValues.source;
|
|
3049
3171
|
for (const mode of Object.keys(tokenRawValues.mode)) {
|
|
3050
3172
|
tokens[tokenRawValues.jsonID].mode[mode].originalValue = tokenRawValues.mode[mode].originalValue;
|
|
@@ -3089,12 +3211,12 @@ function processTokens(rootSource, { config, logger, sourceByFilename, isResolve
|
|
|
3089
3211
|
if (config.alphabetize === false) return tokens;
|
|
3090
3212
|
const sortStart = performance.now();
|
|
3091
3213
|
const tokensSorted = {};
|
|
3092
|
-
tokenIDs.sort(
|
|
3214
|
+
tokenIDs.sort(alphaComparator);
|
|
3093
3215
|
for (const path of tokenIDs) {
|
|
3094
3216
|
const id = refToTokenID(path);
|
|
3095
3217
|
tokensSorted[id] = tokens[path];
|
|
3096
3218
|
}
|
|
3097
|
-
for (const group of Object.values(groups)) group.tokens.sort(
|
|
3219
|
+
for (const group of Object.values(groups)) group.tokens.sort(alphaComparator);
|
|
3098
3220
|
logger.debug({
|
|
3099
3221
|
...entry,
|
|
3100
3222
|
message: "Sorted tokens",
|
|
@@ -3678,7 +3800,7 @@ function createResolver(resolverSource, { config, logger, sources }) {
|
|
|
3678
3800
|
}
|
|
3679
3801
|
return {
|
|
3680
3802
|
apply(inputRaw) {
|
|
3681
|
-
|
|
3803
|
+
const tokensRaw = {};
|
|
3682
3804
|
const input = {
|
|
3683
3805
|
...inputDefaults,
|
|
3684
3806
|
...inputRaw
|
|
@@ -3687,7 +3809,7 @@ function createResolver(resolverSource, { config, logger, sources }) {
|
|
|
3687
3809
|
if (resolverCache[permutationID]) return resolverCache[permutationID];
|
|
3688
3810
|
for (const item of resolverSource.resolutionOrder) switch (item.type) {
|
|
3689
3811
|
case "set":
|
|
3690
|
-
for (const s of item.sources)
|
|
3812
|
+
for (const s of item.sources) destructiveMerge(tokensRaw, s);
|
|
3691
3813
|
break;
|
|
3692
3814
|
case "modifier": {
|
|
3693
3815
|
const context = input[item.name];
|
|
@@ -3696,7 +3818,7 @@ function createResolver(resolverSource, { config, logger, sources }) {
|
|
|
3696
3818
|
group: "resolver",
|
|
3697
3819
|
message: `Modifier ${item.name} has no context ${JSON.stringify(context)}.`
|
|
3698
3820
|
});
|
|
3699
|
-
for (const s of sources ?? [])
|
|
3821
|
+
for (const s of sources ?? []) destructiveMerge(tokensRaw, s);
|
|
3700
3822
|
break;
|
|
3701
3823
|
}
|
|
3702
3824
|
}
|
|
@@ -3734,6 +3856,7 @@ function createResolver(resolverSource, { config, logger, sources }) {
|
|
|
3734
3856
|
return false;
|
|
3735
3857
|
}
|
|
3736
3858
|
for (const [name, contexts] of Object.entries(validContexts)) if (name in input) {
|
|
3859
|
+
if (name === "tzMode") continue;
|
|
3737
3860
|
if (!contexts.includes(input[name])) {
|
|
3738
3861
|
if (throwError) logger.error({
|
|
3739
3862
|
group: "resolver",
|
|
@@ -3783,7 +3906,6 @@ function calculatePermutations(options) {
|
|
|
3783
3906
|
async function createSyntheticResolver(tokens, { config, logger, req, sources }) {
|
|
3784
3907
|
const contexts = {};
|
|
3785
3908
|
for (const token of Object.values(tokens)) for (const [mode, value] of Object.entries(token.mode)) {
|
|
3786
|
-
if (mode === ".") continue;
|
|
3787
3909
|
if (!(mode in contexts)) contexts[mode] = [{}];
|
|
3788
3910
|
addToken(contexts[mode][0], {
|
|
3789
3911
|
...token,
|
|
@@ -3797,7 +3919,8 @@ async function createSyntheticResolver(tokens, { config, logger, req, sources })
|
|
|
3797
3919
|
sets: { allTokens: { sources: [simpleFlatten(tokens, { logger })] } },
|
|
3798
3920
|
modifiers: { tzMode: {
|
|
3799
3921
|
description: "Automatically built from $extensions.mode",
|
|
3800
|
-
contexts
|
|
3922
|
+
contexts,
|
|
3923
|
+
default: "."
|
|
3801
3924
|
} }
|
|
3802
3925
|
}, void 0, 2);
|
|
3803
3926
|
return createResolver(await normalizeResolver(momoa.parse(src), {
|
|
@@ -3954,6 +4077,16 @@ async function parse(_input, { logger = new Logger(), req = defaultReq, skipLint
|
|
|
3954
4077
|
let tokens = {};
|
|
3955
4078
|
let resolver;
|
|
3956
4079
|
let sources = [];
|
|
4080
|
+
if (inputs.length === 0) logger.error({
|
|
4081
|
+
group: "parser",
|
|
4082
|
+
label: "init",
|
|
4083
|
+
message: "Nothing to parse."
|
|
4084
|
+
});
|
|
4085
|
+
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({
|
|
4086
|
+
group: "parser",
|
|
4087
|
+
label: "init",
|
|
4088
|
+
message: `Input ${i}: expected { src: any; filename: URL }`
|
|
4089
|
+
});
|
|
3957
4090
|
const totalStart = performance.now();
|
|
3958
4091
|
const initStart = performance.now();
|
|
3959
4092
|
const resolverResult = await loadResolver(inputs, {
|