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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -912,262 +912,134 @@ const rule$16 = {
912
912
  var required_typography_properties_default = rule$16;
913
913
 
914
914
  //#endregion
915
- //#region src/lint/plugin-core/rules/valid-boolean.ts
916
- const VALID_BOOLEAN = "core/valid-boolean";
915
+ //#region src/lint/plugin-core/rules/valid-font-family.ts
916
+ const VALID_FONT_FAMILY = "core/valid-font-family";
917
917
  const ERROR$9 = "ERROR";
918
918
  const rule$15 = {
919
919
  meta: {
920
- messages: { [ERROR$9]: "Must be a boolean." },
920
+ messages: { [ERROR$9]: "Must be a string, or array of strings." },
921
921
  docs: {
922
- description: "Require boolean tokens to follow the Terrazzo extension.",
923
- url: docsLink(VALID_BOOLEAN)
922
+ description: "Require fontFamily tokens to follow the format.",
923
+ url: docsLink(VALID_FONT_FAMILY)
924
924
  }
925
925
  },
926
926
  defaultOptions: {},
927
927
  create({ tokens, report }) {
928
928
  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({
929
+ if (t.aliasOf || !t.originalValue) continue;
930
+ switch (t.$type) {
931
+ case "fontFamily":
932
+ validateFontFamily(t.originalValue.$value, {
933
+ node: getObjMember(t.source.node, "$value"),
934
+ filename: t.source.filename
935
+ });
936
+ break;
937
+ case "typography":
938
+ if (typeof t.originalValue.$value === "object" && t.originalValue.$value.fontFamily) {
939
+ if (t.partialAliasOf?.fontFamily) continue;
940
+ const properties = getObjMembers(getObjMember(t.source.node, "$value"));
941
+ validateFontFamily(t.originalValue.$value.fontFamily, {
942
+ node: properties.fontFamily,
943
+ filename: t.source.filename
944
+ });
945
+ }
946
+ break;
947
+ }
948
+ function validateFontFamily(value, { node, filename }) {
949
+ if (typeof value === "string") {
950
+ if (!value) report({
951
+ messageId: ERROR$9,
952
+ node,
953
+ filename
954
+ });
955
+ } else if (Array.isArray(value)) {
956
+ if (!value.every((v) => v && typeof v === "string")) report({
957
+ messageId: ERROR$9,
958
+ node,
959
+ filename
960
+ });
961
+ } else report({
936
962
  messageId: ERROR$9,
937
- filename,
938
- node
963
+ node,
964
+ filename
939
965
  });
940
966
  }
941
967
  }
942
968
  }
943
969
  };
944
- var valid_boolean_default = rule$15;
970
+ var valid_font_family_default = rule$15;
945
971
 
946
972
  //#endregion
947
- //#region src/lint/plugin-core/rules/valid-border.ts
948
- const VALID_BORDER = "core/valid-border";
973
+ //#region src/lint/plugin-core/rules/valid-font-weight.ts
974
+ const VALID_FONT_WEIGHT = "core/valid-font-weight";
949
975
  const ERROR$8 = "ERROR";
950
- const ERROR_INVALID_PROP$7 = "ERROR_INVALID_PROP";
976
+ const ERROR_STYLE = "ERROR_STYLE";
951
977
  const rule$14 = {
952
978
  meta: {
953
979
  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 }}.`
980
+ [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))}.`,
981
+ [ERROR_STYLE]: "Expected style {{ style }}, received {{ value }}."
1012
982
  },
1013
983
  docs: {
1014
- description: "Require color tokens to follow the format.",
1015
- url: docsLink(VALID_COLOR)
984
+ description: "Require number tokens to follow the format.",
985
+ url: docsLink(VALID_FONT_WEIGHT)
1016
986
  }
1017
987
  },
