@terrazzo/parser 2.0.0-beta.0 → 2.0.0-beta.2

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.js CHANGED
@@ -2,8 +2,8 @@ import wcmatch from "wildcard-match";
2
2
  import * as momoa from "@humanwhocodes/momoa";
3
3
  import pc from "picocolors";
4
4
  import { merge } from "merge-anything";
5
- import { BORDER_REQUIRED_PROPERTIES, COLORSPACE, 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, tokenToCulori } from "@terrazzo/token-tools";
6
- import { clampChroma, wcagContrast } from "culori";
5
+ 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, isAlias, parseAlias, parseColor, pluralize, tokenToColor } from "@terrazzo/token-tools";
6
+ import { contrastWCAG21, inGamut } from "colorjs.io/fn";
7
7
  import { camelCase, kebabCase, pascalCase, snakeCase } from "scule";
8
8
  import { bundle, encodeFragment, findNode, getObjMember, getObjMembers, isPure$ref, maybeRawJSON, mergeObjects, parseRef, replaceNode, traverse } from "@terrazzo/json-schema-tools";
9
9
 
@@ -236,7 +236,7 @@ async function build(tokens, { resolver, sources, logger = new Logger(), config
236
236
  const formats = {};
237
237
  const result = { outputFiles: [] };
238
238
  function getTransforms(plugin) {
239
- return function getTransforms$1(params) {
239
+ return function getTransforms(params) {
240
240
  if (!params?.format) {
241
241
  logger.warn({
242
242
  group: "plugin",
@@ -408,7 +408,7 @@ const rule$26 = {
408
408
  if (tokens[foreground].$type !== "color") throw new Error(`Token ${foreground} isn’t a color`);
409
409
  if (!tokens[background]) throw new Error(`Token ${background} does not exist`);
410
410
  if (tokens[background].$type !== "color") throw new Error(`Token ${background} isn’t a color`);
411
- const contrast = wcagContrast(tokenToCulori(tokens[foreground].$value), tokenToCulori(tokens[background].$value));
411
+ const contrast = contrastWCAG21(tokenToColor(tokens[foreground].$value), tokenToColor(tokens[background].$value));
412
412
  const min = WCAG2_MIN_CONTRAST[options.level ?? "AA"][largeText ? "large" : "default"];
413
413
  if (contrast < min) report({
414
414
  messageId: ERROR_INSUFFICIENT_CONTRAST,
@@ -422,7 +422,6 @@ const rule$26 = {
422
422
  }
423
423
  }
424
424
  };
425
- var a11y_min_contrast_default = rule$26;
426
425
 
427
426
  //#endregion
428
427
  //#region src/lint/plugin-core/rules/a11y-min-font-size.ts
@@ -456,11 +455,10 @@ const rule$25 = {
456
455
  }
457
456
  }
458
457
  };
459
- var a11y_min_font_size_default = rule$25;
460
458
 
461
459
  //#endregion
462
460
  //#region src/lint/plugin-core/rules/colorspace.ts
463
- const COLORSPACE$1 = "core/colorspace";
461
+ const COLORSPACE = "core/colorspace";
464
462
  const ERROR_COLOR$1 = "COLOR";
465
463
  const ERROR_BORDER$1 = "BORDER";
466
464
  const ERROR_GRADIENT$1 = "GRADIENT";
@@ -475,7 +473,7 @@ const rule$24 = {
475
473
  },
476
474
  docs: {
477
475
  description: "Enforce that all colors are in a specific colorspace.",
478
- url: docsLink(COLORSPACE$1)
476
+ url: docsLink(COLORSPACE)
479
477
  }
480
478
  },
481
479
  defaultOptions: { colorSpace: "srgb" },
@@ -534,7 +532,6 @@ const rule$24 = {
534
532
  }
535
533
  }
536
534
  };
537
- var colorspace_default = rule$24;
538
535
 
539
536
  //#endregion
540
537
  //#region src/lint/plugin-core/rules/consistent-naming.ts
@@ -578,7 +575,6 @@ const rule$23 = {
578
575
  }
579
576
  }
580
577
  };
581
- var consistent_naming_default = rule$23;
582
578
 
583
579
  //#endregion
584
580
  //#region src/lint/plugin-core/rules/descriptions.ts
@@ -605,7 +601,6 @@ const rule$22 = {
605
601
  }
606
602
  }
607
603
  };
608
- var descriptions_default = rule$22;
609
604
 
610
605
  //#endregion
611
606
  //#region src/lint/plugin-core/rules/duplicate-values.ts
@@ -650,32 +645,10 @@ const rule$21 = {
650
645
  }
651
646
  }
652
647
  };
653
- var duplicate_values_default = rule$21;
654
648
 
655
649
  //#endregion
656
650
  //#region src/lint/plugin-core/rules/max-gamut.ts
657
651
  const MAX_GAMUT = "core/max-gamut";
658
- const TOLERANCE = 1e-6;
659
- /** is a Culori-parseable color within the specified gamut? */
660
- function isWithinGamut(color, gamut) {
661
- const parsed = tokenToCulori(color);
662
- if (!parsed) return false;
663
- if ([
664
- "rgb",
665
- "hsl",
666
- "hwb"
667
- ].includes(parsed.mode)) return true;
668
- return isWithinThreshold(parsed, clampChroma(parsed, parsed.mode, gamut === "srgb" ? "rgb" : gamut));
669
- }
670
- /** is Color A close enough to Color B? */
671
- function isWithinThreshold(a, b, tolerance = TOLERANCE) {
672
- for (const k in a) {
673
- if (k === "mode" || k === "alpha") continue;
674
- if (!(k in b)) throw new Error(`Can’t compare ${a.mode} to ${b.mode}`);
675
- if (Math.abs(a[k] - b[k]) > tolerance) return false;
676
- }
677
- return true;
678
- }
679
652
  const ERROR_COLOR = "COLOR";
680
653
  const ERROR_BORDER = "BORDER";
681
654
  const ERROR_GRADIENT = "GRADIENT";