1018
- defaultOptions: {
1019
- legacyFormat: false,
1020
- ignoreRanges: false
1021
- },
988
+ defaultOptions: { style: void 0 },
1022
989
  create({ tokens, options, report }) {
1023
990
  for (const t of Object.values(tokens)) {
1024
991
  if (t.aliasOf || !t.originalValue) continue;
1025
992
  switch (t.$type) {
1026
- case "color":
1027
- validateColor(t.originalValue.$value, {
993
+ case "fontWeight":
994
+ validateFontWeight(t.originalValue.$value, {
1028
995
  node: getObjMember(t.source.node, "$value"),
1029
996
  filename: t.source.filename
1030
997
  });
1031
998
  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"),
999
+ case "typography":
1000
+ if (typeof t.originalValue.$value === "object" && t.originalValue.$value.fontWeight) {
1001
+ if (t.partialAliasOf?.fontWeight) continue;
1002
+ const properties = getObjMembers(getObjMember(t.source.node, "$value"));
1003
+ validateFontWeight(t.originalValue.$value.fontWeight, {
1004
+ node: properties.fontWeight,
1058
1005
  filename: t.source.filename
1059
1006
  });
1060
1007
  }
1061
1008
  break;
1062
- }
1063
1009
  }
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,
1010
+ function validateFontWeight(value, { node, filename }) {
1011
+ if (typeof value === "string") {
1012
+ if (options.style === "numbers") report({
1013
+ messageId: ERROR_STYLE,
1110
1014
  data: {
1111
- expected: csData?.ranges.length,
1112
- got: components?.length ?? 0
1015
+ style: "numbers",
1016
+ value
1113
1017
  },
1114
- node: getObjMember(node, "components") ?? node,
1018
+ node,
1115
1019
  filename
1116
1020
  });
1117
- else report({
1118
- messageId: ERROR_MISSING_COMPONENTS,
1119
- data: { got: JSON.stringify(components) },
1120
- node: getObjMember(node, "components") ?? node,
1021
+ else if (!(value in FONT_WEIGHTS)) report({
1022
+ messageId: ERROR$8,
1023
+ node,
1121
1024
  filename
1122
1025
  });
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,
1026
+ } else if (typeof value === "number") {
1027
+ if (options.style === "names") report({
1028
+ messageId: ERROR_STYLE,
1029
+ data: {
1030
+ style: "names",
1031
+ value
1032
+ },
1033
+ node,
1128
1034
  filename
1129
1035
  });
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) },
1036
+ else if (!(value >= 0 && value < 1e3)) report({
1037
+ messageId: ERROR$8,
1156
1038
  node,
1157
1039
  filename
1158
1040
  });
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
1041
  } else report({
1170
- messageId: ERROR_INVALID_COLOR,
1042
+ messageId: ERROR$8,
1171
1043
  node,
1172
1044
  filename
1173
1045
  });
@@ -1175,64 +1047,96 @@ const rule$13 = {
1175
1047
  }
1176
1048
  }
1177
1049
  };
1178
- var valid_color_default = rule$13;
1050
+ var valid_font_weight_default = rule$14;
1179
1051
 
1180
1052
  //#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 = {
1053
+ //#region src/lint/plugin-core/rules/valid-gradient.ts
1054
+ const VALID_GRADIENT = "core/valid-gradient";
1055
+ const ERROR_MISSING$1 = "ERROR_MISSING";
1056
+ const ERROR_POSITION = "ERROR_POSITION";
1057
+ const ERROR_INVALID_PROP$7 = "ERROR_INVALID_PROP";
1058
+ const rule$13 = {
1187
1059
  meta: {
1188
1060
  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."
1061
+ [ERROR_MISSING$1]: "Must be an array of { color, position } objects.",
1062
+ [ERROR_POSITION]: "Expected number 0-1, received {{ value }}.",
1063
+ [ERROR_INVALID_PROP$7]: "Unknown property {{ key }}."
1192
1064
  },
1193
1065
  docs: {
1194
- description: "Require cubicBezier tokens to follow the format.",
1195
- url: docsLink(VALID_CUBIC_BEZIER)
1066
+ description: "Require gradient tokens to follow the format.",
1067
+ url: docsLink(VALID_GRADIENT)
1196
1068
  }
1197
1069
  },
1198
1070
  defaultOptions: {},
1199
1071
  create({ tokens, report }) {
1200
1072
  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
1073
+ if (t.aliasOf || !t.originalValue || t.$type !== "gradient") continue;
1074
+ validateGradient(t.originalValue.$value, {
1075
+ node: getObjMember(t.source.node, "$value"),
1076
+ filename: t.source.filename
1077
+ });
1078
+ function validateGradient(value, { node, filename }) {
1079
+ if (Array.isArray(value)) for (let i = 0; i < value.length; i++) {
1080
+ const stop = value[i];
1081
+ if (!stop || typeof stop !== "object") {
1082
+ report({
1083
+ messageId: ERROR_MISSING$1,
1084
+ node,
1085
+ filename
1086
+ });
1087
+ continue;
1088
+ }
1089
+ for (const property of GRADIENT_REQUIRED_STOP_PROPERTIES) if (!(property in stop)) report({
1090
+ messageId: ERROR_MISSING$1,
1091
+ node: node.elements[i],
1092
+ filename
1207
1093
  });
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
1094
+ for (const key of Object.keys(stop)) if (!GRADIENT_REQUIRED_STOP_PROPERTIES.includes(key)) report({
1095
+ messageId: ERROR_INVALID_PROP$7,
1096
+ data: { key: JSON.stringify(key) },
1097
+ node: node.elements[i],
1098
+ filename
1099
+ });
1100
+ if ("position" in stop && typeof stop.position !== "number" && !isAlias(stop.position)) report({
1101
+ messageId: ERROR_POSITION,
1102
+ data: { value: stop.position },
1103
+ node: getObjMember(node.elements[i].value, "position"),
1104
+ filename
1214
1105
  });
1215
1106
  }
1107
+ else report({
1108
+ messageId: ERROR_MISSING$1,
1109
+ node,
1110
+ filename
1111
+ });
1216
1112
  }
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({
1113
+ }
1114
+ }
1115
+ };
1116
+ var valid_gradient_default = rule$13;
1117
+
1118
+ //#endregion
1119
+ //#region src/lint/plugin-core/rules/valid-link.ts
1120
+ const VALID_LINK = "core/valid-link";
1121
+ const ERROR$7 = "ERROR";
1122
+ const rule$12 = {
1123
+ meta: {
1124
+ messages: { [ERROR$7]: "Must be a string." },
1125
+ docs: {
1126
+ description: "Require link tokens to follow the Terrazzo extension.",
1127
+ url: docsLink(VALID_LINK)
1128
+ }
1129
+ },
1130
+ defaultOptions: {},
1131
+ create({ tokens, report }) {
1132
+ for (const t of Object.values(tokens)) {
1133
+ if (t.aliasOf || !t.originalValue || t.$type !== "link") continue;
1134
+ validateLink(t.originalValue.$value, {
1135
+ node: getObjMember(t.source.node, "$value"),
1136
+ filename: t.source.filename
1137
+ });
1138
+ function validateLink(value, { node, filename }) {
1139
+ if (!value || typeof value !== "string") report({
1236
1140
  messageId: ERROR$7,
1237
1141
  node,
1238
1142
  filename
@@ -1241,160 +1145,44 @@ const rule$12 = {
1241
1145
  }
1242
1146
  }
1243
1147
  };
1244
- var valid_cubic_bezier_default = rule$12;
1148
+ var valid_link_default = rule$12;
1245
1149
 
1246
1150
  //#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";
1151
+ //#region src/lint/plugin-core/rules/valid-number.ts
1152
+ const VALID_NUMBER = "core/valid-number";
1153
+ const ERROR_NAN = "ERROR_NAN";
1254
1154
  const rule$11 = {
1255
1155
  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
- },
1156
+ messages: { [ERROR_NAN]: "Must be a number." },
1263
1157
  docs: {
1264
- description: "Require dimension tokens to follow the format",
1265
- url: docsLink(VALID_DIMENSION)
1158
+ description: "Require number tokens to follow the format.",
1159
+ url: docsLink(VALID_NUMBER)
1266
1160
  }
1267
1161
  },
1268
- defaultOptions: {
1269
- legacyFormat: false,
1270
- allowedUnits: [
1271
- "px",
1272
- "em",
1273
- "rem"
1274
- ]
1275
- },
1276
- create({ tokens, options, report }) {
1162
+ defaultOptions: {},
1163
+ create({ tokens, report }) {
1277
1164
  for (const t of Object.values(tokens)) {
1278
1165
  if (t.aliasOf || !t.originalValue) continue;
1279
1166
  switch (t.$type) {
1280
- case "dimension":
1281
- validateDimension(t.originalValue.$value, {
1167
+ case "number":
1168
+ validateNumber(t.originalValue.$value, {
1282
1169
  node: getObjMember(t.source.node, "$value"),
1283
1170
  filename: t.source.filename
1284
1171
  });
1285
1172
  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": {
1173
+ case "typography": {
1299
1174
  const $valueNode = getObjMember(t.source.node, "$value");
1300
1175
  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"),
1176
+ if (t.originalValue.$value.lineHeight && !isAlias(t.originalValue.$value.lineHeight) && typeof t.originalValue.$value.lineHeight !== "object") validateNumber(t.originalValue.$value.lineHeight, {
1177
+ node: getObjMember($valueNode, "lineHeight"),
1303
1178
  filename: t.source.filename
1304
1179
  });
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
1180
  }
1354
- break;
1355
1181
  }
1356
1182
  }
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 },
1183
+ function validateNumber(value, { node, filename }) {
1184
+ if (typeof value !== "number" || Number.isNaN(value)) report({
1185
+ messageId: ERROR_NAN,
1398
1186
  node,
1399
1187
  filename
1400
1188
  });
@@ -1402,150 +1190,74 @@ const rule$11 = {
1402
1190
  }
1403
1191
  }
1404
1192
  };
1405
- var valid_dimension_default = rule$11;
1193
+ var valid_number_default = rule$11;
1406
1194
 
1407
1195
  //#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";
1196
+ //#region src/lint/plugin-core/rules/valid-shadow.ts
1197
+ const VALID_SHADOW = "core/valid-shadow";
1198
+ const ERROR$6 = "ERROR";
1199
+ const ERROR_INVALID_PROP$6 = "ERROR_INVALID_PROP";
1415
1200
  const rule$10 = {
1416
1201
  meta: {
1417
1202
  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 }}."
1203
+ [ERROR$6]: `Missing required properties: ${new Intl.ListFormat("en-us", { type: "conjunction" }).format(SHADOW_REQUIRED_PROPERTIES)}.`,
1204
+ [ERROR_INVALID_PROP$6]: "Unknown property {{ key }}."
1423
1205
  },
1424
1206
  docs: {
1425
- description: "Require duration tokens to follow the format",
1426
- url: docsLink(VALID_DURATION)
1207
+ description: "Require shadow tokens to follow the format.",
1208
+ url: docsLink(VALID_SHADOW)
1427
1209
  }
1428
1210
  },
1429
- defaultOptions: {
1430
- legacyFormat: false,
1431
- unknownUnits: false
1432
- },
1433
- create({ tokens, options, report }) {
1211
+ defaultOptions: {},
1212
+ create({ tokens, report }) {
1434
1213
  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,
1214
+ if (t.aliasOf || !t.originalValue || t.$type !== "shadow") continue;
1215
+ validateShadow(t.originalValue.$value, {
1216
+ node: getObjMember(t.source.node, "$value"),
1217
+ filename: t.source.filename
1218
+ });
1219
+ function validateShadow(value, { node, filename }) {
1220
+ const wrappedValue = Array.isArray(value) ? value : [value];
1221
+ 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({
1222
+ messageId: ERROR$6,
1485
1223
  node,
1486
1224
  filename
1487
1225
  });
1488
- else report({
1489
- messageId: ERROR_FORMAT,
1490
- data: { value },
1491
- node,
1226
+ else for (const key of Object.keys(wrappedValue[i])) if (![...SHADOW_REQUIRED_PROPERTIES, "inset"].includes(key)) report({
1227
+ messageId: ERROR_INVALID_PROP$6,
1228
+ data: { key: JSON.stringify(key) },
1229
+ node: getObjMember(node.type === "Array" ? node.elements[i].value : node, key),
1492
1230
  filename
1493
1231
  });
1494
1232
  }
1495
1233
  }
1496
1234
  }
1497
1235
  };
1498
- var valid_duration_default = rule$10;
1236
+ var valid_shadow_default = rule$10;
1499
1237
 
1500
1238
  //#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";
1239
+ //#region src/lint/plugin-core/rules/valid-string.ts
1240
+ const VALID_STRING = "core/valid-string";
1241
+ const ERROR$5 = "ERROR";
1504
1242
  const rule$9 = {
1505
1243
  meta: {
1506
- messages: { [ERROR$6]: "Must be a string, or array of strings." },
1244
+ messages: { [ERROR$5]: "Must be a string." },
1507
1245
  docs: {
1508
- description: "Require fontFamily tokens to follow the format.",
1509
- url: docsLink(VALID_FONT_FAMILY)
1246
+ description: "Require string tokens to follow the Terrazzo extension.",
1247
+ url: docsLink(VALID_STRING)
1510
1248
  }
1511
1249
  },
1512
1250
  defaultOptions: {},
1513
1251
  create({ tokens, report }) {
1514
1252
  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,
1253
+ if (t.aliasOf || !t.originalValue || t.$type !== "string") continue;
1254
+ validateString(t.originalValue.$value, {
1255
+ node: getObjMember(t.source.node, "$value"),
1256
+ filename: t.source.filename
1257
+ });
1258
+ function validateString(value, { node, filename }) {
1259
+ if (typeof value !== "string") report({
1260
+ messageId: ERROR$5,
1549
1261
  node,
1550
1262
  filename
1551
1263
  });
@@ -1553,79 +1265,83 @@ const rule$9 = {
1553
1265
  }
1554
1266
  }
1555
1267
  };
1556
- var valid_font_family_default = rule$9;
1268
+ var valid_string_default = rule$9;
1557
1269
 
1558
1270
  //#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";
1271
+ //#region src/lint/plugin-core/rules/valid-stroke-style.ts
1272
+ const VALID_STROKE_STYLE = "core/valid-stroke-style";
1273
+ const ERROR_STR = "ERROR_STR";
1274
+ const ERROR_OBJ = "ERROR_OBJ";
1275
+ const ERROR_LINE_CAP = "ERROR_LINE_CAP";
1276
+ const ERROR_INVALID_PROP$5 = "ERROR_INVALID_PROP";
1563
1277
  const rule$8 = {
1564
1278
  meta: {
1565
1279
  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 }}."
1280
+ [ERROR_STR]: `Value most be one of ${new Intl.ListFormat("en-us", { type: "disjunction" }).format(STROKE_STYLE_STRING_VALUES)}.`,
1281
+ [ERROR_OBJ]: `Missing required properties: ${new Intl.ListFormat("en-us", { type: "conjunction" }).format(TRANSITION_REQUIRED_PROPERTIES)}.`,
1282
+ [ERROR_LINE_CAP]: `lineCap must be one of ${new Intl.ListFormat("en-us", { type: "disjunction" }).format(STROKE_STYLE_LINE_CAP_VALUES)}.`,
1283
+ [ERROR_INVALID_PROP$5]: "Unknown property: {{ key }}."
1568
1284
  },
1569
1285
  docs: {
1570
- description: "Require number tokens to follow the format.",
1571
- url: docsLink(VALID_FONT_WEIGHT)
1286
+ description: "Require strokeStyle tokens to follow the format.",
1287
+ url: docsLink(VALID_STROKE_STYLE)
1572
1288
  }
1573
1289
  },
1574
- defaultOptions: { style: void 0 },
1575
- create({ tokens, options, report }) {
1290
+ defaultOptions: {},
1291
+ create({ tokens, report }) {
1576
1292
  for (const t of Object.values(tokens)) {
1577
1293
  if (t.aliasOf || !t.originalValue) continue;
1578
1294
  switch (t.$type) {
1579
- case "fontWeight":
1580
- validateFontWeight(t.originalValue.$value, {
1295
+ case "strokeStyle":
1296
+ validateStrokeStyle(t.originalValue.$value, {
1581
1297
  node: getObjMember(t.source.node, "$value"),
1582
1298
  filename: t.source.filename
1583
1299
  });
1584
1300
  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,
1301
+ case "border":
1302
+ if (t.originalValue.$value && typeof t.originalValue.$value === "object") {
1303
+ const $valueNode = getObjMember(t.source.node, "$value");
1304
+ if (t.originalValue.$value.style) validateStrokeStyle(t.originalValue.$value.style, {
1305
+ node: getObjMember($valueNode, "style"),
1591
1306
  filename: t.source.filename
1592
1307
  });
1593
1308
  }
1594
1309
  break;
1595
1310
  }
1596
- function validateFontWeight(value, { node, filename }) {
1311
+ function validateStrokeStyle(value, { node, filename }) {
1597
1312
  if (typeof value === "string") {
1598
- if (options.style === "numbers") report({
1599
- messageId: ERROR_STYLE,
1600
- data: {
1601
- style: "numbers",
1602
- value
1603
- },
1313
+ if (!isAlias(value) && !STROKE_STYLE_STRING_VALUES.includes(value)) {
1314
+ report({
1315
+ messageId: ERROR_STR,
1316
+ node,
1317
+ filename
1318
+ });
1319
+ return;
1320
+ }
1321
+ } else if (value && typeof value === "object") {
1322
+ if (!STROKE_STYLE_OBJECT_REQUIRED_PROPERTIES.every((property) => property in value)) report({
1323
+ messageId: ERROR_OBJ,
1604
1324
  node,
1605
1325
  filename
1606
1326
  });
1607
- else if (!(value in FONT_WEIGHTS)) report({
1608
- messageId: ERROR$5,
1609
- node,
1327
+ if (!Array.isArray(value.dashArray)) report({
1328
+ messageId: ERROR_OBJ,
1329
+ node: getObjMember(node, "dashArray"),
1610
1330
  filename
1611
1331
  });
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,
1332
+ if (!STROKE_STYLE_LINE_CAP_VALUES.includes(value.lineCap)) report({
1333
+ messageId: ERROR_OBJ,
1334
+ node: getObjMember(node, "lineCap"),
1620
1335
  filename
1621
1336
  });
1622
- else if (!(value >= 0 && value < 1e3)) report({
1623
- messageId: ERROR$5,
1624
- node,
1337
+ for (const key of Object.keys(value)) if (!["dashArray", "lineCap"].includes(key)) report({
1338
+ messageId: ERROR_INVALID_PROP$5,
1339
+ data: { key: JSON.stringify(key) },
1340
+ node: getObjMember(node, key),
1625
1341
  filename
1626
1342
  });
1627
1343
  } else report({
1628
- messageId: ERROR$5,
1344
+ messageId: ERROR_OBJ,
1629
1345
  node,
1630
1346
  filename
1631
1347
  });
@@ -1633,97 +1349,92 @@ const rule$8 = {
1633
1349
  }
1634
1350
  }
1635
1351
  };
1636
- var valid_font_weight_default = rule$8;
1352
+ var valid_stroke_style_default = rule$8;
1637
1353
 
1638
1354
  //#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";
1355
+ //#region src/lint/plugin-core/rules/valid-transition.ts
1356
+ const VALID_TRANSITION = "core/valid-transition";
1357
+ const ERROR$4 = "ERROR";
1358
+ const ERROR_INVALID_PROP$4 = "ERROR_INVALID_PROP";
1644
1359
  const rule$7 = {
1645
1360
  meta: {
1646
1361
  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 }}."
1362
+ [ERROR$4]: `Missing required properties: ${new Intl.ListFormat("en-us", { type: "conjunction" }).format(TRANSITION_REQUIRED_PROPERTIES)}.`,
1363
+ [ERROR_INVALID_PROP$4]: "Unknown property: {{ key }}."
1650
1364
  },
1651
1365
  docs: {
1652
- description: "Require gradient tokens to follow the format.",
1653
- url: docsLink(VALID_GRADIENT)
1366
+ description: "Require transition tokens to follow the format.",
1367
+ url: docsLink(VALID_TRANSITION)
1654
1368
  }
1655
1369
  },
1656
1370
  defaultOptions: {},
1657
1371
  create({ tokens, report }) {
1658
1372
  for (const t of Object.values(tokens)) {
1659
- if (t.aliasOf || !t.originalValue || t.$type !== "gradient") continue;
1660
- validateGradient(t.originalValue.$value, {
1373
+ if (t.aliasOf || !t.originalValue || t.$type !== "transition") continue;
1374
+ validateTransition(t.originalValue.$value, {
1661
1375
  node: getObjMember(t.source.node, "$value"),
1662
1376
  filename: t.source.filename
1663
1377
  });
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
- }
1378
+ }
1379
+ function validateTransition(value, { node, filename }) {
1380
+ if (!value || typeof value !== "object" || !TRANSITION_REQUIRED_PROPERTIES.every((property) => property in value)) report({
1381
+ messageId: ERROR$4,
1382
+ node,
1383
+ filename
1384
+ });
1385
+ else for (const key of Object.keys(value)) if (!TRANSITION_REQUIRED_PROPERTIES.includes(key)) report({
1386
+ messageId: ERROR_INVALID_PROP$4,
1387
+ data: { key: JSON.stringify(key) },
1388
+ node: getObjMember(node, key),
1389
+ filename
1390
+ });
1699
1391
  }
1700
1392
  }
1701
1393
  };
1702
- var valid_gradient_default = rule$7;
1394
+ var valid_transition_default = rule$7;
1703
1395
 
1704
1396
  //#endregion
1705
- //#region src/lint/plugin-core/rules/valid-link.ts
1706
- const VALID_LINK = "core/valid-link";
1707
- const ERROR$4 = "ERROR";
1397
+ //#region src/lint/plugin-core/rules/valid-typography.ts
1398
+ const VALID_TYPOGRAPHY = "core/valid-typography";
1399
+ const ERROR$3 = "ERROR";
1400
+ const ERROR_MISSING = "ERROR_MISSING";
1708
1401
  const rule$6 = {
1709
1402
  meta: {
1710
- messages: { [ERROR$4]: "Must be a string." },
1403
+ messages: {
1404
+ [ERROR$3]: `Expected object, received {{ value }}.`,
1405
+ [ERROR_MISSING]: `Missing required property "{{ property }}".`
1406
+ },
1711
1407
  docs: {
1712
- description: "Require link tokens to follow the Terrazzo extension.",
1713
- url: docsLink(VALID_LINK)
1408
+ description: "Require typography tokens to follow the format.",
1409
+ url: docsLink(VALID_TYPOGRAPHY)
1714
1410
  }
1715
1411
  },
1716
- defaultOptions: {},
1717
- create({ tokens, report }) {
1412
+ defaultOptions: { requiredProperties: [
1413
+ "fontFamily",
1414
+ "fontSize",
1415
+ "fontWeight",
1416
+ "letterSpacing",
1417
+ "lineHeight"
1418
+ ] },
1419
+ create({ tokens, options, report }) {
1420
+ const isIgnored = options.ignore ? wcmatch(options.ignore) : () => false;
1718
1421
  for (const t of Object.values(tokens)) {
1719
- if (t.aliasOf || !t.originalValue || t.$type !== "link") continue;
1720
- validateLink(t.originalValue.$value, {
1422
+ if (t.aliasOf || !t.originalValue || t.$type !== "typography" || isIgnored(t.id)) continue;
1423
+ validateTypography(t.originalValue.$value, {
1721
1424
  node: getObjMember(t.source.node, "$value"),
1722
1425
  filename: t.source.filename
1723
1426
  });
1724
- function validateLink(value, { node, filename }) {
1725
- if (!value || typeof value !== "string") report({
1726
- messageId: ERROR$4,
1427
+ function validateTypography(value, { node, filename }) {
1428
+ if (value && typeof value === "object") {
1429
+ for (const property of options.requiredProperties) if (!(property in value)) report({
1430
+ messageId: ERROR_MISSING,
1431
+ data: { property },
1432
+ node,
1433
+ filename
1434
+ });
1435
+ } else report({
1436
+ messageId: ERROR$3,
1437
+ data: { value: JSON.stringify(value) },
1727
1438
  node,
1728
1439
  filename
1729
1440
  });
@@ -1731,203 +1442,265 @@ const rule$6 = {
1731
1442
  }
1732
1443
  }
1733
1444
  };
1734
- var valid_link_default = rule$6;
1445
+ var valid_typography_default = rule$6;
1735
1446
 
1736
1447
  //#endregion
1737
- //#region src/lint/plugin-core/rules/valid-number.ts
1738
- const VALID_NUMBER = "core/valid-number";
1739
- const ERROR_NAN = "ERROR_NAN";
1448
+ //#region src/lint/plugin-core/rules/valid-boolean.ts
1449
+ const VALID_BOOLEAN = "core/valid-boolean";
1450
+ const ERROR$2 = "ERROR";
1740
1451
  const rule$5 = {
1741
1452
  meta: {
1742
- messages: { [ERROR_NAN]: "Must be a number." },
1453
+ messages: { [ERROR$2]: "Must be a boolean." },
1743
1454
  docs: {
1744
- description: "Require number tokens to follow the format.",
1745
- url: docsLink(VALID_NUMBER)
1455
+ description: "Require boolean tokens to follow the Terrazzo extension.",
1456
+ url: docsLink(VALID_BOOLEAN)
1746
1457
  }
1747
1458
  },
1748
1459
  defaultOptions: {},
1749
1460
  create({ tokens, report }) {
1750
1461
  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
1462
+ if (t.aliasOf || !t.originalValue || t.$type !== "boolean") continue;
1463
+ validateBoolean(t.originalValue.$value, {
1464
+ node: getObjMember(t.source.node, "$value"),
1465
+ filename: t.source.filename
1466
+ });
1467
+ function validateBoolean(value, { node, filename }) {
1468
+ if (typeof value !== "boolean") report({
1469
+ messageId: ERROR$2,
1470
+ filename,
1471
+ node
1774
1472
  });
1775
1473
  }
1776
1474
  }
1777
1475
  }
1778
1476
  };
1779
- var valid_number_default = rule$5;
1477
+ var valid_boolean_default = rule$5;
1780
1478
 
1781
1479
  //#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";
1480
+ //#region src/lint/plugin-core/rules/valid-border.ts
1481
+ const VALID_BORDER = "core/valid-border";
1482
+ const ERROR$1 = "ERROR";
1483
+ const ERROR_INVALID_PROP$3 = "ERROR_INVALID_PROP";
1786
1484
  const rule$4 = {
1787
1485
  meta: {
1788
1486
  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 }}."
1487
+ [ERROR$1]: `Border token missing required properties: ${new Intl.ListFormat("en-us", { type: "conjunction" }).format(BORDER_REQUIRED_PROPERTIES)}.`,
1488
+ [ERROR_INVALID_PROP$3]: "Unknown property: {{ key }}."
1791
1489
  },
1792
1490
  docs: {
1793
- description: "Require shadow tokens to follow the format.",
1794
- url: docsLink(VALID_SHADOW)
1491
+ description: "Require border tokens to follow the format.",
1492
+ url: docsLink(VALID_BORDER)
1795
1493
  }
1796
1494
  },
1797
1495
  defaultOptions: {},
1798
1496
  create({ tokens, report }) {
1799
1497
  for (const t of Object.values(tokens)) {
1800
- if (t.aliasOf || !t.originalValue || t.$type !== "shadow") continue;
1801
- validateShadow(t.originalValue.$value, {
1498
+ if (t.aliasOf || !t.originalValue || t.$type !== "border") continue;
1499
+ validateBorder(t.originalValue.$value, {
1802
1500
  node: getObjMember(t.source.node, "$value"),
1803
1501
  filename: t.source.filename
1804
1502
  });
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
- }
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
1503
  }
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
1504
+ function validateBorder(value, { node, filename }) {
1505
+ if (!value || typeof value !== "object" || !BORDER_REQUIRED_PROPERTIES.every((property) => property in value)) report({
1506
+ messageId: ERROR$1,
1507
+ filename,
1508
+ node
1509
+ });
1510
+ else for (const key of Object.keys(value)) if (!BORDER_REQUIRED_PROPERTIES.includes(key)) report({
1511
+ messageId: ERROR_INVALID_PROP$3,
1512
+ data: { key: JSON.stringify(key) },
1513
+ node: getObjMember(node, key) ?? node,
1514
+ filename
1843
1515
  });
1844
- function validateString(value, { node, filename }) {
1845
- if (typeof value !== "string") report({
1846
- messageId: ERROR$2,
1847
- node,
1848
- filename
1849
- });
1850
- }
1851
1516
  }
1852
1517
  }
1853
1518
  };
1854
- var valid_string_default = rule$3;
1519
+ var valid_border_default = rule$4;
1855
1520
 
1856
1521
  //#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 = {
1522
+ //#region src/lint/plugin-core/rules/valid-color.ts
1523
+ const VALID_COLOR = "core/valid-color";
1524
+ const ERROR_ALPHA = "ERROR_ALPHA";
1525
+ const ERROR_INVALID_COLOR = "ERROR_INVALID_COLOR";
1526
+ const ERROR_INVALID_COLOR_SPACE = "ERROR_INVALID_COLOR_SPACE";
1527
+ const ERROR_INVALID_COMPONENT_LENGTH = "ERROR_INVALID_COMPONENT_LENGTH";
1528
+ const ERROR_INVALID_HEX8 = "ERROR_INVALID_HEX8";
1529
+ const ERROR_INVALID_PROP$2 = "ERROR_INVALID_PROP";
1530
+ const ERROR_MISSING_COMPONENTS = "ERROR_MISSING_COMPONENTS";
1531
+ const ERROR_OBJ_FORMAT = "ERROR_OBJ_FORMAT";
1532
+ const ERROR_OUT_OF_RANGE = "ERROR_OUT_OF_RANGE";
1533
+ const rule$3 = {
1864
1534
  meta: {
1865
1535
  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 }}."
1536
+ [ERROR_ALPHA]: `Alpha {{ alpha }} not in range 0 1.`,
1537
+ [ERROR_INVALID_COLOR_SPACE]: `Invalid color space: {{ colorSpace }}. Expected ${new Intl.ListFormat("en-us", { type: "disjunction" }).format(Object.keys(COLORSPACE))}`,
1538
+ [ERROR_INVALID_COLOR]: `Could not parse color {{ color }}.`,
1539
+ [ERROR_INVALID_COMPONENT_LENGTH]: "Expected {{ expected }} components, received {{ got }}.",
1540
+ [ERROR_INVALID_HEX8]: `Hex value can’t be semi-transparent.`,
1541
+ [ERROR_INVALID_PROP$2]: `Unknown property {{ key }}.`,
1542
+ [ERROR_MISSING_COMPONENTS]: "Expected components to be array of numbers, received {{ got }}.",
1543
+ [ERROR_OBJ_FORMAT]: "Migrate to the new object format, e.g. \"#ff0000\" → { \"colorSpace\": \"srgb\", \"components\": [1, 0, 0] } }",
1544
+ [ERROR_OUT_OF_RANGE]: `Invalid range for color space {{ colorSpace }}. Expected {{ range }}.`
1870
1545
  },
1871
1546
  docs: {
1872
- description: "Require strokeStyle tokens to follow the format.",
1873
- url: docsLink(VALID_STROKE_STYLE)
1547
+ description: "Require color tokens to follow the format.",
1548
+ url: docsLink(VALID_COLOR)
1874
1549
  }
1875
1550
  },
1876
- defaultOptions: {},
1877
- create({ tokens, report }) {
1551
+ defaultOptions: {
1552
+ legacyFormat: false,
1553
+ ignoreRanges: false
1554
+ },
1555
+ create({ tokens, options, report }) {
1878
1556
  for (const t of Object.values(tokens)) {
1879
1557
  if (t.aliasOf || !t.originalValue) continue;
1880
1558
  switch (t.$type) {
1881
- case "strokeStyle":
1882
- validateStrokeStyle(t.originalValue.$value, {
1559
+ case "color":
1560
+ validateColor(t.originalValue.$value, {
1883
1561
  node: getObjMember(t.source.node, "$value"),
1884
1562
  filename: t.source.filename
1885
1563
  });
1886
1564
  break;
1887
1565
  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"),
1566
+ if (t.originalValue.$value.color && !isAlias(t.originalValue.$value.color)) validateColor(t.originalValue.$value.color, {
1567
+ node: getObjMember(getObjMember(t.source.node, "$value"), "color"),
1568
+ filename: t.source.filename
1569
+ });
1570
+ break;
1571
+ case "gradient": {
1572
+ const $valueNode = getObjMember(t.source.node, "$value");
1573
+ for (let i = 0; i < t.originalValue.$value.length; i++) {
1574
+ const stop = t.originalValue.$value[i];
1575
+ if (!stop.color || isAlias(stop.color)) continue;
1576
+ validateColor(stop.color, {
1577
+ node: getObjMember($valueNode.elements[i].value, "color"),
1892
1578
  filename: t.source.filename
1893
1579
  });
1894
1580
  }
1895
1581
  break;
1582
+ }
1583
+ case "shadow": {
1584
+ const $value = Array.isArray(t.originalValue.$value) ? t.originalValue.$value : [t.originalValue.$value];
1585
+ const $valueNode = getObjMember(t.source.node, "$value");
1586
+ for (let i = 0; i < $value.length; i++) {
1587
+ const layer = $value[i];
1588
+ if (!layer.color || isAlias(layer.color)) continue;
1589
+ validateColor(layer.color, {
1590
+ node: $valueNode.type === "Object" ? getObjMember($valueNode, "color") : getObjMember($valueNode.elements[i].value, "color"),
1591
+ filename: t.source.filename
1592
+ });
1593
+ }
1594
+ break;
1595
+ }
1896
1596
  }
1897
- function validateStrokeStyle(value, { node, filename }) {
1898
- if (typeof value === "string") {
1899
- if (!isAlias(value) && !STROKE_STYLE_STRING_VALUES.includes(value)) {
1597
+ function validateColor(value, { node, filename }) {
1598
+ if (!value) report({
1599
+ messageId: ERROR_INVALID_COLOR,
1600
+ data: { color: JSON.stringify(value) },
1601
+ node,
1602
+ filename
1603
+ });
1604
+ else if (typeof value === "object") {
1605
+ for (const key of Object.keys(value)) if (![
1606
+ "colorSpace",
1607
+ "components",
1608
+ "channels",
1609
+ "hex",
1610
+ "alpha"
1611
+ ].includes(key)) report({
1612
+ messageId: ERROR_INVALID_PROP$2,
1613
+ data: { key: JSON.stringify(key) },
1614
+ node: getObjMember(node, key) ?? node,
1615
+ filename
1616
+ });
1617
+ const colorSpace = "colorSpace" in value && typeof value.colorSpace === "string" ? value.colorSpace : void 0;
1618
+ const csData = COLORSPACE[colorSpace] || void 0;
1619
+ if (!("colorSpace" in value) || !csData) {
1900
1620
  report({
1901
- messageId: ERROR_STR,
1902
- node,
1621
+ messageId: ERROR_INVALID_COLOR_SPACE,
1622
+ data: { colorSpace },
1623
+ node: getObjMember(node, "colorSpace") ?? node,
1903
1624
  filename
1904
1625
  });
1905
1626
  return;
1906
1627
  }
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,
1628
+ const components = "components" in value ? value.components : void 0;
1629
+ if (Array.isArray(components)) if (csData?.ranges && components?.length === csData.ranges.length) {
1630
+ 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]) {
1631
+ if (!(colorSpace === "hsl" && components[0] === null) && !(colorSpace === "hwb" && components[0] === null) && !(colorSpace === "lch" && components[2] === null) && !(colorSpace === "oklch" && components[2] === null)) report({
1632
+ messageId: ERROR_OUT_OF_RANGE,
1633
+ data: {
1634
+ colorSpace,
1635
+ range: `[${csData.ranges.map((r) => `${r[0]}–${r[1]}`).join(", ")}]`
1636
+ },
1637
+ node: getObjMember(node, "components") ?? node,
1638
+ filename
1639
+ });
1640
+ }
1641
+ } else report({
1642
+ messageId: ERROR_INVALID_COMPONENT_LENGTH,
1643
+ data: {
1644
+ expected: csData?.ranges.length,
1645
+ got: components?.length ?? 0
1646
+ },
1647
+ node: getObjMember(node, "components") ?? node,
1911
1648
  filename
1912
1649
  });
1913
- if (!Array.isArray(value.dashArray)) report({
1914
- messageId: ERROR_OBJ,
1915
- node: getObjMember(node, "dashArray"),
1650
+ else report({
1651
+ messageId: ERROR_MISSING_COMPONENTS,
1652
+ data: { got: JSON.stringify(components) },
1653
+ node: getObjMember(node, "components") ?? node,
1916
1654
  filename
1917
1655
  });
1918
- if (!STROKE_STYLE_LINE_CAP_VALUES.includes(value.lineCap)) report({
1919
- messageId: ERROR_OBJ,
1920
- node: getObjMember(node, "lineCap"),
1656
+ const alpha = "alpha" in value ? value.alpha : void 0;
1657
+ if (alpha !== void 0 && (typeof alpha !== "number" || alpha < 0 || alpha > 1)) report({
1658
+ messageId: ERROR_ALPHA,
1659
+ data: { alpha },
1660
+ node: getObjMember(node, "alpha") ?? node,
1921
1661
  filename
1922
1662
  });
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),
1663
+ const hex = "hex" in value ? value.hex : void 0;
1664
+ if (hex) {
1665
+ let color;
1666
+ try {
1667
+ color = parseColor(hex);
1668
+ } catch {
1669
+ report({
1670
+ messageId: ERROR_INVALID_COLOR,
1671
+ data: { color: hex },
1672
+ node: getObjMember(node, "hex") ?? node,
1673
+ filename
1674
+ });
1675
+ return;
1676
+ }
1677
+ if (color.alpha !== 1) report({
1678
+ messageId: ERROR_INVALID_HEX8,
1679
+ data: { color: hex },
1680
+ node: getObjMember(node, "hex") ?? node,
1681
+ filename
1682
+ });
1683
+ }
1684
+ } else if (typeof value === "string") {
1685
+ if (isAlias(value)) return;
1686
+ if (!options.legacyFormat) report({
1687
+ messageId: ERROR_OBJ_FORMAT,
1688
+ data: { color: JSON.stringify(value) },
1689
+ node,
1927
1690
  filename
1928
1691
  });
1692
+ else try {
1693
+ parseColor(value);
1694
+ } catch {
1695
+ report({
1696
+ messageId: ERROR_INVALID_COLOR,
1697
+ data: { color: JSON.stringify(value) },
1698
+ node,
1699
+ filename
1700
+ });
1701
+ }
1929
1702
  } else report({
1930
- messageId: ERROR_OBJ,
1703
+ messageId: ERROR_INVALID_COLOR,
1931
1704
  node,
1932
1705
  filename
1933
1706
  });
@@ -1935,92 +1708,319 @@ const rule$2 = {
1935
1708
  }
1936
1709
  }
1937
1710
  };
1938
- var valid_stroke_style_default = rule$2;
1711
+ var valid_color_default = rule$3;
1939
1712
 
1940
1713
  //#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 = {
1714
+ //#region src/lint/plugin-core/rules/valid-cubic-bezier.ts
1715
+ const VALID_CUBIC_BEZIER = "core/valid-cubic-bezier";
1716
+ const ERROR = "ERROR";
1717
+ const ERROR_X = "ERROR_X";
1718
+ const ERROR_Y = "ERROR_Y";
1719
+ const rule$2 = {
1946
1720
  meta: {
1947
1721
  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 }}."
1722
+ [ERROR]: "Expected [number, number, number, number].",
1723
+ [ERROR_X]: "x values must be between 0-1.",
1724
+ [ERROR_Y]: "y values must be a valid number."
1950
1725
  },
1951
1726
  docs: {
1952
- description: "Require transition tokens to follow the format.",
1953
- url: docsLink(VALID_TRANSITION)
1727
+ description: "Require cubicBezier tokens to follow the format.",
1728
+ url: docsLink(VALID_CUBIC_BEZIER)
1954
1729
  }
1955
1730
  },
1956
1731
  defaultOptions: {},
1957
1732
  create({ tokens, report }) {
1958
1733
  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
- });
1734
+ if (t.aliasOf || !t.originalValue) continue;
1735
+ switch (t.$type) {
1736
+ case "cubicBezier":
1737
+ validateCubicBezier(t.originalValue.$value, {
1738
+ node: getObjMember(t.source.node, "$value"),
1739
+ filename: t.source.filename
1740
+ });
1741
+ break;
1742
+ case "transition": if (typeof t.originalValue.$value === "object" && t.originalValue.$value.timingFunction && !isAlias(t.originalValue.$value.timingFunction)) {
1743
+ const $valueNode = getObjMember(t.source.node, "$value");
1744
+ validateCubicBezier(t.originalValue.$value.timingFunction, {
1745
+ node: getObjMember($valueNode, "timingFunction"),
1746
+ filename: t.source.filename
1747
+ });
1748
+ }
1749
+ }
1750
+ function validateCubicBezier(value, { node, filename }) {
1751
+ if (Array.isArray(value) && value.length === 4) {
1752
+ for (const pos of [0, 2]) {
1753
+ if (isAlias(value[pos]) || isPure$ref(value[pos])) continue;
1754
+ if (!(value[pos] >= 0 && value[pos] <= 1)) report({
1755
+ messageId: ERROR_X,
1756
+ node: node.elements[pos],
1757
+ filename
1758
+ });
1759
+ }
1760
+ for (const pos of [1, 3]) {
1761
+ if (isAlias(value[pos]) || isPure$ref(value[pos])) continue;
1762
+ if (typeof value[pos] !== "number") report({
1763
+ messageId: ERROR_Y,
1764
+ node: node.elements[pos],
1765
+ filename
1766
+ });
1767
+ }
1768
+ } else report({
1769
+ messageId: ERROR,
1770
+ node,
1771
+ filename
1772
+ });
1773
+ }
1964
1774
  }
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
- });
1775
+ }
1776
+ };
1777
+ var valid_cubic_bezier_default = rule$2;
1778
+
1779
+ //#endregion
1780
+ //#region src/lint/plugin-core/rules/valid-dimension.ts
1781
+ const VALID_DIMENSION = "core/valid-dimension";
1782
+ const ERROR_FORMAT$1 = "ERROR_FORMAT";
1783
+ const ERROR_INVALID_PROP$1 = "ERROR_INVALID_PROP";
1784
+ const ERROR_LEGACY$1 = "ERROR_LEGACY";
1785
+ const ERROR_UNIT$1 = "ERROR_UNIT";
1786
+ const ERROR_VALUE$1 = "ERROR_VALUE";
1787
+ const rule$1 = {
1788
+ meta: {
1789
+ messages: {
1790
+ [ERROR_FORMAT$1]: "Invalid dimension: {{ value }}. Expected object with \"value\" and \"unit\".",
1791
+ [ERROR_LEGACY$1]: "Migrate to the new object format: { \"value\": 10, \"unit\": \"px\" }.",
1792
+ [ERROR_UNIT$1]: "Unit {{ unit }} not allowed. Expected {{ allowed }}.",
1793
+ [ERROR_INVALID_PROP$1]: "Unknown property {{ key }}.",
1794
+ [ERROR_VALUE$1]: "Expected number, received {{ value }}."
1795
+ },
1796
+ docs: {
1797
+ description: "Require dimension tokens to follow the format",
1798
+ url: docsLink(VALID_DIMENSION)
1799
+ }
1800
+ },
1801
+ defaultOptions: {
1802
+ legacyFormat: false,
1803
+ allowedUnits: [
1804
+ "px",
1805
+ "em",
1806
+ "rem"
1807
+ ]
1808
+ },
1809
+ create({ tokens, options, report }) {
1810
+ for (const t of Object.values(tokens)) {
1811
+ if (t.aliasOf || !t.originalValue) continue;
1812
+ switch (t.$type) {
1813
+ case "dimension":
1814
+ validateDimension(t.originalValue.$value, {
1815
+ node: getObjMember(t.source.node, "$value"),
1816
+ filename: t.source.filename
1817
+ });
1818
+ break;
1819
+ case "strokeStyle":
1820
+ if (typeof t.originalValue.$value === "object" && Array.isArray(t.originalValue.$value.dashArray)) {
1821
+ const dashArray = getObjMember(getObjMember(t.source.node, "$value"), "dashArray");
1822
+ for (let i = 0; i < t.originalValue.$value.dashArray.length; i++) {
1823
+ if (isAlias(t.originalValue.$value.dashArray[i])) continue;
1824
+ validateDimension(t.originalValue.$value.dashArray[i], {
1825
+ node: dashArray.elements[i].value,
1826
+ filename: t.source.filename
1827
+ });
1828
+ }
1829
+ }
1830
+ break;
1831
+ case "border": {
1832
+ const $valueNode = getObjMember(t.source.node, "$value");
1833
+ if (typeof t.originalValue.$value === "object") {
1834
+ if (t.originalValue.$value.width && !isAlias(t.originalValue.$value.width)) validateDimension(t.originalValue.$value.width, {
1835
+ node: getObjMember($valueNode, "width"),
1836
+ filename: t.source.filename
1837
+ });
1838
+ if (typeof t.originalValue.$value.style === "object" && Array.isArray(t.originalValue.$value.style.dashArray)) {
1839
+ const dashArray = getObjMember(getObjMember($valueNode, "style"), "dashArray");
1840
+ for (let i = 0; i < t.originalValue.$value.style.dashArray.length; i++) {
1841
+ if (isAlias(t.originalValue.$value.style.dashArray[i])) continue;
1842
+ validateDimension(t.originalValue.$value.style.dashArray[i], {
1843
+ node: dashArray.elements[i].value,
1844
+ filename: t.source.filename
1845
+ });
1846
+ }
1847
+ }
1848
+ }
1849
+ break;
1850
+ }
1851
+ case "shadow":
1852
+ if (t.originalValue.$value && typeof t.originalValue.$value === "object") {
1853
+ const $valueNode = getObjMember(t.source.node, "$value");
1854
+ const valueArray = Array.isArray(t.originalValue.$value) ? t.originalValue.$value : [t.originalValue.$value];
1855
+ for (let i = 0; i < valueArray.length; i++) {
1856
+ const node = $valueNode.type === "Array" ? $valueNode.elements[i].value : $valueNode;
1857
+ for (const property of [
1858
+ "offsetX",
1859
+ "offsetY",
1860
+ "blur",
1861
+ "spread"
1862
+ ]) {
1863
+ if (isAlias(valueArray[i][property])) continue;
1864
+ validateDimension(valueArray[i][property], {
1865
+ node: getObjMember(node, property),
1866
+ filename: t.source.filename
1867
+ });
1868
+ }
1869
+ }
1870
+ }
1871
+ break;
1872
+ case "typography": {
1873
+ const $valueNode = getObjMember(t.source.node, "$value");
1874
+ if (typeof t.originalValue.$value === "object") {
1875
+ for (const property of [
1876
+ "fontSize",
1877
+ "lineHeight",
1878
+ "letterSpacing"
1879
+ ]) if (property in t.originalValue.$value) {
1880
+ if (isAlias(t.originalValue.$value[property]) || property === "lineHeight" && typeof t.originalValue.$value[property] === "number") continue;
1881
+ validateDimension(t.originalValue.$value[property], {
1882
+ node: getObjMember($valueNode, property),
1883
+ filename: t.source.filename
1884
+ });
1885
+ }
1886
+ }
1887
+ break;
1888
+ }
1889
+ }
1890
+ function validateDimension(value, { node, filename }) {
1891
+ if (value && typeof value === "object") {
1892
+ for (const key of Object.keys(value)) if (!["value", "unit"].includes(key)) report({
1893
+ messageId: ERROR_INVALID_PROP$1,
1894
+ data: { key: JSON.stringify(key) },
1895
+ node: getObjMember(node, key) ?? node,
1896
+ filename
1897
+ });
1898
+ const { unit, value: numValue } = value;
1899
+ if (!("value" in value || "unit" in value)) {
1900
+ report({
1901
+ messageId: ERROR_FORMAT$1,
1902
+ data: { value },
1903
+ node,
1904
+ filename
1905
+ });
1906
+ return;
1907
+ }
1908
+ if (!options.allowedUnits.includes(unit)) report({
1909
+ messageId: ERROR_UNIT$1,
1910
+ data: {
1911
+ unit,
1912
+ allowed: new Intl.ListFormat("en-us", { type: "disjunction" }).format(options.allowedUnits)
1913
+ },
1914
+ node: getObjMember(node, "unit") ?? node,
1915
+ filename
1916
+ });
1917
+ if (!Number.isFinite(numValue)) report({
1918
+ messageId: ERROR_VALUE$1,
1919
+ data: { value },
1920
+ node: getObjMember(node, "value") ?? node,
1921
+ filename
1922
+ });
1923
+ } else if (typeof value === "string" && !options.legacyFormat) report({
1924
+ messageId: ERROR_LEGACY$1,
1925
+ node,
1926
+ filename
1927
+ });
1928
+ else report({
1929
+ messageId: ERROR_FORMAT$1,
1930
+ data: { value },
1931
+ node,
1932
+ filename
1933
+ });
1934
+ }
1977
1935
  }
1978
1936
  }
1979
1937
  };
1980
- var valid_transition_default = rule$1;
1938
+ var valid_dimension_default = rule$1;
1981
1939
 
1982
1940
  //#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";
1941
+ //#region src/lint/plugin-core/rules/valid-duration.ts
1942
+ const VALID_DURATION = "core/valid-duration";
1943
+ const ERROR_FORMAT = "ERROR_FORMAT";
1944
+ const ERROR_INVALID_PROP = "ERROR_INVALID_PROP";
1945
+ const ERROR_LEGACY = "ERROR_LEGACY";
1946
+ const ERROR_UNIT = "ERROR_UNIT";
1947
+ const ERROR_VALUE = "ERROR_VALUE";
1987
1948
  const rule = {
1988
1949
  meta: {
1989
1950
  messages: {
1990
- [ERROR]: `Expected object, received {{ value }}.`,
1991
- [ERROR_MISSING]: `Missing required property "{{ property }}".`
1951
+ [ERROR_FORMAT]: "Migrate to the new object format: { \"value\": 2, \"unit\": \"ms\" }.",
1952
+ [ERROR_LEGACY]: "Migrate to the new object format: { \"value\": 10, \"unit\": \"px\" }.",
1953
+ [ERROR_INVALID_PROP]: "Unknown property: {{ key }}.",
1954
+ [ERROR_UNIT]: "Unknown unit {{ unit }}. Expected \"ms\" or \"s\".",
1955
+ [ERROR_VALUE]: "Expected number, received {{ value }}."
1992
1956
  },
1993
1957
  docs: {
1994
- description: "Require typography tokens to follow the format.",
1995
- url: docsLink(VALID_TYPOGRAPHY)
1958
+ description: "Require duration tokens to follow the format",
1959
+ url: docsLink(VALID_DURATION)
1996
1960
  }
1997
1961
  },
1998
- defaultOptions: { requiredProperties: [
1999
- "fontFamily",
2000
- "fontSize",
2001
- "fontWeight",
2002
- "letterSpacing",
2003
- "lineHeight"
2004
- ] },
1962
+ defaultOptions: {
1963
+ legacyFormat: false,
1964
+ unknownUnits: false
1965
+ },
2005
1966
  create({ tokens, options, report }) {
2006
- const isIgnored = options.ignore ? wcmatch(options.ignore) : () => false;
2007
1967
  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 }) {
1968
+ if (t.aliasOf || !t.originalValue) continue;
1969
+ switch (t.$type) {
1970
+ case "duration":
1971
+ validateDuration(t.originalValue.$value, {
1972
+ node: getObjMember(t.source.node, "$value"),
1973
+ filename: t.source.filename
1974
+ });
1975
+ break;
1976
+ case "transition":
1977
+ if (typeof t.originalValue.$value === "object") {
1978
+ const $valueNode = getObjMember(t.source.node, "$value");
1979
+ for (const property of ["duration", "delay"]) if (t.originalValue.$value[property] && !isAlias(t.originalValue.$value[property])) validateDuration(t.originalValue.$value[property], {
1980
+ node: getObjMember($valueNode, property),
1981
+ filename: t.source.filename
1982
+ });
1983
+ }
1984
+ break;
1985
+ }
1986
+ function validateDuration(value, { node, filename }) {
2014
1987
  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,
1988
+ for (const key of Object.keys(value)) if (!["value", "unit"].includes(key)) report({
1989
+ messageId: ERROR_INVALID_PROP,
1990
+ data: { key: JSON.stringify(key) },
1991
+ node: getObjMember(node, key) ?? node,
2019
1992
  filename
2020
1993
  });
2021
- } else report({
2022
- messageId: ERROR,
2023
- data: { value: JSON.stringify(value) },
1994
+ const { unit, value: numValue } = value;
1995
+ if (!("value" in value || "unit" in value)) {
1996
+ report({
1997
+ messageId: ERROR_FORMAT,
1998
+ data: { value },
1999
+ node,
2000
+ filename
2001
+ });
2002
+ return;
2003
+ }
2004
+ if (!options.unknownUnits && !["ms", "s"].includes(unit)) report({
2005
+ messageId: ERROR_UNIT,
2006
+ data: { unit },
2007
+ node: getObjMember(node, "unit") ?? node,
2008
+ filename
2009
+ });
2010
+ if (!Number.isFinite(numValue)) report({
2011
+ messageId: ERROR_VALUE,
2012
+ data: { value },
2013
+ node: getObjMember(node, "value") ?? node,
2014
+ filename
2015
+ });
2016
+ } else if (typeof value === "string" && !options.legacyFormat) report({
2017
+ messageId: ERROR_FORMAT,
2018
+ node,
2019
+ filename
2020
+ });
2021
+ else report({
2022
+ messageId: ERROR_FORMAT,
2023
+ data: { value },
2024
2024
  node,
2025
2025
  filename
2026
2026
  });
@@ -2028,7 +2028,7 @@ const rule = {
2028
2028
  }
2029
2029
  }
2030
2030
  };
2031
- var valid_typography_default = rule;
2031
+ var valid_duration_default = rule;
2032
2032
 
2033
2033
  //#endregion
2034
2034
  //#region src/lint/plugin-core/index.ts
@@ -2840,7 +2840,9 @@ const EXPECTED_NESTED_ALIAS = {
2840
2840
  fontWeight: ["fontWeight"],
2841
2841
  fontSize: ["dimension"],
2842
2842
  lineHeight: ["dimension", "number"],
2843
- letterSpacing: ["dimension"]
2843
+ letterSpacing: ["dimension"],
2844
+ paragraphSpacing: ["dimension", "string"],
2845
+ wordSpacing: ["dimension", "string"]
2844
2846
  }
2845
2847
  };
2846
2848
  /**