@@ -703,7 +676,7 @@ const rule$20 = {
703
676
  if (t.aliasOf) continue;
704
677
  switch (t.$type) {
705
678
  case "color":
706
- if (!isWithinGamut(t.$value, options.gamut)) report({
679
+ if (!inGamut(tokenToColor(t.$value), options.gamut)) report({
707
680
  messageId: ERROR_COLOR,
708
681
  data: {
709
682
  id: t.id,
@@ -714,7 +687,7 @@ const rule$20 = {
714
687
  });
715
688
  break;
716
689
  case "border":
717
- if (!t.partialAliasOf?.color && !isWithinGamut(t.$value.color, options.gamut)) report({
690
+ if (!t.partialAliasOf?.color && !inGamut(tokenToColor(t.$value.color), options.gamut)) report({
718
691
  messageId: ERROR_BORDER,
719
692
  data: {
720
693
  id: t.id,
@@ -725,7 +698,7 @@ const rule$20 = {
725
698
  });
726
699
  break;
727
700
  case "gradient":
728
- for (let stopI = 0; stopI < t.$value.length; stopI++) if (!t.partialAliasOf?.[stopI]?.color && !isWithinGamut(t.$value[stopI].color, options.gamut)) report({
701
+ for (let stopI = 0; stopI < t.$value.length; stopI++) if (!t.partialAliasOf?.[stopI]?.color && !inGamut(tokenToColor(t.$value[stopI].color), options.gamut)) report({
729
702
  messageId: ERROR_GRADIENT,
730
703
  data: {
731
704
  id: t.id,
@@ -736,7 +709,7 @@ const rule$20 = {
736
709
  });
737
710
  break;
738
711
  case "shadow":
739
- for (let shadowI = 0; shadowI < t.$value.length; shadowI++) if (!t.partialAliasOf?.[shadowI]?.color && !isWithinGamut(t.$value[shadowI].color, options.gamut)) report({
712
+ for (let shadowI = 0; shadowI < t.$value.length; shadowI++) if (!t.partialAliasOf?.[shadowI]?.color && !inGamut(tokenToColor(t.$value[shadowI].color), options.gamut)) report({
740
713
  messageId: ERROR_SHADOW,
741
714
  data: {
742
715
  id: t.id,
@@ -750,7 +723,6 @@ const rule$20 = {
750
723
  }
751
724
  }
752
725
  };
753
- var max_gamut_default = rule$20;
754
726
 
755
727
  //#endregion
756
728
  //#region src/lint/plugin-core/rules/required-children.ts
@@ -816,7 +788,6 @@ const rule$19 = {
816
788
  }
817
789
  }
818
790
  };
819
- var required_children_default = rule$19;
820
791
 
821
792
  //#endregion
822
793
  //#region src/lint/plugin-core/rules/required-modes.ts
@@ -852,7 +823,6 @@ const rule$18 = {
852
823
  }
853
824
  }
854
825
  };
855
- var required_modes_default = rule$18;
856
826
 
857
827
  //#endregion
858
828
  //#region src/lint/plugin-core/rules/required-type.ts
@@ -875,7 +845,6 @@ const rule$17 = {
875
845
  });
876
846
  }
877
847
  };
878
- var required_type_default = rule$17;
879
848
 
880
849
  //#endregion
881
850
  //#region src/lint/plugin-core/rules/required-typography-properties.ts
@@ -909,265 +878,135 @@ const rule$16 = {
909
878
  }
910
879
  }
911
880
  };
912
- var required_typography_properties_default = rule$16;
913
881
 
914
882
  //#endregion
915
- //#region src/lint/plugin-core/rules/valid-boolean.ts
916
- const VALID_BOOLEAN = "core/valid-boolean";
883
+ //#region src/lint/plugin-core/rules/valid-font-family.ts
884
+ const VALID_FONT_FAMILY = "core/valid-font-family";
917
885
  const ERROR$9 = "ERROR";
918
886
  const rule$15 = {
919
887
  meta: {
920
- messages: { [ERROR$9]: "Must be a boolean." },
888
+ messages: { [ERROR$9]: "Must be a string, or array of strings." },
921
889
  docs: {
922
- description: "Require boolean tokens to follow the Terrazzo extension.",
923
- url: docsLink(VALID_BOOLEAN)
890
+ description: "Require fontFamily tokens to follow the format.",
891
+ url: docsLink(VALID_FONT_FAMILY)
924
892
  }
925
893
  },
926
894
  defaultOptions: {},
927
895
  create({ tokens, report }) {
928
896
  for (const t of Object.values(tokens)) {
929
- if (t.aliasOf || !t.originalValue || t.$type !== "boolean") continue;
930
- validateBoolean(t.originalValue.$value, {
931
- node: getObjMember(t.source.node, "$value"),
932
- filename: t.source.filename
933
- });
934
- function validateBoolean(value, { node, filename }) {
935
- if (typeof value !== "boolean") report({
897
+ if (t.aliasOf || !t.originalValue) continue;
898
+ switch (t.$type) {
899
+ case "fontFamily":
900
+ validateFontFamily(t.originalValue.$value, {
901
+ node: getObjMember(t.source.node, "$value"),
902
+ filename: t.source.filename
903
+ });
904
+ break;
905
+ case "typography":
906
+ if (typeof t.originalValue.$value === "object" && t.originalValue.$value.fontFamily) {
907
+ if (t.partialAliasOf?.fontFamily) continue;
908
+ const properties = getObjMembers(getObjMember(t.source.node, "$value"));
909
+ validateFontFamily(t.originalValue.$value.fontFamily, {
910
+ node: properties.fontFamily,
911
+ filename: t.source.filename
912
+ });
913
+ }
914
+ break;
915
+ }
916
+ function validateFontFamily(value, { node, filename }) {
917
+ if (typeof value === "string") {
918
+ if (!value) report({
919
+ messageId: ERROR$9,
920
+ node,
921
+ filename
922
+ });
923
+ } else if (Array.isArray(value)) {
924
+ if (!value.every((v) => v && typeof v === "string")) report({
925
+ messageId: ERROR$9,
926
+ node,
927
+ filename
928
+ });
929
+ } else report({
936
930
  messageId: ERROR$9,
937
- filename,
938
- node
931
+ node,
932
+ filename
939
933
  });
940
934
  }
941
935
  }
942
936
  }
943
937
  };
944
- var valid_boolean_default = rule$15;
945
938
 
946
939
  //#endregion
947
- //#region src/lint/plugin-core/rules/valid-border.ts
948
- const VALID_BORDER = "core/valid-border";
940
+ //#region src/lint/plugin-core/rules/valid-font-weight.ts
941
+ const VALID_FONT_WEIGHT = "core/valid-font-weight";
949
942
  const ERROR$8 = "ERROR";
950
- const ERROR_INVALID_PROP$7 = "ERROR_INVALID_PROP";
943
+ const ERROR_STYLE = "ERROR_STYLE";
951
944
  const rule$14 = {
952
945
  meta: {
953
946
  messages: {
954
- [ERROR$8]: `Border token missing required properties: ${new Intl.ListFormat("en-us", { type: "conjunction" }).format(BORDER_REQUIRED_PROPERTIES)}.`,
955
- [ERROR_INVALID_PROP$7]: "Unknown property: {{ key }}."
956
- },
957
- docs: {
958
- description: "Require border tokens to follow the format.",
959
- url: docsLink(VALID_BORDER)
960
- }
961
- },
962
- defaultOptions: {},
963
- create({ tokens, report }) {
964
- for (const t of Object.values(tokens)) {
965
- if (t.aliasOf || !t.originalValue || t.$type !== "border") continue;
966
- validateBorder(t.originalValue.$value, {
967
- node: getObjMember(t.source.node, "$value"),
968
- filename: t.source.filename
969
- });
970
- }
971
- function validateBorder(value, { node, filename }) {
972
- if (!value || typeof value !== "object" || !BORDER_REQUIRED_PROPERTIES.every((property) => property in value)) report({
973
- messageId: ERROR$8,
974
- filename,
975
- node
976
- });
977
- else for (const key of Object.keys(value)) if (!BORDER_REQUIRED_PROPERTIES.includes(key)) report({
978
- messageId: ERROR_INVALID_PROP$7,
979
- data: { key: JSON.stringify(key) },
980
- node: getObjMember(node, key) ?? node,
981
- filename
982
- });
983
- }
984
- }
985
- };
986
- var valid_border_default = rule$14;
987
-
988
- //#endregion
989
- //#region src/lint/plugin-core/rules/valid-color.ts
990
- const VALID_COLOR = "core/valid-color";
991
- const ERROR_ALPHA = "ERROR_ALPHA";
992
- const ERROR_INVALID_COLOR = "ERROR_INVALID_COLOR";
993
- const ERROR_INVALID_COLOR_SPACE = "ERROR_INVALID_COLOR_SPACE";
994
- const ERROR_INVALID_COMPONENT_LENGTH = "ERROR_INVALID_COMPONENT_LENGTH";
995
- const ERROR_INVALID_HEX8 = "ERROR_INVALID_HEX8";
996
- const ERROR_INVALID_PROP$6 = "ERROR_INVALID_PROP";
997
- const ERROR_MISSING_COMPONENTS = "ERROR_MISSING_COMPONENTS";
998
- const ERROR_OBJ_FORMAT = "ERROR_OBJ_FORMAT";
999
- const ERROR_OUT_OF_RANGE = "ERROR_OUT_OF_RANGE";
1000
- const rule$13 = {
1001
- meta: {
1002
- messages: {
1003
- [ERROR_ALPHA]: `Alpha {{ alpha }} not in range 0 – 1.`,
1004
- [ERROR_INVALID_COLOR_SPACE]: `Invalid color space: {{ colorSpace }}. Expected ${new Intl.ListFormat("en-us", { type: "disjunction" }).format(Object.keys(COLORSPACE))}`,
1005
- [ERROR_INVALID_COLOR]: `Could not parse color {{ color }}.`,
1006
- [ERROR_INVALID_COMPONENT_LENGTH]: "Expected {{ expected }} components, received {{ got }}.",
1007
- [ERROR_INVALID_HEX8]: `Hex value can’t be semi-transparent.`,
1008
- [ERROR_INVALID_PROP$6]: `Unknown property {{ key }}.`,
1009
- [ERROR_MISSING_COMPONENTS]: "Expected components to be array of numbers, received {{ got }}.",
1010
- [ERROR_OBJ_FORMAT]: "Migrate to the new object format, e.g. \"#ff0000\" → { \"colorSpace\": \"srgb\", \"components\": [1, 0, 0] } }",
1011
- [ERROR_OUT_OF_RANGE]: `Invalid range for color space {{ colorSpace }}. Expected {{ range }}.`
947
+ [ERROR$8]: `Must either be a valid number (0 - 999) or a valid font weight: ${new Intl.ListFormat("en-us", { type: "disjunction" }).format(Object.keys(FONT_WEIGHTS))}.`,
948
+ [ERROR_STYLE]: "Expected style {{ style }}, received {{ value }}."
1012
949
  },
1013
950
  docs: {
1014
- description: "Require color tokens to follow the format.",
1015
- url: docsLink(VALID_COLOR)
951
+ description: "Require number tokens to follow the format.",
952
+ url: docsLink(VALID_FONT_WEIGHT)
1016
953
  }
1017
954
  },
1018
- defaultOptions: {
1019
- legacyFormat: false,
1020
- ignoreRanges: false
1021
- },
955
+ defaultOptions: { style: void 0 },
1022
956
  create({ tokens, options, report }) {
1023
957
  for (const t of Object.values(tokens)) {
1024
958
  if (t.aliasOf || !t.originalValue) continue;
1025
959
  switch (t.$type) {
1026
- case "color":
1027
- validateColor(t.originalValue.$value, {
960
+ case "fontWeight":
961
+ validateFontWeight(t.originalValue.$value, {
1028
962
  node: getObjMember(t.source.node, "$value"),
1029
963
  filename: t.source.filename
1030
964
  });
1031
965
  break;
1032
- case "border":
1033
- if (t.originalValue.$value.color && !isAlias(t.originalValue.$value.color)) validateColor(t.originalValue.$value.color, {
1034
- node: getObjMember(getObjMember(t.source.node, "$value"), "color"),
1035
- filename: t.source.filename
1036
- });
1037
- break;
1038
- case "gradient": {
1039
- const $valueNode = getObjMember(t.source.node, "$value");
1040
- for (let i = 0; i < t.originalValue.$value.length; i++) {
1041
- const stop = t.originalValue.$value[i];
1042
- if (!stop.color || isAlias(stop.color)) continue;
1043
- validateColor(stop.color, {
1044
- node: getObjMember($valueNode.elements[i].value, "color"),
1045
- filename: t.source.filename
1046
- });
1047
- }
1048
- break;
1049
- }
1050
- case "shadow": {
1051
- const $value = Array.isArray(t.originalValue.$value) ? t.originalValue.$value : [t.originalValue.$value];
1052
- const $valueNode = getObjMember(t.source.node, "$value");
1053
- for (let i = 0; i < $value.length; i++) {
1054
- const layer = $value[i];
1055
- if (!layer.color || isAlias(layer.color)) continue;
1056
- validateColor(layer.color, {
1057
- node: $valueNode.type === "Object" ? getObjMember($valueNode, "color") : getObjMember($valueNode.elements[i].value, "color"),
966
+ case "typography":
967
+ if (typeof t.originalValue.$value === "object" && t.originalValue.$value.fontWeight) {
968
+ if (t.partialAliasOf?.fontWeight) continue;
969
+ const properties = getObjMembers(getObjMember(t.source.node, "$value"));
970
+ validateFontWeight(t.originalValue.$value.fontWeight, {
971
+ node: properties.fontWeight,
1058
972
  filename: t.source.filename
1059
973
  });
1060
974
  }
1061
975
  break;
1062
- }
1063
976
  }
1064
- function validateColor(value, { node, filename }) {
1065
- if (!value) report({
1066
- messageId: ERROR_INVALID_COLOR,
1067
- data: { color: JSON.stringify(value) },
1068
- node,
1069
- filename
1070
- });
1071
- else if (typeof value === "object") {
1072
- for (const key of Object.keys(value)) if (![
1073
- "colorSpace",
1074
- "components",
1075
- "channels",
1076
- "hex",
1077
- "alpha"
1078
- ].includes(key)) report({
1079
- messageId: ERROR_INVALID_PROP$6,
1080
- data: { key: JSON.stringify(key) },
1081
- node: getObjMember(node, key) ?? node,
1082
- filename
1083
- });
1084
- const colorSpace = "colorSpace" in value && typeof value.colorSpace === "string" ? value.colorSpace : void 0;
1085
- const csData = COLORSPACE[colorSpace] || void 0;
1086
- if (!("colorSpace" in value) || !csData) {
1087
- report({
1088
- messageId: ERROR_INVALID_COLOR_SPACE,
1089
- data: { colorSpace },
1090
- node: getObjMember(node, "colorSpace") ?? node,
1091
- filename
1092
- });
1093
- return;
1094
- }
1095
- const components = "components" in value ? value.components : void 0;
1096
- if (Array.isArray(components)) if (csData?.ranges && components?.length === csData.ranges.length) {
1097
- for (let i = 0; i < components.length; i++) if (!Number.isFinite(components[i]) || components[i] < csData.ranges[i][0] || components[i] > csData.ranges[i][1]) {
1098
- if (!(colorSpace === "hsl" && components[0] === null) && !(colorSpace === "hwb" && components[0] === null) && !(colorSpace === "lch" && components[2] === null) && !(colorSpace === "oklch" && components[2] === null)) report({
1099
- messageId: ERROR_OUT_OF_RANGE,
1100
- data: {
1101
- colorSpace,
1102
- range: `[${csData.ranges.map((r) => `${r[0]}–${r[1]}`).join(", ")}]`
1103
- },
1104
- node: getObjMember(node, "components") ?? node,
1105
- filename
1106
- });
1107
- }
1108
- } else report({
1109
- messageId: ERROR_INVALID_COMPONENT_LENGTH,
977
+ function validateFontWeight(value, { node, filename }) {
978
+ if (typeof value === "string") {
979
+ if (options.style === "numbers") report({
980
+ messageId: ERROR_STYLE,
1110
981
  data: {
1111
- expected: csData?.ranges.length,
1112
- got: components?.length ?? 0
982
+ style: "numbers",
983
+ value
1113
984
  },
1114
- node: getObjMember(node, "components") ?? node,
985
+ node,
1115
986
  filename
1116
987
  });
1117
- else report({
1118
- messageId: ERROR_MISSING_COMPONENTS,
1119
- data: { got: JSON.stringify(components) },
1120
- node: getObjMember(node, "components") ?? node,
988
+ else if (!(value in FONT_WEIGHTS)) report({
989
+ messageId: ERROR$8,
990
+ node,
1121
991
  filename
1122
992
  });
1123
- const alpha = "alpha" in value ? value.alpha : void 0;
1124
- if (alpha !== void 0 && (typeof alpha !== "number" || alpha < 0 || alpha > 1)) report({
1125
- messageId: ERROR_ALPHA,
1126
- data: { alpha },
1127
- node: getObjMember(node, "alpha") ?? node,
993
+ } else if (typeof value === "number") {
994
+ if (options.style === "names") report({
995
+ messageId: ERROR_STYLE,
996
+ data: {
997
+ style: "names",
998
+ value
999
+ },
1000
+ node,
1128
1001
  filename
1129
1002
  });
1130
- const hex = "hex" in value ? value.hex : void 0;
1131
- if (hex) {
1132
- let color;
1133
- try {
1134
- color = parseColor(hex);
1135
- } catch {
1136
- report({
1137
- messageId: ERROR_INVALID_COLOR,
1138
- data: { color: hex },
1139
- node: getObjMember(node, "hex") ?? node,
1140
- filename
1141
- });
1142
- return;
1143
- }
1144
- if (color.alpha !== 1) report({
1145
- messageId: ERROR_INVALID_HEX8,
1146
- data: { color: hex },
1147
- node: getObjMember(node, "hex") ?? node,
1148
- filename
1149
- });
1150
- }
1151
- } else if (typeof value === "string") {
1152
- if (isAlias(value)) return;
1153
- if (!options.legacyFormat) report({
1154
- messageId: ERROR_OBJ_FORMAT,
1155
- data: { color: JSON.stringify(value) },
1003
+ else if (!(value >= 0 && value < 1e3)) report({
1004
+ messageId: ERROR$8,
1156
1005
  node,
1157
1006
  filename
1158
1007
  });
1159
- else try {
1160
- parseColor(value);
1161
- } catch {
1162
- report({
1163
- messageId: ERROR_INVALID_COLOR,
1164
- data: { color: JSON.stringify(value) },
1165
- node,
1166
- filename
1167
- });
1168
- }
1169
1008
  } else report({
1170
- messageId: ERROR_INVALID_COLOR,
1009
+ messageId: ERROR$8,
1171
1010
  node,
1172
1011
  filename
1173
1012
  });
@@ -1175,64 +1014,94 @@ const rule$13 = {
1175
1014
  }
1176
1015
  }
1177
1016
  };
1178
- var valid_color_default = rule$13;
1179
1017
 
1180
1018
  //#endregion
1181
- //#region src/lint/plugin-core/rules/valid-cubic-bezier.ts
1182
- const VALID_CUBIC_BEZIER = "core/valid-cubic-bezier";
1183
- const ERROR$7 = "ERROR";
1184
- const ERROR_X = "ERROR_X";
1185
- const ERROR_Y = "ERROR_Y";
1186
- const rule$12 = {
1019
+ //#region src/lint/plugin-core/rules/valid-gradient.ts
1020
+ const VALID_GRADIENT = "core/valid-gradient";
1021
+ const ERROR_MISSING$1 = "ERROR_MISSING";
1022
+ const ERROR_POSITION = "ERROR_POSITION";
1023
+ const ERROR_INVALID_PROP$7 = "ERROR_INVALID_PROP";
1024
+ const rule$13 = {
1187
1025
  meta: {
1188
1026
  messages: {
1189
- [ERROR$7]: "Expected [number, number, number, number].",
1190
- [ERROR_X]: "x values must be between 0-1.",
1191
- [ERROR_Y]: "y values must be a valid number."
1027
+ [ERROR_MISSING$1]: "Must be an array of { color, position } objects.",
1028
+ [ERROR_POSITION]: "Expected number 0-1, received {{ value }}.",
1029
+ [ERROR_INVALID_PROP$7]: "Unknown property {{ key }}."
1192
1030
  },
1193
1031
  docs: {
1194
- description: "Require cubicBezier tokens to follow the format.",
1195
- url: docsLink(VALID_CUBIC_BEZIER)
1032
+ description: "Require gradient tokens to follow the format.",
1033
+ url: docsLink(VALID_GRADIENT)
1196
1034
  }
1197
1035
  },
1198
1036
  defaultOptions: {},
1199
1037
  create({ tokens, report }) {
1200
1038
  for (const t of Object.values(tokens)) {
1201
- if (t.aliasOf || !t.originalValue) continue;
1202
- switch (t.$type) {
1203
- case "cubicBezier":
1204
- validateCubicBezier(t.originalValue.$value, {
1205
- node: getObjMember(t.source.node, "$value"),
1206
- filename: t.source.filename
1039
+ if (t.aliasOf || !t.originalValue || t.$type !== "gradient") continue;
1040
+ validateGradient(t.originalValue.$value, {
1041
+ node: getObjMember(t.source.node, "$value"),
1042
+ filename: t.source.filename
1043
+ });
1044
+ function validateGradient(value, { node, filename }) {
1045
+ if (Array.isArray(value)) for (let i = 0; i < value.length; i++) {
1046
+ const stop = value[i];
1047
+ if (!stop || typeof stop !== "object") {
1048
+ report({
1049
+ messageId: ERROR_MISSING$1,
1050
+ node,
1051
+ filename
1052
+ });
1053
+ continue;
1054
+ }
1055
+ for (const property of GRADIENT_REQUIRED_STOP_PROPERTIES) if (!(property in stop)) report({
1056
+ messageId: ERROR_MISSING$1,
1057
+ node: node.elements[i],
1058
+ filename
1207
1059
  });
1208
- break;
1209
- case "transition": if (typeof t.originalValue.$value === "object" && t.originalValue.$value.timingFunction && !isAlias(t.originalValue.$value.timingFunction)) {
1210
- const $valueNode = getObjMember(t.source.node, "$value");
1211
- validateCubicBezier(t.originalValue.$value.timingFunction, {
1212
- node: getObjMember($valueNode, "timingFunction"),
1213
- filename: t.source.filename
1060
+ for (const key of Object.keys(stop)) if (!GRADIENT_REQUIRED_STOP_PROPERTIES.includes(key)) report({
1061
+ messageId: ERROR_INVALID_PROP$7,
1062
+ data: { key: JSON.stringify(key) },
1063
+ node: node.elements[i],
1064
+ filename
1065
+ });
1066
+ if ("position" in stop && typeof stop.position !== "number" && !isAlias(stop.position)) report({
1067
+ messageId: ERROR_POSITION,
1068
+ data: { value: stop.position },
1069
+ node: getObjMember(node.elements[i].value, "position"),
1070
+ filename
1214
1071
  });
1215
1072
  }
1073
+ else report({
1074
+ messageId: ERROR_MISSING$1,
1075
+ node,
1076
+ filename
1077
+ });
1216
1078
  }
1217
- function validateCubicBezier(value, { node, filename }) {
1218
- if (Array.isArray(value) && value.length === 4) {
1219
- for (const pos of [0, 2]) {
1220
- if (isAlias(value[pos]) || isPure$ref(value[pos])) continue;
1221
- if (!(value[pos] >= 0 && value[pos] <= 1)) report({
1222
- messageId: ERROR_X,
1223
- node: node.elements[pos],
1224
- filename
1225
- });
1226
- }
1227
- for (const pos of [1, 3]) {
1228
- if (isAlias(value[pos]) || isPure$ref(value[pos])) continue;
1229
- if (typeof value[pos] !== "number") report({
1230
- messageId: ERROR_Y,
1231
- node: node.elements[pos],
1232
- filename
1233
- });
1234
- }
1235
- } else report({
1079
+ }
1080
+ }
1081
+ };
1082
+
1083
+ //#endregion
1084
+ //#region src/lint/plugin-core/rules/valid-link.ts
1085
+ const VALID_LINK = "core/valid-link";
1086
+ const ERROR$7 = "ERROR";
1087
+ const rule$12 = {
1088
+ meta: {
1089
+ messages: { [ERROR$7]: "Must be a string." },
1090
+ docs: {
1091
+ description: "Require link tokens to follow the Terrazzo extension.",
1092
+ url: docsLink(VALID_LINK)
1093
+ }
1094
+ },
1095
+ defaultOptions: {},
1096
+ create({ tokens, report }) {
1097
+ for (const t of Object.values(tokens)) {
1098
+ if (t.aliasOf || !t.originalValue || t.$type !== "link") continue;
1099
+ validateLink(t.originalValue.$value, {
1100
+ node: getObjMember(t.source.node, "$value"),
1101
+ filename: t.source.filename
1102
+ });
1103
+ function validateLink(value, { node, filename }) {
1104
+ if (!value || typeof value !== "string") report({
1236
1105
  messageId: ERROR$7,
1237
1106
  node,
1238
1107
  filename
@@ -1241,160 +1110,43 @@ const rule$12 = {
1241
1110
  }
1242
1111
  }
1243
1112
  };
1244
- var valid_cubic_bezier_default = rule$12;
1245
1113
 
1246
1114
  //#endregion
1247
- //#region src/lint/plugin-core/rules/valid-dimension.ts
1248
- const VALID_DIMENSION = "core/valid-dimension";
1249
- const ERROR_FORMAT$1 = "ERROR_FORMAT";
1250
- const ERROR_INVALID_PROP$5 = "ERROR_INVALID_PROP";
1251
- const ERROR_LEGACY$1 = "ERROR_LEGACY";
1252
- const ERROR_UNIT$1 = "ERROR_UNIT";
1253
- const ERROR_VALUE$1 = "ERROR_VALUE";
1115
+ //#region src/lint/plugin-core/rules/valid-number.ts
1116
+ const VALID_NUMBER = "core/valid-number";
1117
+ const ERROR_NAN = "ERROR_NAN";
1254
1118
  const rule$11 = {
1255
1119
  meta: {
1256
- messages: {
1257
- [ERROR_FORMAT$1]: "Invalid dimension: {{ value }}. Expected object with \"value\" and \"unit\".",
1258
- [ERROR_LEGACY$1]: "Migrate to the new object format: { \"value\": 10, \"unit\": \"px\" }.",
1259
- [ERROR_UNIT$1]: "Unit {{ unit }} not allowed. Expected {{ allowed }}.",
1260
- [ERROR_INVALID_PROP$5]: "Unknown property {{ key }}.",
1261
- [ERROR_VALUE$1]: "Expected number, received {{ value }}."
1262
- },
1120
+ messages: { [ERROR_NAN]: "Must be a number." },
1263
1121
  docs: {
1264
- description: "Require dimension tokens to follow the format",
1265
- url: docsLink(VALID_DIMENSION)
1122
+ description: "Require number tokens to follow the format.",
1123
+ url: docsLink(VALID_NUMBER)
1266
1124
  }
1267
1125
  },
1268
- defaultOptions: {
1269
- legacyFormat: false,
1270
- allowedUnits: [
1271
- "px",
1272
- "em",
1273
- "rem"
1274
- ]
1275
- },
1276
- create({ tokens, options, report }) {
1126
+ defaultOptions: {},
1127
+ create({ tokens, report }) {
1277
1128
  for (const t of Object.values(tokens)) {
1278
1129
  if (t.aliasOf || !t.originalValue) continue;
1279
1130
  switch (t.$type) {
1280
- case "dimension":
1281
- validateDimension(t.originalValue.$value, {
1131
+ case "number":
1132
+ validateNumber(t.originalValue.$value, {
1282
1133
  node: getObjMember(t.source.node, "$value"),
1283
1134
  filename: t.source.filename
1284
1135
  });
1285
1136
  break;
1286
- case "strokeStyle":
1287
- if (typeof t.originalValue.$value === "object" && Array.isArray(t.originalValue.$value.dashArray)) {
1288
- const dashArray = getObjMember(getObjMember(t.source.node, "$value"), "dashArray");
1289
- for (let i = 0; i < t.originalValue.$value.dashArray.length; i++) {
1290
- if (isAlias(t.originalValue.$value.dashArray[i])) continue;
1291
- validateDimension(t.originalValue.$value.dashArray[i], {
1292
- node: dashArray.elements[i].value,
1293
- filename: t.source.filename
1294
- });
1295
- }
1296
- }
1297
- break;
1298
- case "border": {
1137
+ case "typography": {
1299
1138
  const $valueNode = getObjMember(t.source.node, "$value");
1300
1139
  if (typeof t.originalValue.$value === "object") {
1301
- if (t.originalValue.$value.width && !isAlias(t.originalValue.$value.width)) validateDimension(t.originalValue.$value.width, {
1302
- node: getObjMember($valueNode, "width"),
1140
+ if (t.originalValue.$value.lineHeight && !isAlias(t.originalValue.$value.lineHeight) && typeof t.originalValue.$value.lineHeight !== "object") validateNumber(t.originalValue.$value.lineHeight, {
1141
+ node: getObjMember($valueNode, "lineHeight"),
1303
1142
  filename: t.source.filename
1304
1143
  });
1305
- if (typeof t.originalValue.$value.style === "object" && Array.isArray(t.originalValue.$value.style.dashArray)) {
1306
- const dashArray = getObjMember(getObjMember($valueNode, "style"), "dashArray");
1307
- for (let i = 0; i < t.originalValue.$value.style.dashArray.length; i++) {
1308
- if (isAlias(t.originalValue.$value.style.dashArray[i])) continue;
1309
- validateDimension(t.originalValue.$value.style.dashArray[i], {
1310
- node: dashArray.elements[i].value,
1311
- filename: t.source.filename
1312
- });
1313
- }
1314
- }
1315
- }
1316
- break;
1317
- }
1318
- case "shadow":
1319
- if (t.originalValue.$value && typeof t.originalValue.$value === "object") {
1320
- const $valueNode = getObjMember(t.source.node, "$value");
1321
- const valueArray = Array.isArray(t.originalValue.$value) ? t.originalValue.$value : [t.originalValue.$value];
1322
- for (let i = 0; i < valueArray.length; i++) {
1323
- const node = $valueNode.type === "Array" ? $valueNode.elements[i].value : $valueNode;
1324
- for (const property of [
1325
- "offsetX",
1326
- "offsetY",
1327
- "blur",
1328
- "spread"
1329
- ]) {
1330
- if (isAlias(valueArray[i][property])) continue;
1331
- validateDimension(valueArray[i][property], {
1332
- node: getObjMember(node, property),
1333
- filename: t.source.filename
1334
- });
1335
- }
1336
- }
1337
- }
1338
- break;
1339
- case "typography": {
1340
- const $valueNode = getObjMember(t.source.node, "$value");
1341
- if (typeof t.originalValue.$value === "object") {
1342
- for (const property of [
1343
- "fontSize",
1344
- "lineHeight",
1345
- "letterSpacing"
1346
- ]) if (property in t.originalValue.$value) {
1347
- if (isAlias(t.originalValue.$value[property]) || property === "lineHeight" && typeof t.originalValue.$value[property] === "number") continue;
1348
- validateDimension(t.originalValue.$value[property], {
1349
- node: getObjMember($valueNode, property),
1350
- filename: t.source.filename
1351
- });
1352
- }
1353
1144
  }
1354
- break;
1355
1145
  }
1356
1146
  }
1357
- function validateDimension(value, { node, filename }) {
1358
- if (value && typeof value === "object") {
1359
- for (const key of Object.keys(value)) if (!["value", "unit"].includes(key)) report({
1360
- messageId: ERROR_INVALID_PROP$5,
1361
- data: { key: JSON.stringify(key) },
1362
- node: getObjMember(node, key) ?? node,
1363
- filename
1364
- });
1365
- const { unit, value: numValue } = value;
1366
- if (!("value" in value || "unit" in value)) {
1367
- report({
1368
- messageId: ERROR_FORMAT$1,
1369
- data: { value },
1370
- node,
1371
- filename
1372
- });
1373
- return;
1374
- }
1375
- if (!options.allowedUnits.includes(unit)) report({
1376
- messageId: ERROR_UNIT$1,
1377
- data: {
1378
- unit,
1379
- allowed: new Intl.ListFormat("en-us", { type: "disjunction" }).format(options.allowedUnits)
1380
- },
1381
- node: getObjMember(node, "unit") ?? node,
1382
- filename
1383
- });
1384
- if (!Number.isFinite(numValue)) report({
1385
- messageId: ERROR_VALUE$1,
1386
- data: { value },
1387
- node: getObjMember(node, "value") ?? node,
1388
- filename
1389
- });
1390
- } else if (typeof value === "string" && !options.legacyFormat) report({
1391
- messageId: ERROR_LEGACY$1,
1392
- node,
1393
- filename
1394
- });
1395
- else report({
1396
- messageId: ERROR_FORMAT$1,
1397
- data: { value },
1147
+ function validateNumber(value, { node, filename }) {
1148
+ if (typeof value !== "number" || Number.isNaN(value)) report({
1149
+ messageId: ERROR_NAN,
1398
1150
  node,
1399
1151
  filename
1400
1152
  });
@@ -1402,150 +1154,72 @@ const rule$11 = {
1402
1154
  }
1403
1155
  }
1404
1156
  };
1405
- var valid_dimension_default = rule$11;
1406
1157
 
1407
1158
  //#endregion
1408
- //#region src/lint/plugin-core/rules/valid-duration.ts
1409
- const VALID_DURATION = "core/valid-duration";
1410
- const ERROR_FORMAT = "ERROR_FORMAT";
1411
- const ERROR_INVALID_PROP$4 = "ERROR_INVALID_PROP";
1412
- const ERROR_LEGACY = "ERROR_LEGACY";
1413
- const ERROR_UNIT = "ERROR_UNIT";
1414
- const ERROR_VALUE = "ERROR_VALUE";
1159
+ //#region src/lint/plugin-core/rules/valid-shadow.ts
1160
+ const VALID_SHADOW = "core/valid-shadow";
1161
+ const ERROR$6 = "ERROR";
1162
+ const ERROR_INVALID_PROP$6 = "ERROR_INVALID_PROP";
1415
1163
  const rule$10 = {
1416
1164
  meta: {
1417
1165
  messages: {
1418
- [ERROR_FORMAT]: "Migrate to the new object format: { \"value\": 2, \"unit\": \"ms\" }.",
1419
- [ERROR_LEGACY]: "Migrate to the new object format: { \"value\": 10, \"unit\": \"px\" }.",
1420
- [ERROR_INVALID_PROP$4]: "Unknown property: {{ key }}.",
1421
- [ERROR_UNIT]: "Unknown unit {{ unit }}. Expected \"ms\" or \"s\".",
1422
- [ERROR_VALUE]: "Expected number, received {{ value }}."
1166
+ [ERROR$6]: `Missing required properties: ${new Intl.ListFormat("en-us", { type: "conjunction" }).format(SHADOW_REQUIRED_PROPERTIES)}.`,
1167
+ [ERROR_INVALID_PROP$6]: "Unknown property {{ key }}."
1423
1168
  },
1424
1169
  docs: {
1425
- description: "Require duration tokens to follow the format",
1426
- url: docsLink(VALID_DURATION)
1170
+ description: "Require shadow tokens to follow the format.",
1171
+ url: docsLink(VALID_SHADOW)
1427
1172
  }
1428
1173
  },
1429
- defaultOptions: {
1430
- legacyFormat: false,
1431
- unknownUnits: false
1432
- },
1433
- create({ tokens, options, report }) {
1174
+ defaultOptions: {},
1175
+ create({ tokens, report }) {
1434
1176
  for (const t of Object.values(tokens)) {
1435
- if (t.aliasOf || !t.originalValue) continue;
1436
- switch (t.$type) {
1437
- case "duration":
1438
- validateDuration(t.originalValue.$value, {
1439
- node: getObjMember(t.source.node, "$value"),
1440
- filename: t.source.filename
1441
- });
1442
- break;
1443
- case "transition":
1444
- if (typeof t.originalValue.$value === "object") {
1445
- const $valueNode = getObjMember(t.source.node, "$value");
1446
- for (const property of ["duration", "delay"]) if (t.originalValue.$value[property] && !isAlias(t.originalValue.$value[property])) validateDuration(t.originalValue.$value[property], {
1447
- node: getObjMember($valueNode, property),
1448
- filename: t.source.filename
1449
- });
1450
- }
1451
- break;
1452
- }
1453
- function validateDuration(value, { node, filename }) {
1454
- if (value && typeof value === "object") {
1455
- for (const key of Object.keys(value)) if (!["value", "unit"].includes(key)) report({
1456
- messageId: ERROR_INVALID_PROP$4,
1457
- data: { key: JSON.stringify(key) },
1458
- node: getObjMember(node, key) ?? node,
1459
- filename
1460
- });
1461
- const { unit, value: numValue } = value;
1462
- if (!("value" in value || "unit" in value)) {
1463
- report({
1464
- messageId: ERROR_FORMAT,
1465
- data: { value },
1466
- node,
1467
- filename
1468
- });
1469
- return;
1470
- }
1471
- if (!options.unknownUnits && !["ms", "s"].includes(unit)) report({
1472
- messageId: ERROR_UNIT,
1473
- data: { unit },
1474
- node: getObjMember(node, "unit") ?? node,
1475
- filename
1476
- });
1477
- if (!Number.isFinite(numValue)) report({
1478
- messageId: ERROR_VALUE,
1479
- data: { value },
1480
- node: getObjMember(node, "value") ?? node,
1481
- filename
1482
- });
1483
- } else if (typeof value === "string" && !options.legacyFormat) report({
1484
- messageId: ERROR_FORMAT,
1177
+ if (t.aliasOf || !t.originalValue || t.$type !== "shadow") continue;
1178
+ validateShadow(t.originalValue.$value, {
1179
+ node: getObjMember(t.source.node, "$value"),
1180
+ filename: t.source.filename
1181
+ });
1182
+ function validateShadow(value, { node, filename }) {
1183
+ const wrappedValue = Array.isArray(value) ? value : [value];
1184
+ for (let i = 0; i < wrappedValue.length; i++) if (!wrappedValue[i] || typeof wrappedValue[i] !== "object" || !SHADOW_REQUIRED_PROPERTIES.every((property) => property in wrappedValue[i])) report({
1185
+ messageId: ERROR$6,
1485
1186
  node,
1486
1187
  filename
1487
1188
  });
1488
- else report({
1489
- messageId: ERROR_FORMAT,
1490
- data: { value },
1491
- node,
1189
+ else for (const key of Object.keys(wrappedValue[i])) if (![...SHADOW_REQUIRED_PROPERTIES, "inset"].includes(key)) report({
1190
+ messageId: ERROR_INVALID_PROP$6,
1191
+ data: { key: JSON.stringify(key) },
1192
+ node: getObjMember(node.type === "Array" ? node.elements[i].value : node, key),
1492
1193
  filename
1493
1194
  });
1494
1195
  }
1495
1196
  }
1496
1197
  }
1497
1198
  };
1498
- var valid_duration_default = rule$10;
1499
1199
 
1500
1200
  //#endregion
1501
- //#region src/lint/plugin-core/rules/valid-font-family.ts
1502
- const VALID_FONT_FAMILY = "core/valid-font-family";
1503
- const ERROR$6 = "ERROR";
1201
+ //#region src/lint/plugin-core/rules/valid-string.ts
1202
+ const VALID_STRING = "core/valid-string";
1203
+ const ERROR$5 = "ERROR";
1504
1204
  const rule$9 = {
1505
1205
  meta: {
1506
- messages: { [ERROR$6]: "Must be a string, or array of strings." },
1206
+ messages: { [ERROR$5]: "Must be a string." },
1507
1207
  docs: {
1508
- description: "Require fontFamily tokens to follow the format.",
1509
- url: docsLink(VALID_FONT_FAMILY)
1208
+ description: "Require string tokens to follow the Terrazzo extension.",
1209
+ url: docsLink(VALID_STRING)
1510
1210
  }
1511
1211
  },
1512
1212
  defaultOptions: {},
1513
1213
  create({ tokens, report }) {
1514
1214
  for (const t of Object.values(tokens)) {
1515
- if (t.aliasOf || !t.originalValue) continue;
1516
- switch (t.$type) {
1517
- case "fontFamily":
1518
- validateFontFamily(t.originalValue.$value, {
1519
- node: getObjMember(t.source.node, "$value"),
1520
- filename: t.source.filename
1521
- });
1522
- break;
1523
- case "typography":
1524
- if (typeof t.originalValue.$value === "object" && t.originalValue.$value.fontFamily) {
1525
- if (t.partialAliasOf?.fontFamily) continue;
1526
- const properties = getObjMembers(getObjMember(t.source.node, "$value"));
1527
- validateFontFamily(t.originalValue.$value.fontFamily, {
1528
- node: properties.fontFamily,
1529
- filename: t.source.filename
1530
- });
1531
- }
1532
- break;
1533
- }
1534
- function validateFontFamily(value, { node, filename }) {
1535
- if (typeof value === "string") {
1536
- if (!value) report({
1537
- messageId: ERROR$6,
1538
- node,
1539
- filename
1540
- });
1541
- } else if (Array.isArray(value)) {
1542
- if (!value.every((v) => v && typeof v === "string")) report({
1543
- messageId: ERROR$6,
1544
- node,
1545
- filename
1546
- });
1547
- } else report({
1548
- messageId: ERROR$6,
1215
+ if (t.aliasOf || !t.originalValue || t.$type !== "string") continue;
1216
+ validateString(t.originalValue.$value, {
1217
+ node: getObjMember(t.source.node, "$value"),
1218
+ filename: t.source.filename
1219
+ });
1220
+ function validateString(value, { node, filename }) {
1221
+ if (typeof value !== "string") report({
1222
+ messageId: ERROR$5,
1549
1223
  node,
1550
1224
  filename
1551
1225
  });
@@ -1553,79 +1227,82 @@ const rule$9 = {
1553
1227
  }
1554
1228
  }
1555
1229
  };
1556
- var valid_font_family_default = rule$9;
1557
1230
 
1558
1231
  //#endregion
1559
- //#region src/lint/plugin-core/rules/valid-font-weight.ts
1560
- const VALID_FONT_WEIGHT = "core/valid-font-weight";
1561
- const ERROR$5 = "ERROR";
1562
- const ERROR_STYLE = "ERROR_STYLE";
1232
+ //#region src/lint/plugin-core/rules/valid-stroke-style.ts
1233
+ const VALID_STROKE_STYLE = "core/valid-stroke-style";
1234
+ const ERROR_STR = "ERROR_STR";
1235
+ const ERROR_OBJ = "ERROR_OBJ";
1236
+ const ERROR_LINE_CAP = "ERROR_LINE_CAP";
1237
+ const ERROR_INVALID_PROP$5 = "ERROR_INVALID_PROP";
1563
1238
  const rule$8 = {
1564
1239
  meta: {
1565
1240
  messages: {
1566
- [ERROR$5]: `Must either be a valid number (0 - 999) or a valid font weight: ${new Intl.ListFormat("en-us", { type: "disjunction" }).format(Object.keys(FONT_WEIGHTS))}.`,
1567
- [ERROR_STYLE]: "Expected style {{ style }}, received {{ value }}."
1241
+ [ERROR_STR]: `Value most be one of ${new Intl.ListFormat("en-us", { type: "disjunction" }).format(STROKE_STYLE_STRING_VALUES)}.`,
1242
+ [ERROR_OBJ]: `Missing required properties: ${new Intl.ListFormat("en-us", { type: "conjunction" }).format(TRANSITION_REQUIRED_PROPERTIES)}.`,
1243
+ [ERROR_LINE_CAP]: `lineCap must be one of ${new Intl.ListFormat("en-us", { type: "disjunction" }).format(STROKE_STYLE_LINE_CAP_VALUES)}.`,
1244
+ [ERROR_INVALID_PROP$5]: "Unknown property: {{ key }}."
1568
1245
  },
1569
1246
  docs: {
1570
- description: "Require number tokens to follow the format.",
1571
- url: docsLink(VALID_FONT_WEIGHT)
1247
+ description: "Require strokeStyle tokens to follow the format.",
1248
+ url: docsLink(VALID_STROKE_STYLE)
1572
1249
  }
1573
1250
  },
1574
- defaultOptions: { style: void 0 },
1575
- create({ tokens, options, report }) {
1251
+ defaultOptions: {},
1252
+ create({ tokens, report }) {
1576
1253
  for (const t of Object.values(tokens)) {
1577
1254
  if (t.aliasOf || !t.originalValue) continue;
1578
1255
  switch (t.$type) {
1579
- case "fontWeight":
1580
- validateFontWeight(t.originalValue.$value, {
1256
+ case "strokeStyle":
1257
+ validateStrokeStyle(t.originalValue.$value, {
1581
1258
  node: getObjMember(t.source.node, "$value"),
1582
1259
  filename: t.source.filename
1583
1260
  });
1584
1261
  break;
1585
- case "typography":
1586
- if (typeof t.originalValue.$value === "object" && t.originalValue.$value.fontWeight) {
1587
- if (t.partialAliasOf?.fontWeight) continue;
1588
- const properties = getObjMembers(getObjMember(t.source.node, "$value"));
1589
- validateFontWeight(t.originalValue.$value.fontWeight, {
1590
- node: properties.fontWeight,
1262
+ case "border":
1263
+ if (t.originalValue.$value && typeof t.originalValue.$value === "object") {
1264
+ const $valueNode = getObjMember(t.source.node, "$value");
1265
+ if (t.originalValue.$value.style) validateStrokeStyle(t.originalValue.$value.style, {
1266
+ node: getObjMember($valueNode, "style"),
1591
1267
  filename: t.source.filename
1592
1268
  });
1593
1269
  }
1594
1270
  break;
1595
1271
  }
1596
- function validateFontWeight(value, { node, filename }) {
1272
+ function validateStrokeStyle(value, { node, filename }) {
1597
1273
  if (typeof value === "string") {
1598
- if (options.style === "numbers") report({
1599
- messageId: ERROR_STYLE,
1600
- data: {
1601
- style: "numbers",
1602
- value
1603
- },
1274
+ if (!isAlias(value) && !STROKE_STYLE_STRING_VALUES.includes(value)) {
1275
+ report({
1276
+ messageId: ERROR_STR,
1277
+ node,
1278
+ filename
1279
+ });
1280
+ return;
1281
+ }
1282
+ } else if (value && typeof value === "object") {
1283
+ if (!STROKE_STYLE_OBJECT_REQUIRED_PROPERTIES.every((property) => property in value)) report({
1284
+ messageId: ERROR_OBJ,
1604
1285
  node,
1605
1286
  filename
1606
1287
  });
1607
- else if (!(value in FONT_WEIGHTS)) report({
1608
- messageId: ERROR$5,
1609
- node,
1288
+ if (!Array.isArray(value.dashArray)) report({
1289
+ messageId: ERROR_OBJ,
1290
+ node: getObjMember(node, "dashArray"),
1610
1291
  filename
1611
1292
  });
1612
- } else if (typeof value === "number") {
1613
- if (options.style === "names") report({
1614
- messageId: ERROR_STYLE,
1615
- data: {
1616
- style: "names",
1617
- value
1618
- },
1619
- node,
1293
+ if (!STROKE_STYLE_LINE_CAP_VALUES.includes(value.lineCap)) report({
1294
+ messageId: ERROR_OBJ,
1295
+ node: getObjMember(node, "lineCap"),
1620
1296
  filename
1621
1297
  });
1622
- else if (!(value >= 0 && value < 1e3)) report({
1623
- messageId: ERROR$5,
1624
- node,
1298
+ for (const key of Object.keys(value)) if (!["dashArray", "lineCap"].includes(key)) report({
1299
+ messageId: ERROR_INVALID_PROP$5,
1300
+ data: { key: JSON.stringify(key) },
1301
+ node: getObjMember(node, key),
1625
1302
  filename
1626
1303
  });
1627
1304
  } else report({
1628
- messageId: ERROR$5,
1305
+ messageId: ERROR_OBJ,
1629
1306
  node,
1630
1307
  filename
1631
1308
  });
@@ -1633,97 +1310,90 @@ const rule$8 = {
1633
1310
  }
1634
1311
  }
1635
1312
  };
1636
- var valid_font_weight_default = rule$8;
1637
1313
 
1638
1314
  //#endregion
1639
- //#region src/lint/plugin-core/rules/valid-gradient.ts
1640
- const VALID_GRADIENT = "core/valid-gradient";
1641
- const ERROR_MISSING$1 = "ERROR_MISSING";
1642
- const ERROR_POSITION = "ERROR_POSITION";
1643
- const ERROR_INVALID_PROP$3 = "ERROR_INVALID_PROP";
1315
+ //#region src/lint/plugin-core/rules/valid-transition.ts
1316
+ const VALID_TRANSITION = "core/valid-transition";
1317
+ const ERROR$4 = "ERROR";
1318
+ const ERROR_INVALID_PROP$4 = "ERROR_INVALID_PROP";
1644
1319
  const rule$7 = {
1645
1320
  meta: {
1646
1321
  messages: {
1647
- [ERROR_MISSING$1]: "Must be an array of { color, position } objects.",
1648
- [ERROR_POSITION]: "Expected number 0-1, received {{ value }}.",
1649
- [ERROR_INVALID_PROP$3]: "Unknown property {{ key }}."
1322
+ [ERROR$4]: `Missing required properties: ${new Intl.ListFormat("en-us", { type: "conjunction" }).format(TRANSITION_REQUIRED_PROPERTIES)}.`,
1323
+ [ERROR_INVALID_PROP$4]: "Unknown property: {{ key }}."
1650
1324
  },
1651
1325
  docs: {
1652
- description: "Require gradient tokens to follow the format.",
1653
- url: docsLink(VALID_GRADIENT)
1326
+ description: "Require transition tokens to follow the format.",
1327
+ url: docsLink(VALID_TRANSITION)
1654
1328
  }
1655
1329
  },
1656
1330
  defaultOptions: {},
1657
1331
  create({ tokens, report }) {
1658
1332
  for (const t of Object.values(tokens)) {
1659
- if (t.aliasOf || !t.originalValue || t.$type !== "gradient") continue;
1660
- validateGradient(t.originalValue.$value, {
1333
+ if (t.aliasOf || !t.originalValue || t.$type !== "transition") continue;
1334
+ validateTransition(t.originalValue.$value, {
1661
1335
  node: getObjMember(t.source.node, "$value"),
1662
1336
  filename: t.source.filename
1663
1337
  });
1664
- function validateGradient(value, { node, filename }) {
1665
- if (Array.isArray(value)) for (let i = 0; i < value.length; i++) {
1666
- const stop = value[i];
1667
- if (!stop || typeof stop !== "object") {
1668
- report({
1669
- messageId: ERROR_MISSING$1,
1670
- node,
1671
- filename
1672
- });
1673
- continue;
1674
- }
1675
- for (const property of GRADIENT_REQUIRED_STOP_PROPERTIES) if (!(property in stop)) report({
1676
- messageId: ERROR_MISSING$1,
1677
- node: node.elements[i],
1678
- filename
1679
- });
1680
- for (const key of Object.keys(stop)) if (!GRADIENT_REQUIRED_STOP_PROPERTIES.includes(key)) report({
1681
- messageId: ERROR_INVALID_PROP$3,
1682
- data: { key: JSON.stringify(key) },
1683
- node: node.elements[i],
1684
- filename
1685
- });
1686
- if ("position" in stop && typeof stop.position !== "number" && !isAlias(stop.position)) report({
1687
- messageId: ERROR_POSITION,
1688
- data: { value: stop.position },
1689
- node: getObjMember(node.elements[i].value, "position"),
1690
- filename
1691
- });
1692
- }
1693
- else report({
1694
- messageId: ERROR_MISSING$1,
1695
- node,
1696
- filename
1697
- });
1698
- }
1338
+ }
1339
+ function validateTransition(value, { node, filename }) {
1340
+ if (!value || typeof value !== "object" || !TRANSITION_REQUIRED_PROPERTIES.every((property) => property in value)) report({
1341
+ messageId: ERROR$4,
1342
+ node,
1343
+ filename
1344
+ });
1345
+ else for (const key of Object.keys(value)) if (!TRANSITION_REQUIRED_PROPERTIES.includes(key)) report({
1346
+ messageId: ERROR_INVALID_PROP$4,
1347
+ data: { key: JSON.stringify(key) },
1348
+ node: getObjMember(node, key),
1349
+ filename
1350
+ });
1699
1351
  }
1700
1352
  }
1701
1353
  };
1702
- var valid_gradient_default = rule$7;
1703
1354
 
1704
1355
  //#endregion
1705
- //#region src/lint/plugin-core/rules/valid-link.ts
1706
- const VALID_LINK = "core/valid-link";
1707
- const ERROR$4 = "ERROR";
1356
+ //#region src/lint/plugin-core/rules/valid-typography.ts
1357
+ const VALID_TYPOGRAPHY = "core/valid-typography";
1358
+ const ERROR$3 = "ERROR";
1359
+ const ERROR_MISSING = "ERROR_MISSING";
1708
1360
  const rule$6 = {
1709
1361
  meta: {
1710
- messages: { [ERROR$4]: "Must be a string." },
1362
+ messages: {
1363
+ [ERROR$3]: `Expected object, received {{ value }}.`,
1364
+ [ERROR_MISSING]: `Missing required property "{{ property }}".`
1365
+ },
1711
1366
  docs: {
1712
- description: "Require link tokens to follow the Terrazzo extension.",
1713
- url: docsLink(VALID_LINK)
1367
+ description: "Require typography tokens to follow the format.",
1368
+ url: docsLink(VALID_TYPOGRAPHY)
1714
1369
  }
1715
1370
  },
1716
- defaultOptions: {},
1717
- create({ tokens, report }) {
1371
+ defaultOptions: { requiredProperties: [
1372
+ "fontFamily",
1373
+ "fontSize",
1374
+ "fontWeight",
1375
+ "letterSpacing",
1376
+ "lineHeight"
1377
+ ] },
1378
+ create({ tokens, options, report }) {
1379
+ const isIgnored = options.ignore ? wcmatch(options.ignore) : () => false;
1718
1380
  for (const t of Object.values(tokens)) {
1719
- if (t.aliasOf || !t.originalValue || t.$type !== "link") continue;
1720
- validateLink(t.originalValue.$value, {
1381
+ if (t.aliasOf || !t.originalValue || t.$type !== "typography" || isIgnored(t.id)) continue;
1382
+ validateTypography(t.originalValue.$value, {
1721
1383
  node: getObjMember(t.source.node, "$value"),
1722
1384
  filename: t.source.filename
1723
1385
  });
1724
- function validateLink(value, { node, filename }) {
1725
- if (!value || typeof value !== "string") report({
1726
- messageId: ERROR$4,
1386
+ function validateTypography(value, { node, filename }) {
1387
+ if (value && typeof value === "object") {
1388
+ for (const property of options.requiredProperties) if (!(property in value)) report({
1389
+ messageId: ERROR_MISSING,
1390
+ data: { property },
1391
+ node,
1392
+ filename
1393
+ });
1394
+ } else report({
1395
+ messageId: ERROR$3,
1396
+ data: { value: JSON.stringify(value) },
1727
1397
  node,
1728
1398
  filename
1729
1399
  });
@@ -1731,203 +1401,266 @@ const rule$6 = {
1731
1401
  }
1732
1402
  }
1733
1403
  };
1734
- var valid_link_default = rule$6;
1735
1404
 
1736
1405
  //#endregion
1737
- //#region src/lint/plugin-core/rules/valid-number.ts
1738
- const VALID_NUMBER = "core/valid-number";
1739
- const ERROR_NAN = "ERROR_NAN";
1406
+ //#region src/lint/plugin-core/rules/valid-boolean.ts
1407
+ const VALID_BOOLEAN = "core/valid-boolean";
1408
+ const ERROR$2 = "ERROR";
1740
1409
  const rule$5 = {
1741
1410
  meta: {
1742
- messages: { [ERROR_NAN]: "Must be a number." },
1411
+ messages: { [ERROR$2]: "Must be a boolean." },
1743
1412
  docs: {
1744
- description: "Require number tokens to follow the format.",
1745
- url: docsLink(VALID_NUMBER)
1413
+ description: "Require boolean tokens to follow the Terrazzo extension.",
1414
+ url: docsLink(VALID_BOOLEAN)
1746
1415
  }
1747
1416
  },
1748
1417
  defaultOptions: {},
1749
1418
  create({ tokens, report }) {
1750
1419
  for (const t of Object.values(tokens)) {
1751
- if (t.aliasOf || !t.originalValue) continue;
1752
- switch (t.$type) {
1753
- case "number":
1754
- validateNumber(t.originalValue.$value, {
1755
- node: getObjMember(t.source.node, "$value"),
1756
- filename: t.source.filename
1757
- });
1758
- break;
1759
- case "typography": {
1760
- const $valueNode = getObjMember(t.source.node, "$value");
1761
- if (typeof t.originalValue.$value === "object") {
1762
- if (t.originalValue.$value.lineHeight && !isAlias(t.originalValue.$value.lineHeight) && typeof t.originalValue.$value.lineHeight !== "object") validateNumber(t.originalValue.$value.lineHeight, {
1763
- node: getObjMember($valueNode, "lineHeight"),
1764
- filename: t.source.filename
1765
- });
1766
- }
1767
- }
1768
- }
1769
- function validateNumber(value, { node, filename }) {
1770
- if (typeof value !== "number" || Number.isNaN(value)) report({
1771
- messageId: ERROR_NAN,
1772
- node,
1773
- filename
1420
+ if (t.aliasOf || !t.originalValue || t.$type !== "boolean") continue;
1421
+ validateBoolean(t.originalValue.$value, {
1422
+ node: getObjMember(t.source.node, "$value"),
1423
+ filename: t.source.filename
1424
+ });
1425
+ function validateBoolean(value, { node, filename }) {
1426
+ if (typeof value !== "boolean") report({
1427
+ messageId: ERROR$2,
1428
+ filename,
1429
+ node
1774
1430
  });
1775
1431
  }
1776
1432
  }
1777
1433
  }
1778
1434
  };
1779
- var valid_number_default = rule$5;
1780
1435
 
1781
1436
  //#endregion
1782
- //#region src/lint/plugin-core/rules/valid-shadow.ts
1783
- const VALID_SHADOW = "core/valid-shadow";
1784
- const ERROR$3 = "ERROR";
1785
- const ERROR_INVALID_PROP$2 = "ERROR_INVALID_PROP";
1437
+ //#region src/lint/plugin-core/rules/valid-border.ts
1438
+ const VALID_BORDER = "core/valid-border";
1439
+ const ERROR$1 = "ERROR";
1440
+ const ERROR_INVALID_PROP$3 = "ERROR_INVALID_PROP";
1786
1441
  const rule$4 = {
1787
1442
  meta: {
1788
1443
  messages: {
1789
- [ERROR$3]: `Missing required properties: ${new Intl.ListFormat("en-us", { type: "conjunction" }).format(SHADOW_REQUIRED_PROPERTIES)}.`,
1790
- [ERROR_INVALID_PROP$2]: "Unknown property {{ key }}."
1444
+ [ERROR$1]: `Border token missing required properties: ${new Intl.ListFormat("en-us", { type: "conjunction" }).format(BORDER_REQUIRED_PROPERTIES)}.`,
1445
+ [ERROR_INVALID_PROP$3]: "Unknown property: {{ key }}."
1791
1446
  },
1792
1447
  docs: {
1793
- description: "Require shadow tokens to follow the format.",
1794
- url: docsLink(VALID_SHADOW)
1448
+ description: "Require border tokens to follow the format.",
1449
+ url: docsLink(VALID_BORDER)
1795
1450
  }
1796
1451
  },
1797
1452
  defaultOptions: {},
1798
1453
  create({ tokens, report }) {
1799
1454
  for (const t of Object.values(tokens)) {
1800
- if (t.aliasOf || !t.originalValue || t.$type !== "shadow") continue;
1801
- validateShadow(t.originalValue.$value, {
1455
+ if (t.aliasOf || !t.originalValue || t.$type !== "border") continue;
1456
+ validateBorder(t.originalValue.$value, {
1802
1457
  node: getObjMember(t.source.node, "$value"),
1803
1458
  filename: t.source.filename
1804
1459
  });
1805
- function validateShadow(value, { node, filename }) {
1806
- const wrappedValue = Array.isArray(value) ? value : [value];
1807
- for (let i = 0; i < wrappedValue.length; i++) if (!wrappedValue[i] || typeof wrappedValue[i] !== "object" || !SHADOW_REQUIRED_PROPERTIES.every((property) => property in wrappedValue[i])) report({
1808
- messageId: ERROR$3,
1809
- node,
1810
- filename
1811
- });
1812
- else for (const key of Object.keys(wrappedValue[i])) if (![...SHADOW_REQUIRED_PROPERTIES, "inset"].includes(key)) report({
1813
- messageId: ERROR_INVALID_PROP$2,
1814
- data: { key: JSON.stringify(key) },
1815
- node: getObjMember(node.type === "Array" ? node.elements[i].value : node, key),
1816
- filename
1817
- });
1818
- }
1819
1460
  }
1820
- }
1821
- };
1822
- var valid_shadow_default = rule$4;
1823
-
1824
- //#endregion
1825
- //#region src/lint/plugin-core/rules/valid-string.ts
1826
- const VALID_STRING = "core/valid-string";
1827
- const ERROR$2 = "ERROR";
1828
- const rule$3 = {
1829
- meta: {
1830
- messages: { [ERROR$2]: "Must be a string." },
1831
- docs: {
1832
- description: "Require string tokens to follow the Terrazzo extension.",
1833
- url: docsLink(VALID_STRING)
1834
- }
1835
- },
1836
- defaultOptions: {},
1837
- create({ tokens, report }) {
1838
- for (const t of Object.values(tokens)) {
1839
- if (t.aliasOf || !t.originalValue || t.$type !== "string") continue;
1840
- validateString(t.originalValue.$value, {
1841
- node: getObjMember(t.source.node, "$value"),
1842
- filename: t.source.filename
1461
+ function validateBorder(value, { node, filename }) {
1462
+ if (!value || typeof value !== "object" || !BORDER_REQUIRED_PROPERTIES.every((property) => property in value)) report({
1463
+ messageId: ERROR$1,
1464
+ filename,
1465
+ node
1466
+ });
1467
+ else for (const key of Object.keys(value)) if (!BORDER_REQUIRED_PROPERTIES.includes(key)) report({
1468
+ messageId: ERROR_INVALID_PROP$3,
1469
+ data: { key: JSON.stringify(key) },
1470
+ node: getObjMember(node, key) ?? node,
1471
+ filename
1843
1472
  });
1844
- function validateString(value, { node, filename }) {
1845
- if (typeof value !== "string") report({
1846
- messageId: ERROR$2,
1847
- node,
1848
- filename
1849
- });
1850
- }
1851
1473
  }
1852
1474
  }
1853
1475
  };
1854
- var valid_string_default = rule$3;
1855
1476
 
1856
1477
  //#endregion
1857
- //#region src/lint/plugin-core/rules/valid-stroke-style.ts
1858
- const VALID_STROKE_STYLE = "core/valid-stroke-style";
1859
- const ERROR_STR = "ERROR_STR";
1860
- const ERROR_OBJ = "ERROR_OBJ";
1861
- const ERROR_LINE_CAP = "ERROR_LINE_CAP";
1862
- const ERROR_INVALID_PROP$1 = "ERROR_INVALID_PROP";
1863
- const rule$2 = {
1478
+ //#region src/lint/plugin-core/rules/valid-color.ts
1479
+ const VALID_COLOR = "core/valid-color";
1480
+ const ERROR_ALPHA = "ERROR_ALPHA";
1481
+ const ERROR_INVALID_COLOR = "ERROR_INVALID_COLOR";
1482
+ const ERROR_INVALID_COLOR_SPACE = "ERROR_INVALID_COLOR_SPACE";
1483
+ const ERROR_INVALID_COMPONENT_LENGTH = "ERROR_INVALID_COMPONENT_LENGTH";
1484
+ const ERROR_INVALID_HEX8 = "ERROR_INVALID_HEX8";
1485
+ const ERROR_INVALID_PROP$2 = "ERROR_INVALID_PROP";
1486
+ const ERROR_MISSING_COMPONENTS = "ERROR_MISSING_COMPONENTS";
1487
+ const ERROR_OBJ_FORMAT = "ERROR_OBJ_FORMAT";
1488
+ const ERROR_OUT_OF_RANGE = "ERROR_OUT_OF_RANGE";
1489
+ const rule$3 = {
1864
1490
  meta: {
1865
1491
  messages: {
1866
- [ERROR_STR]: `Value most be one of ${new Intl.ListFormat("en-us", { type: "disjunction" }).format(STROKE_STYLE_STRING_VALUES)}.`,
1867
- [ERROR_OBJ]: `Missing required properties: ${new Intl.ListFormat("en-us", { type: "conjunction" }).format(TRANSITION_REQUIRED_PROPERTIES)}.`,
1868
- [ERROR_LINE_CAP]: `lineCap must be one of ${new Intl.ListFormat("en-us", { type: "disjunction" }).format(STROKE_STYLE_LINE_CAP_VALUES)}.`,
1869
- [ERROR_INVALID_PROP$1]: "Unknown property: {{ key }}."
1492
+ [ERROR_ALPHA]: `Alpha {{ alpha }} not in range 0 1.`,
1493
+ [ERROR_INVALID_COLOR_SPACE]: `Invalid color space: {{ colorSpace }}. Expected ${new Intl.ListFormat("en-us", { type: "disjunction" }).format(Object.keys(COLOR_SPACE))}.`,
1494
+ [ERROR_INVALID_COLOR]: `Could not parse color {{ color }}.`,
1495
+ [ERROR_INVALID_COMPONENT_LENGTH]: "Expected {{ expected }} components, received {{ got }}.",
1496
+ [ERROR_INVALID_HEX8]: `Hex value can’t be semi-transparent.`,
1497
+ [ERROR_INVALID_PROP$2]: `Unknown property {{ key }}.`,
1498
+ [ERROR_MISSING_COMPONENTS]: "Expected components to be array of numbers, received {{ got }}.",
1499
+ [ERROR_OBJ_FORMAT]: "Migrate to the new object format, e.g. \"#ff0000\" → { \"colorSpace\": \"srgb\", \"components\": [1, 0, 0] } }",
1500
+ [ERROR_OUT_OF_RANGE]: `Invalid range for color space {{ colorSpace }}. Expected {{ range }}.`
1870
1501
  },
1871
1502
  docs: {
1872
- description: "Require strokeStyle tokens to follow the format.",
1873
- url: docsLink(VALID_STROKE_STYLE)
1503
+ description: "Require color tokens to follow the format.",
1504
+ url: docsLink(VALID_COLOR)
1874
1505
  }
1875
1506
  },
1876
- defaultOptions: {},
1877
- create({ tokens, report }) {
1507
+ defaultOptions: {
1508
+ legacyFormat: false,
1509
+ ignoreRanges: false
1510
+ },
1511
+ create({ tokens, options, report }) {
1878
1512
  for (const t of Object.values(tokens)) {
1879
1513
  if (t.aliasOf || !t.originalValue) continue;
1880
1514
  switch (t.$type) {
1881
- case "strokeStyle":
1882
- validateStrokeStyle(t.originalValue.$value, {
1515
+ case "color":
1516
+ validateColor(t.originalValue.$value, {
1883
1517
  node: getObjMember(t.source.node, "$value"),
1884
1518
  filename: t.source.filename
1885
1519
  });
1886
1520
  break;
1887
1521
  case "border":
1888
- if (t.originalValue.$value && typeof t.originalValue.$value === "object") {
1889
- const $valueNode = getObjMember(t.source.node, "$value");
1890
- if (t.originalValue.$value.style) validateStrokeStyle(t.originalValue.$value.style, {
1891
- node: getObjMember($valueNode, "style"),
1522
+ if (t.originalValue.$value.color && !isAlias(t.originalValue.$value.color)) validateColor(t.originalValue.$value.color, {
1523
+ node: getObjMember(getObjMember(t.source.node, "$value"), "color"),
1524
+ filename: t.source.filename
1525
+ });
1526
+ break;
1527
+ case "gradient": {
1528
+ const $valueNode = getObjMember(t.source.node, "$value");
1529
+ for (let i = 0; i < t.originalValue.$value.length; i++) {
1530
+ const stop = t.originalValue.$value[i];
1531
+ if (!stop.color || isAlias(stop.color)) continue;
1532
+ validateColor(stop.color, {
1533
+ node: getObjMember($valueNode.elements[i].value, "color"),
1534
+ filename: t.source.filename
1535
+ });
1536
+ }
1537
+ break;
1538
+ }
1539
+ case "shadow": {
1540
+ const $value = Array.isArray(t.originalValue.$value) ? t.originalValue.$value : [t.originalValue.$value];
1541
+ const $valueNode = getObjMember(t.source.node, "$value");
1542
+ for (let i = 0; i < $value.length; i++) {
1543
+ const layer = $value[i];
1544
+ if (!layer.color || isAlias(layer.color)) continue;
1545
+ validateColor(layer.color, {
1546
+ node: $valueNode.type === "Object" ? getObjMember($valueNode, "color") : getObjMember($valueNode.elements[i].value, "color"),
1892
1547
  filename: t.source.filename
1893
1548
  });
1894
1549
  }
1895
1550
  break;
1551
+ }
1896
1552
  }
1897
- function validateStrokeStyle(value, { node, filename }) {
1898
- if (typeof value === "string") {
1899
- if (!isAlias(value) && !STROKE_STYLE_STRING_VALUES.includes(value)) {
1553
+ function validateColor(value, { node, filename }) {
1554
+ if (!value) report({
1555
+ messageId: ERROR_INVALID_COLOR,
1556
+ data: { color: JSON.stringify(value) },
1557
+ node,
1558
+ filename
1559
+ });
1560
+ else if (typeof value === "object") {
1561
+ for (const key of Object.keys(value)) if (![
1562
+ "colorSpace",
1563
+ "components",
1564
+ "channels",
1565
+ "hex",
1566
+ "alpha"
1567
+ ].includes(key)) report({
1568
+ messageId: ERROR_INVALID_PROP$2,
1569
+ data: { key: JSON.stringify(key) },
1570
+ node: getObjMember(node, key) ?? node,
1571
+ filename
1572
+ });
1573
+ const colorSpace = "colorSpace" in value && typeof value.colorSpace === "string" ? value.colorSpace : void 0;
1574
+ const csData = COLOR_SPACE[colorSpace] || void 0;
1575
+ if (!("colorSpace" in value) || !csData) {
1900
1576
  report({
1901
- messageId: ERROR_STR,
1902
- node,
1577
+ messageId: ERROR_INVALID_COLOR_SPACE,
1578
+ data: { colorSpace },
1579
+ node: getObjMember(node, "colorSpace") ?? node,
1903
1580
  filename
1904
1581
  });
1905
1582
  return;
1906
1583
  }
1907
- } else if (value && typeof value === "object") {
1908
- if (!STROKE_STYLE_OBJECT_REQUIRED_PROPERTIES.every((property) => property in value)) report({
1909
- messageId: ERROR_OBJ,
1910
- node,
1911
- filename
1912
- });
1913
- if (!Array.isArray(value.dashArray)) report({
1914
- messageId: ERROR_OBJ,
1915
- node: getObjMember(node, "dashArray"),
1584
+ const components = "components" in value ? value.components : void 0;
1585
+ if (Array.isArray(components)) {
1586
+ const coords = Object.values(csData.coords);
1587
+ if (components?.length === coords.length) for (let i = 0; i < components.length; i++) {
1588
+ const range = coords[i]?.range ?? coords[i]?.refRange;
1589
+ if (!Number.isFinite(components[i]) || components[i] < (range?.[0] ?? -Infinity) || components[i] > (range?.[1] ?? Infinity)) {
1590
+ if (!(colorSpace === "hsl" && components[0] === null) && !(colorSpace === "hwb" && components[0] === null) && !(colorSpace === "lch" && components[2] === null) && !(colorSpace === "oklch" && components[2] === null)) report({
1591
+ messageId: ERROR_OUT_OF_RANGE,
1592
+ data: {
1593
+ colorSpace,
1594
+ range: `[${range?.[0]}–${range?.[1]}]`
1595
+ },
1596
+ node: getObjMember(node, "components") ?? node,
1597
+ filename
1598
+ });
1599
+ }
1600
+ }
1601
+ else report({
1602
+ messageId: ERROR_INVALID_COMPONENT_LENGTH,
1603
+ data: {
1604
+ expected: coords.length ?? 0,
1605
+ got: components?.length
1606
+ },
1607
+ node: getObjMember(node, "components") ?? node,
1608
+ filename
1609
+ });
1610
+ } else report({
1611
+ messageId: ERROR_MISSING_COMPONENTS,
1612
+ data: { got: JSON.stringify(components) },
1613
+ node: getObjMember(node, "components") ?? node,
1916
1614
  filename
1917
1615
  });
1918
- if (!STROKE_STYLE_LINE_CAP_VALUES.includes(value.lineCap)) report({
1919
- messageId: ERROR_OBJ,
1920
- node: getObjMember(node, "lineCap"),
1616
+ const alpha = "alpha" in value ? value.alpha : void 0;
1617
+ if (alpha !== void 0 && (typeof alpha !== "number" || alpha < 0 || alpha > 1)) report({
1618
+ messageId: ERROR_ALPHA,
1619
+ data: { alpha },
1620
+ node: getObjMember(node, "alpha") ?? node,
1921
1621
  filename
1922
1622
  });
1923
- for (const key of Object.keys(value)) if (!["dashArray", "lineCap"].includes(key)) report({
1924
- messageId: ERROR_INVALID_PROP$1,
1925
- data: { key: JSON.stringify(key) },
1926
- node: getObjMember(node, key),
1623
+ const hex = "hex" in value ? value.hex : void 0;
1624
+ if (hex) {
1625
+ let color;
1626
+ try {
1627
+ color = parseColor(hex);
1628
+ } catch {
1629
+ report({
1630
+ messageId: ERROR_INVALID_COLOR,
1631
+ data: { color: hex },
1632
+ node: getObjMember(node, "hex") ?? node,
1633
+ filename
1634
+ });
1635
+ return;
1636
+ }
1637
+ if (color.alpha !== 1) report({
1638
+ messageId: ERROR_INVALID_HEX8,
1639
+ data: { color: hex },
1640
+ node: getObjMember(node, "hex") ?? node,
1641
+ filename
1642
+ });
1643
+ }
1644
+ } else if (typeof value === "string") {
1645
+ if (isAlias(value)) return;
1646
+ if (!options.legacyFormat) report({
1647
+ messageId: ERROR_OBJ_FORMAT,
1648
+ data: { color: JSON.stringify(value) },
1649
+ node,
1927
1650
  filename
1928
1651
  });
1652
+ else try {
1653
+ parseColor(value);
1654
+ } catch {
1655
+ report({
1656
+ messageId: ERROR_INVALID_COLOR,
1657
+ data: { color: JSON.stringify(value) },
1658
+ node,
1659
+ filename
1660
+ });
1661
+ }
1929
1662
  } else report({
1930
- messageId: ERROR_OBJ,
1663
+ messageId: ERROR_INVALID_COLOR,
1931
1664
  node,
1932
1665
  filename
1933
1666
  });
@@ -1935,92 +1668,316 @@ const rule$2 = {
1935
1668
  }
1936
1669
  }
1937
1670
  };
1938
- var valid_stroke_style_default = rule$2;
1939
1671
 
1940
1672
  //#endregion
1941
- //#region src/lint/plugin-core/rules/valid-transition.ts
1942
- const VALID_TRANSITION = "core/valid-transition";
1943
- const ERROR$1 = "ERROR";
1944
- const ERROR_INVALID_PROP = "ERROR_INVALID_PROP";
1945
- const rule$1 = {
1673
+ //#region src/lint/plugin-core/rules/valid-cubic-bezier.ts
1674
+ const VALID_CUBIC_BEZIER = "core/valid-cubic-bezier";
1675
+ const ERROR = "ERROR";
1676
+ const ERROR_X = "ERROR_X";
1677
+ const ERROR_Y = "ERROR_Y";
1678
+ const rule$2 = {
1946
1679
  meta: {
1947
1680
  messages: {
1948
- [ERROR$1]: `Missing required properties: ${new Intl.ListFormat("en-us", { type: "conjunction" }).format(TRANSITION_REQUIRED_PROPERTIES)}.`,
1949
- [ERROR_INVALID_PROP]: "Unknown property: {{ key }}."
1681
+ [ERROR]: "Expected [number, number, number, number].",
1682
+ [ERROR_X]: "x values must be between 0-1.",
1683
+ [ERROR_Y]: "y values must be a valid number."
1950
1684
  },
1951
1685
  docs: {
1952
- description: "Require transition tokens to follow the format.",
1953
- url: docsLink(VALID_TRANSITION)
1686
+ description: "Require cubicBezier tokens to follow the format.",
1687
+ url: docsLink(VALID_CUBIC_BEZIER)
1954
1688
  }
1955
1689
  },
1956
1690
  defaultOptions: {},
1957
1691
  create({ tokens, report }) {
1958
1692
  for (const t of Object.values(tokens)) {
1959
- if (t.aliasOf || !t.originalValue || t.$type !== "transition") continue;
1960
- validateTransition(t.originalValue.$value, {
1961
- node: getObjMember(t.source.node, "$value"),
1962
- filename: t.source.filename
1963
- });
1693
+ if (t.aliasOf || !t.originalValue) continue;
1694
+ switch (t.$type) {
1695
+ case "cubicBezier":
1696
+ validateCubicBezier(t.originalValue.$value, {
1697
+ node: getObjMember(t.source.node, "$value"),
1698
+ filename: t.source.filename
1699
+ });
1700
+ break;
1701
+ case "transition": if (typeof t.originalValue.$value === "object" && t.originalValue.$value.timingFunction && !isAlias(t.originalValue.$value.timingFunction)) {
1702
+ const $valueNode = getObjMember(t.source.node, "$value");
1703
+ validateCubicBezier(t.originalValue.$value.timingFunction, {
1704
+ node: getObjMember($valueNode, "timingFunction"),
1705
+ filename: t.source.filename
1706
+ });
1707
+ }
1708
+ }
1709
+ function validateCubicBezier(value, { node, filename }) {
1710
+ if (Array.isArray(value) && value.length === 4) {
1711
+ for (const pos of [0, 2]) {
1712
+ if (isAlias(value[pos]) || isPure$ref(value[pos])) continue;
1713
+ if (!(value[pos] >= 0 && value[pos] <= 1)) report({
1714
+ messageId: ERROR_X,
1715
+ node: node.elements[pos],
1716
+ filename
1717
+ });
1718
+ }
1719
+ for (const pos of [1, 3]) {
1720
+ if (isAlias(value[pos]) || isPure$ref(value[pos])) continue;
1721
+ if (typeof value[pos] !== "number") report({
1722
+ messageId: ERROR_Y,
1723
+ node: node.elements[pos],
1724
+ filename
1725
+ });
1726
+ }
1727
+ } else report({
1728
+ messageId: ERROR,
1729
+ node,
1730
+ filename
1731
+ });
1732
+ }
1964
1733
  }
1965
- function validateTransition(value, { node, filename }) {
1966
- if (!value || typeof value !== "object" || !TRANSITION_REQUIRED_PROPERTIES.every((property) => property in value)) report({
1967
- messageId: ERROR$1,
1968
- node,
1969
- filename
1970
- });
1971
- else for (const key of Object.keys(value)) if (!TRANSITION_REQUIRED_PROPERTIES.includes(key)) report({
1972
- messageId: ERROR_INVALID_PROP,
1973
- data: { key: JSON.stringify(key) },
1974
- node: getObjMember(node, key),
1975
- filename
1976
- });
1734
+ }
1735
+ };
1736
+
1737
+ //#endregion
1738
+ //#region src/lint/plugin-core/rules/valid-dimension.ts
1739
+ const VALID_DIMENSION = "core/valid-dimension";
1740
+ const ERROR_FORMAT$1 = "ERROR_FORMAT";
1741
+ const ERROR_INVALID_PROP$1 = "ERROR_INVALID_PROP";
1742
+ const ERROR_LEGACY$1 = "ERROR_LEGACY";
1743
+ const ERROR_UNIT$1 = "ERROR_UNIT";
1744
+ const ERROR_VALUE$1 = "ERROR_VALUE";
1745
+ const rule$1 = {
1746
+ meta: {
1747
+ messages: {
1748
+ [ERROR_FORMAT$1]: "Invalid dimension: {{ value }}. Expected object with \"value\" and \"unit\".",
1749
+ [ERROR_LEGACY$1]: "Migrate to the new object format: { \"value\": 10, \"unit\": \"px\" }.",
1750
+ [ERROR_UNIT$1]: "Unit {{ unit }} not allowed. Expected {{ allowed }}.",
1751
+ [ERROR_INVALID_PROP$1]: "Unknown property {{ key }}.",
1752
+ [ERROR_VALUE$1]: "Expected number, received {{ value }}."
1753
+ },
1754
+ docs: {
1755
+ description: "Require dimension tokens to follow the format",
1756
+ url: docsLink(VALID_DIMENSION)
1757
+ }
1758
+ },
1759
+ defaultOptions: {
1760
+ legacyFormat: false,
1761
+ allowedUnits: [
1762
+ "px",
1763
+ "em",
1764
+ "rem"
1765
+ ]
1766
+ },
1767
+ create({ tokens, options, report }) {
1768
+ for (const t of Object.values(tokens)) {
1769
+ if (t.aliasOf || !t.originalValue) continue;
1770
+ switch (t.$type) {
1771
+ case "dimension":
1772
+ validateDimension(t.originalValue.$value, {
1773
+ node: getObjMember(t.source.node, "$value"),
1774
+ filename: t.source.filename
1775
+ });
1776
+ break;
1777
+ case "strokeStyle":
1778
+ if (typeof t.originalValue.$value === "object" && Array.isArray(t.originalValue.$value.dashArray)) {
1779
+ const dashArray = getObjMember(getObjMember(t.source.node, "$value"), "dashArray");
1780
+ for (let i = 0; i < t.originalValue.$value.dashArray.length; i++) {
1781
+ if (isAlias(t.originalValue.$value.dashArray[i])) continue;
1782
+ validateDimension(t.originalValue.$value.dashArray[i], {
1783
+ node: dashArray.elements[i].value,
1784
+ filename: t.source.filename
1785
+ });
1786
+ }
1787
+ }
1788
+ break;
1789
+ case "border": {
1790
+ const $valueNode = getObjMember(t.source.node, "$value");
1791
+ if (typeof t.originalValue.$value === "object") {
1792
+ if (t.originalValue.$value.width && !isAlias(t.originalValue.$value.width)) validateDimension(t.originalValue.$value.width, {
1793
+ node: getObjMember($valueNode, "width"),
1794
+ filename: t.source.filename
1795
+ });
1796
+ if (typeof t.originalValue.$value.style === "object" && Array.isArray(t.originalValue.$value.style.dashArray)) {
1797
+ const dashArray = getObjMember(getObjMember($valueNode, "style"), "dashArray");
1798
+ for (let i = 0; i < t.originalValue.$value.style.dashArray.length; i++) {
1799
+ if (isAlias(t.originalValue.$value.style.dashArray[i])) continue;
1800
+ validateDimension(t.originalValue.$value.style.dashArray[i], {
1801
+ node: dashArray.elements[i].value,
1802
+ filename: t.source.filename
1803
+ });
1804
+ }
1805
+ }
1806
+ }
1807
+ break;
1808
+ }
1809
+ case "shadow":
1810
+ if (t.originalValue.$value && typeof t.originalValue.$value === "object") {
1811
+ const $valueNode = getObjMember(t.source.node, "$value");
1812
+ const valueArray = Array.isArray(t.originalValue.$value) ? t.originalValue.$value : [t.originalValue.$value];
1813
+ for (let i = 0; i < valueArray.length; i++) {
1814
+ const node = $valueNode.type === "Array" ? $valueNode.elements[i].value : $valueNode;
1815
+ for (const property of [
1816
+ "offsetX",
1817
+ "offsetY",
1818
+ "blur",
1819
+ "spread"
1820
+ ]) {
1821
+ if (isAlias(valueArray[i][property])) continue;
1822
+ validateDimension(valueArray[i][property], {
1823
+ node: getObjMember(node, property),
1824
+ filename: t.source.filename
1825
+ });
1826
+ }
1827
+ }
1828
+ }
1829
+ break;
1830
+ case "typography": {
1831
+ const $valueNode = getObjMember(t.source.node, "$value");
1832
+ if (typeof t.originalValue.$value === "object") {
1833
+ for (const property of [
1834
+ "fontSize",
1835
+ "lineHeight",
1836
+ "letterSpacing"
1837
+ ]) if (property in t.originalValue.$value) {
1838
+ if (isAlias(t.originalValue.$value[property]) || property === "lineHeight" && typeof t.originalValue.$value[property] === "number") continue;
1839
+ validateDimension(t.originalValue.$value[property], {
1840
+ node: getObjMember($valueNode, property),
1841
+ filename: t.source.filename
1842
+ });
1843
+ }
1844
+ }
1845
+ break;
1846
+ }
1847
+ }
1848
+ function validateDimension(value, { node, filename }) {
1849
+ if (value && typeof value === "object") {
1850
+ for (const key of Object.keys(value)) if (!["value", "unit"].includes(key)) report({
1851
+ messageId: ERROR_INVALID_PROP$1,
1852
+ data: { key: JSON.stringify(key) },
1853
+ node: getObjMember(node, key) ?? node,
1854
+ filename
1855
+ });
1856
+ const { unit, value: numValue } = value;
1857
+ if (!("value" in value || "unit" in value)) {
1858
+ report({
1859
+ messageId: ERROR_FORMAT$1,
1860
+ data: { value },
1861
+ node,
1862
+ filename
1863
+ });
1864
+ return;
1865
+ }
1866
+ if (!options.allowedUnits.includes(unit)) report({
1867
+ messageId: ERROR_UNIT$1,
1868
+ data: {
1869
+ unit,
1870
+ allowed: new Intl.ListFormat("en-us", { type: "disjunction" }).format(options.allowedUnits)
1871
+ },
1872
+ node: getObjMember(node, "unit") ?? node,
1873
+ filename
1874
+ });
1875
+ if (!Number.isFinite(numValue)) report({
1876
+ messageId: ERROR_VALUE$1,
1877
+ data: { value },
1878
+ node: getObjMember(node, "value") ?? node,
1879
+ filename
1880
+ });
1881
+ } else if (typeof value === "string" && !options.legacyFormat) report({
1882
+ messageId: ERROR_LEGACY$1,
1883
+ node,
1884
+ filename
1885
+ });
1886
+ else report({
1887
+ messageId: ERROR_FORMAT$1,
1888
+ data: { value },
1889
+ node,
1890
+ filename
1891
+ });
1892
+ }
1977
1893
  }
1978
1894
  }
1979
1895
  };
1980
- var valid_transition_default = rule$1;
1981
1896
 
1982
1897
  //#endregion
1983
- //#region src/lint/plugin-core/rules/valid-typography.ts
1984
- const VALID_TYPOGRAPHY = "core/valid-typography";
1985
- const ERROR = "ERROR";
1986
- const ERROR_MISSING = "ERROR_MISSING";
1898
+ //#region src/lint/plugin-core/rules/valid-duration.ts
1899
+ const VALID_DURATION = "core/valid-duration";
1900
+ const ERROR_FORMAT = "ERROR_FORMAT";
1901
+ const ERROR_INVALID_PROP = "ERROR_INVALID_PROP";
1902
+ const ERROR_LEGACY = "ERROR_LEGACY";
1903
+ const ERROR_UNIT = "ERROR_UNIT";
1904
+ const ERROR_VALUE = "ERROR_VALUE";
1987
1905
  const rule = {
1988
1906
  meta: {
1989
1907
  messages: {
1990
- [ERROR]: `Expected object, received {{ value }}.`,
1991
- [ERROR_MISSING]: `Missing required property "{{ property }}".`
1908
+ [ERROR_FORMAT]: "Migrate to the new object format: { \"value\": 2, \"unit\": \"ms\" }.",
1909
+ [ERROR_LEGACY]: "Migrate to the new object format: { \"value\": 10, \"unit\": \"px\" }.",
1910
+ [ERROR_INVALID_PROP]: "Unknown property: {{ key }}.",
1911
+ [ERROR_UNIT]: "Unknown unit {{ unit }}. Expected \"ms\" or \"s\".",
1912
+ [ERROR_VALUE]: "Expected number, received {{ value }}."
1992
1913
  },
1993
1914
  docs: {
1994
- description: "Require typography tokens to follow the format.",
1995
- url: docsLink(VALID_TYPOGRAPHY)
1915
+ description: "Require duration tokens to follow the format",
1916
+ url: docsLink(VALID_DURATION)
1996
1917
  }
1997
1918
  },
1998
- defaultOptions: { requiredProperties: [
1999
- "fontFamily",
2000
- "fontSize",
2001
- "fontWeight",
2002
- "letterSpacing",
2003
- "lineHeight"
2004
- ] },
1919
+ defaultOptions: {
1920
+ legacyFormat: false,
1921
+ unknownUnits: false
1922
+ },
2005
1923
  create({ tokens, options, report }) {
2006
- const isIgnored = options.ignore ? wcmatch(options.ignore) : () => false;
2007
1924
  for (const t of Object.values(tokens)) {
2008
- if (t.aliasOf || !t.originalValue || t.$type !== "typography" || isIgnored(t.id)) continue;
2009
- validateTypography(t.originalValue.$value, {
2010
- node: getObjMember(t.source.node, "$value"),
2011
- filename: t.source.filename
2012
- });
2013
- function validateTypography(value, { node, filename }) {
1925
+ if (t.aliasOf || !t.originalValue) continue;
1926
+ switch (t.$type) {
1927
+ case "duration":
1928
+ validateDuration(t.originalValue.$value, {
1929
+ node: getObjMember(t.source.node, "$value"),
1930
+ filename: t.source.filename
1931
+ });
1932
+ break;
1933
+ case "transition":
1934
+ if (typeof t.originalValue.$value === "object") {
1935
+ const $valueNode = getObjMember(t.source.node, "$value");
1936
+ for (const property of ["duration", "delay"]) if (t.originalValue.$value[property] && !isAlias(t.originalValue.$value[property])) validateDuration(t.originalValue.$value[property], {
1937
+ node: getObjMember($valueNode, property),
1938
+ filename: t.source.filename
1939
+ });
1940
+ }
1941
+ break;
1942
+ }
1943
+ function validateDuration(value, { node, filename }) {
2014
1944
  if (value && typeof value === "object") {
2015
- for (const property of options.requiredProperties) if (!(property in value)) report({
2016
- messageId: ERROR_MISSING,
2017
- data: { property },
2018
- node,
1945
+ for (const key of Object.keys(value)) if (!["value", "unit"].includes(key)) report({
1946
+ messageId: ERROR_INVALID_PROP,
1947
+ data: { key: JSON.stringify(key) },
1948
+ node: getObjMember(node, key) ?? node,
2019
1949
  filename
2020
1950
  });
2021
- } else report({
2022
- messageId: ERROR,
2023
- data: { value: JSON.stringify(value) },
1951
+ const { unit, value: numValue } = value;
1952
+ if (!("value" in value || "unit" in value)) {
1953
+ report({
1954
+ messageId: ERROR_FORMAT,
1955
+ data: { value },
1956
+ node,
1957
+ filename
1958
+ });
1959
+ return;
1960
+ }
1961
+ if (!options.unknownUnits && !["ms", "s"].includes(unit)) report({
1962
+ messageId: ERROR_UNIT,
1963
+ data: { unit },
1964
+ node: getObjMember(node, "unit") ?? node,
1965
+ filename
1966
+ });
1967
+ if (!Number.isFinite(numValue)) report({
1968
+ messageId: ERROR_VALUE,
1969
+ data: { value },
1970
+ node: getObjMember(node, "value") ?? node,
1971
+ filename
1972
+ });
1973
+ } else if (typeof value === "string" && !options.legacyFormat) report({
1974
+ messageId: ERROR_FORMAT,
1975
+ node,
1976
+ filename
1977
+ });
1978
+ else report({
1979
+ messageId: ERROR_FORMAT,
1980
+ data: { value },
2024
1981
  node,
2025
1982
  filename
2026
1983
  });
@@ -2028,38 +1985,37 @@ const rule = {
2028
1985
  }
2029
1986
  }
2030
1987
  };
2031
- var valid_typography_default = rule;
2032
1988
 
2033
1989
  //#endregion
2034
1990
  //#region src/lint/plugin-core/index.ts
2035
1991
  const ALL_RULES = {
2036
- [VALID_COLOR]: valid_color_default,
2037
- [VALID_DIMENSION]: valid_dimension_default,
2038
- [VALID_FONT_FAMILY]: valid_font_family_default,
2039
- [VALID_FONT_WEIGHT]: valid_font_weight_default,
2040
- [VALID_DURATION]: valid_duration_default,
2041
- [VALID_CUBIC_BEZIER]: valid_cubic_bezier_default,
2042
- [VALID_NUMBER]: valid_number_default,
2043
- [VALID_LINK]: valid_link_default,
2044
- [VALID_BOOLEAN]: valid_boolean_default,
2045
- [VALID_STRING]: valid_string_default,
2046
- [VALID_STROKE_STYLE]: valid_stroke_style_default,
2047
- [VALID_BORDER]: valid_border_default,
2048
- [VALID_TRANSITION]: valid_transition_default,
2049
- [VALID_SHADOW]: valid_shadow_default,
2050
- [VALID_GRADIENT]: valid_gradient_default,
2051
- [VALID_TYPOGRAPHY]: valid_typography_default,
2052
- [COLORSPACE$1]: colorspace_default,
2053
- [CONSISTENT_NAMING]: consistent_naming_default,
2054
- [DESCRIPTIONS]: descriptions_default,
2055
- [DUPLICATE_VALUES]: duplicate_values_default,
2056
- [MAX_GAMUT]: max_gamut_default,
2057
- [REQUIRED_CHILDREN]: required_children_default,
2058
- [REQUIRED_MODES]: required_modes_default,
2059
- [REQUIRED_TYPE]: required_type_default,
2060
- [REQUIRED_TYPOGRAPHY_PROPERTIES]: required_typography_properties_default,
2061
- [A11Y_MIN_CONTRAST]: a11y_min_contrast_default,
2062
- [A11Y_MIN_FONT_SIZE]: a11y_min_font_size_default
1992
+ [VALID_COLOR]: rule$3,
1993
+ [VALID_DIMENSION]: rule$1,
1994
+ [VALID_FONT_FAMILY]: rule$15,
1995
+ [VALID_FONT_WEIGHT]: rule$14,
1996
+ [VALID_DURATION]: rule,
1997
+ [VALID_CUBIC_BEZIER]: rule$2,
1998
+ [VALID_NUMBER]: rule$11,
1999
+ [VALID_LINK]: rule$12,
2000
+ [VALID_BOOLEAN]: rule$5,
2001
+ [VALID_STRING]: rule$9,
2002
+ [VALID_STROKE_STYLE]: rule$8,
2003
+ [VALID_BORDER]: rule$4,
2004
+ [VALID_TRANSITION]: rule$7,
2005
+ [VALID_SHADOW]: rule$10,
2006
+ [VALID_GRADIENT]: rule$13,
2007
+ [VALID_TYPOGRAPHY]: rule$6,
2008
+ [COLORSPACE]: rule$24,
2009
+ [CONSISTENT_NAMING]: rule$23,
2010
+ [DESCRIPTIONS]: rule$22,
2011
+ [DUPLICATE_VALUES]: rule$21,
2012
+ [MAX_GAMUT]: rule$20,
2013
+ [REQUIRED_CHILDREN]: rule$19,
2014
+ [REQUIRED_MODES]: rule$18,
2015
+ [REQUIRED_TYPE]: rule$17,
2016
+ [REQUIRED_TYPOGRAPHY_PROPERTIES]: rule$16,
2017
+ [A11Y_MIN_CONTRAST]: rule$26,
2018
+ [A11Y_MIN_FONT_SIZE]: rule$25
2063
2019
  };
2064
2020
  function coreLintPlugin() {
2065
2021
  return {
@@ -2245,13 +2201,13 @@ function normalizeLint({ config, logger }) {
2245
2201
  });
2246
2202
  continue;
2247
2203
  }
2248
- for (const rule$27 of Object.keys(pluginRules)) {
2249
- if (allRules.get(rule$27) && allRules.get(rule$27) !== plugin.name) logger.error({
2204
+ for (const rule of Object.keys(pluginRules)) {
2205
+ if (allRules.get(rule) && allRules.get(rule) !== plugin.name) logger.error({
2250
2206
  group: "config",
2251
2207
  label: `plugin › ${plugin.name}`,
2252
- message: `Duplicate rule ${rule$27} already registered by plugin ${allRules.get(rule$27)}`
2208
+ message: `Duplicate rule ${rule} already registered by plugin ${allRules.get(rule)}`
2253
2209
  });
2254
- allRules.set(rule$27, plugin.name);
2210
+ allRules.set(rule, plugin.name);
2255
2211
  }
2256
2212
  }
2257
2213
  for (const id of Object.keys(config.lint.rules)) {
@@ -2334,13 +2290,13 @@ async function lintRunner({ tokens, filename, config = {}, sources, logger }) {
2334
2290
  for (const plugin of plugins) if (typeof plugin.lint === "function") {
2335
2291
  const s = performance.now();
2336
2292
  const linter = plugin.lint();
2337
- await Promise.all(Object.entries(linter).map(async ([id, rule$27]) => {
2293
+ await Promise.all(Object.entries(linter).map(async ([id, rule]) => {
2338
2294
  if (!(id in lint.rules) || lint.rules[id] === null) return;
2339
2295
  const unusedLintRuleI = unusedLintRules.indexOf(id);
2340
2296
  if (unusedLintRuleI !== -1) unusedLintRules.splice(unusedLintRuleI, 1);
2341
2297
  const [severity, options] = lint.rules[id];
2342
2298
  if (severity === "off") return;
2343
- await rule$27.create({
2299
+ await rule.create({
2344
2300
  id,
2345
2301
  report(descriptor) {
2346
2302
  let message = "";
@@ -2351,12 +2307,12 @@ async function lintRunner({ tokens, filename, config = {}, sources, logger }) {
2351
2307
  });
2352
2308
  if (descriptor.message) message = descriptor.message;
2353
2309
  else {
2354
- if (!(descriptor.messageId in (rule$27.meta?.messages ?? {}))) logger.error({
2310
+ if (!(descriptor.messageId in (rule.meta?.messages ?? {}))) logger.error({
2355
2311
  group: "lint",
2356
2312
  label: `${plugin.name} › lint › ${id}`,
2357
2313
  message: `messageId "${descriptor.messageId}" does not exist`
2358
2314
  });
2359
- message = rule$27.meta?.messages?.[descriptor.messageId] ?? "";
2315
+ message = rule.meta?.messages?.[descriptor.messageId] ?? "";
2360
2316
  }
2361
2317
  if (descriptor.data && typeof descriptor.data === "object") for (const [k, v] of Object.entries(descriptor.data)) {
2362
2318
  const formatted = [
@@ -2380,7 +2336,7 @@ async function lintRunner({ tokens, filename, config = {}, sources, logger }) {
2380
2336
  tokens,
2381
2337
  filename,
2382
2338
  sources,
2383
- options: merge(rule$27.meta?.defaultOptions ?? [], rule$27.defaultOptions ?? [], options)
2339
+ options: merge(rule.meta?.defaultOptions ?? [], rule.defaultOptions ?? [], options)
2384
2340
  });
2385
2341
  }));
2386
2342
  logger.debug({
@@ -2840,7 +2796,9 @@ const EXPECTED_NESTED_ALIAS = {
2840
2796
  fontWeight: ["fontWeight"],
2841
2797
  fontSize: ["dimension"],
2842
2798
  lineHeight: ["dimension", "number"],
2843
- letterSpacing: ["dimension"]
2799
+ letterSpacing: ["dimension"],
2800
+ paragraphSpacing: ["dimension", "string"],
2801
+ wordSpacing: ["dimension", "string"]
2844
2802
  }
2845
2803
  };
2846
2804
  /**
@@ -3737,12 +3695,12 @@ function createResolver(resolverSource, { config, logger, sources }) {
3737
3695
  break;
3738
3696
  case "modifier": {
3739
3697
  const context = input[item.name];
3740
- const sources$1 = item.contexts[context];
3741
- if (!sources$1) logger.error({
3698
+ const sources = item.contexts[context];
3699
+ if (!sources) logger.error({
3742
3700
  group: "resolver",
3743
3701
  message: `Modifier ${item.name} has no context ${JSON.stringify(context)}.`
3744
3702
  });
3745
- for (const s of sources$1 ?? []) tokensRaw = merge(tokensRaw, s);
3703
+ for (const s of sources ?? []) tokensRaw = merge(tokensRaw, s);
3746
3704
  break;
3747
3705
  }
3748
3706
  }