@shanepadgett/design.md 0.1.0

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.
Files changed (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -0
  3. package/dist/cli/args.d.ts +32 -0
  4. package/dist/cli/args.d.ts.map +1 -0
  5. package/dist/cli/args.js +181 -0
  6. package/dist/cli/args.js.map +1 -0
  7. package/dist/cli/commands/export.d.ts +5 -0
  8. package/dist/cli/commands/export.d.ts.map +1 -0
  9. package/dist/cli/commands/export.js +44 -0
  10. package/dist/cli/commands/export.js.map +1 -0
  11. package/dist/cli/commands/lint.d.ts +5 -0
  12. package/dist/cli/commands/lint.d.ts.map +1 -0
  13. package/dist/cli/commands/lint.js +28 -0
  14. package/dist/cli/commands/lint.js.map +1 -0
  15. package/dist/cli/commands/migrate.d.ts +5 -0
  16. package/dist/cli/commands/migrate.d.ts.map +1 -0
  17. package/dist/cli/commands/migrate.js +36 -0
  18. package/dist/cli/commands/migrate.js.map +1 -0
  19. package/dist/cli/diagnostics.d.ts +4 -0
  20. package/dist/cli/diagnostics.d.ts.map +1 -0
  21. package/dist/cli/diagnostics.js +16 -0
  22. package/dist/cli/diagnostics.js.map +1 -0
  23. package/dist/cli/io.d.ts +11 -0
  24. package/dist/cli/io.d.ts.map +1 -0
  25. package/dist/cli/io.js +2 -0
  26. package/dist/cli/io.js.map +1 -0
  27. package/dist/cli/main.d.ts +3 -0
  28. package/dist/cli/main.d.ts.map +1 -0
  29. package/dist/cli/main.js +20 -0
  30. package/dist/cli/main.js.map +1 -0
  31. package/dist/cli/run.d.ts +4 -0
  32. package/dist/cli/run.d.ts.map +1 -0
  33. package/dist/cli/run.js +29 -0
  34. package/dist/cli/run.js.map +1 -0
  35. package/dist/core/diagnostics/types.d.ts +29 -0
  36. package/dist/core/diagnostics/types.d.ts.map +1 -0
  37. package/dist/core/diagnostics/types.js +14 -0
  38. package/dist/core/diagnostics/types.js.map +1 -0
  39. package/dist/core/document/parse-document.d.ts +8 -0
  40. package/dist/core/document/parse-document.d.ts.map +1 -0
  41. package/dist/core/document/parse-document.js +153 -0
  42. package/dist/core/document/parse-document.js.map +1 -0
  43. package/dist/core/document/types.d.ts +27 -0
  44. package/dist/core/document/types.d.ts.map +1 -0
  45. package/dist/core/document/types.js +2 -0
  46. package/dist/core/document/types.js.map +1 -0
  47. package/dist/core/export/css.d.ts +13 -0
  48. package/dist/core/export/css.d.ts.map +1 -0
  49. package/dist/core/export/css.js +318 -0
  50. package/dist/core/export/css.js.map +1 -0
  51. package/dist/core/migrate/legacy-frontmatter.d.ts +14 -0
  52. package/dist/core/migrate/legacy-frontmatter.d.ts.map +1 -0
  53. package/dist/core/migrate/legacy-frontmatter.js +44 -0
  54. package/dist/core/migrate/legacy-frontmatter.js.map +1 -0
  55. package/dist/core/migrate/legacy-yaml.d.ts +9 -0
  56. package/dist/core/migrate/legacy-yaml.d.ts.map +1 -0
  57. package/dist/core/migrate/legacy-yaml.js +368 -0
  58. package/dist/core/migrate/legacy-yaml.js.map +1 -0
  59. package/dist/core/migrate/migrate-design-md.d.ts +3 -0
  60. package/dist/core/migrate/migrate-design-md.d.ts.map +1 -0
  61. package/dist/core/migrate/migrate-design-md.js +344 -0
  62. package/dist/core/migrate/migrate-design-md.js.map +1 -0
  63. package/dist/core/migrate/serialize-token-yaml.d.ts +3 -0
  64. package/dist/core/migrate/serialize-token-yaml.d.ts.map +1 -0
  65. package/dist/core/migrate/serialize-token-yaml.js +36 -0
  66. package/dist/core/migrate/serialize-token-yaml.js.map +1 -0
  67. package/dist/core/migrate/types.d.ts +23 -0
  68. package/dist/core/migrate/types.d.ts.map +1 -0
  69. package/dist/core/migrate/types.js +2 -0
  70. package/dist/core/migrate/types.js.map +1 -0
  71. package/dist/core/pipeline.d.ts +39 -0
  72. package/dist/core/pipeline.d.ts.map +1 -0
  73. package/dist/core/pipeline.js +80 -0
  74. package/dist/core/pipeline.js.map +1 -0
  75. package/dist/core/resolve/contrast.d.ts +4 -0
  76. package/dist/core/resolve/contrast.d.ts.map +1 -0
  77. package/dist/core/resolve/contrast.js +516 -0
  78. package/dist/core/resolve/contrast.js.map +1 -0
  79. package/dist/core/resolve/resolve-design-system.d.ts +10 -0
  80. package/dist/core/resolve/resolve-design-system.d.ts.map +1 -0
  81. package/dist/core/resolve/resolve-design-system.js +344 -0
  82. package/dist/core/resolve/resolve-design-system.js.map +1 -0
  83. package/dist/core/resolve/types.d.ts +20 -0
  84. package/dist/core/resolve/types.d.ts.map +1 -0
  85. package/dist/core/resolve/types.js +2 -0
  86. package/dist/core/resolve/types.js.map +1 -0
  87. package/dist/core/sections/registry.d.ts +16 -0
  88. package/dist/core/sections/registry.d.ts.map +1 -0
  89. package/dist/core/sections/registry.js +104 -0
  90. package/dist/core/sections/registry.js.map +1 -0
  91. package/dist/core/sections/tokens.d.ts +12 -0
  92. package/dist/core/sections/tokens.d.ts.map +1 -0
  93. package/dist/core/sections/tokens.js +2 -0
  94. package/dist/core/sections/tokens.js.map +1 -0
  95. package/dist/core/source/source-file.d.ts +17 -0
  96. package/dist/core/source/source-file.d.ts.map +1 -0
  97. package/dist/core/source/source-file.js +75 -0
  98. package/dist/core/source/source-file.js.map +1 -0
  99. package/dist/core/token-yaml/parse-token-yaml.d.ts +4 -0
  100. package/dist/core/token-yaml/parse-token-yaml.d.ts.map +1 -0
  101. package/dist/core/token-yaml/parse-token-yaml.js +502 -0
  102. package/dist/core/token-yaml/parse-token-yaml.js.map +1 -0
  103. package/dist/core/token-yaml/types.d.ts +31 -0
  104. package/dist/core/token-yaml/types.d.ts.map +1 -0
  105. package/dist/core/token-yaml/types.js +2 -0
  106. package/dist/core/token-yaml/types.js.map +1 -0
  107. package/dist/core/validation/document-structure.d.ts +9 -0
  108. package/dist/core/validation/document-structure.d.ts.map +1 -0
  109. package/dist/core/validation/document-structure.js +145 -0
  110. package/dist/core/validation/document-structure.js.map +1 -0
  111. package/dist/core/validation/section-schemas.d.ts +4 -0
  112. package/dist/core/validation/section-schemas.d.ts.map +1 -0
  113. package/dist/core/validation/section-schemas.js +624 -0
  114. package/dist/core/validation/section-schemas.js.map +1 -0
  115. package/dist/index.d.ts +7 -0
  116. package/dist/index.d.ts.map +1 -0
  117. package/dist/index.js +2 -0
  118. package/dist/index.js.map +1 -0
  119. package/package.json +61 -0
@@ -0,0 +1,624 @@
1
+ const componentProperties = new Set([
2
+ "backgroundColor",
3
+ "textColor",
4
+ "borderColor",
5
+ "typography",
6
+ "radius",
7
+ "borderWidth",
8
+ "borderStyle",
9
+ "padding",
10
+ "gap",
11
+ "height",
12
+ "width",
13
+ "minHeight",
14
+ "minWidth",
15
+ "shadow",
16
+ "zIndex",
17
+ "transitionDuration",
18
+ "transitionEasing",
19
+ ]);
20
+ const textStyleFields = [
21
+ "fontFamily",
22
+ "fontSize",
23
+ "fontWeight",
24
+ "lineHeight",
25
+ "letterSpacing",
26
+ "fontFeature",
27
+ "fontVariation",
28
+ ];
29
+ export function validateSectionSchemas(sectionTokens) {
30
+ const diagnostics = [];
31
+ for (const sectionToken of sectionTokens) {
32
+ validateKeyStyles(sectionToken, diagnostics);
33
+ switch (sectionToken.definition.group) {
34
+ case "metadata":
35
+ validateMetadata(sectionToken, diagnostics);
36
+ break;
37
+ case "colors":
38
+ validateColors(sectionToken, diagnostics);
39
+ break;
40
+ case "typography":
41
+ validateTypography(sectionToken, diagnostics);
42
+ break;
43
+ case "layout":
44
+ validateLayout(sectionToken, diagnostics);
45
+ break;
46
+ case "elevation":
47
+ validateElevation(sectionToken, diagnostics);
48
+ break;
49
+ case "shapes":
50
+ validateShapes(sectionToken, diagnostics);
51
+ break;
52
+ case "components":
53
+ validateComponents(sectionToken, diagnostics);
54
+ break;
55
+ case "iconography":
56
+ validateIconography(sectionToken, diagnostics);
57
+ break;
58
+ case "motion":
59
+ validateMotion(sectionToken, diagnostics);
60
+ break;
61
+ case undefined:
62
+ break;
63
+ }
64
+ }
65
+ return diagnostics;
66
+ }
67
+ function validateMetadata(sectionToken, diagnostics) {
68
+ warnUnknownKeys(sectionToken, diagnostics, ["themes", "defaultTheme"]);
69
+ }
70
+ function validateColors(sectionToken, diagnostics) {
71
+ const root = sectionToken.parsed.root;
72
+ if (countScalarLeaves(root) === 0) {
73
+ addError(sectionToken, diagnostics, "token-minimum", "Colors must define at least one color token.", root);
74
+ }
75
+ validateLeaves(sectionToken, diagnostics, root, [], (scalar, path) => {
76
+ if (scalar.valueType !== "string" || !isColorOrReference(String(scalar.value))) {
77
+ addError(sectionToken, diagnostics, "invalid-color", `${path} must be a supported color string or token reference.`, scalar, path);
78
+ }
79
+ });
80
+ warnMissingAnchors(sectionToken, diagnostics, root, ["primary", "surface", "on-surface"]);
81
+ }
82
+ function validateTypography(sectionToken, diagnostics) {
83
+ const root = sectionToken.parsed.root;
84
+ warnUnknownKeys(sectionToken, diagnostics, ["fontFamily", "baseFontSize", "measure", "text"]);
85
+ const baseFontSize = requireEntry(sectionToken, diagnostics, root, "baseFontSize");
86
+ if (baseFontSize !== undefined) {
87
+ validateDimensionScalar(sectionToken, diagnostics, baseFontSize.value, ["baseFontSize"]);
88
+ }
89
+ const text = requireEntry(sectionToken, diagnostics, root, "text");
90
+ if (text !== undefined) {
91
+ if (text.value.kind !== "map") {
92
+ addError(sectionToken, diagnostics, "invalid-value-type", "Typography.text must be a map.", text.value, "Typography.text");
93
+ }
94
+ else if (text.value.entries.length === 0) {
95
+ addError(sectionToken, diagnostics, "token-minimum", "Typography.text must contain at least one text style.", text.value, "Typography.text");
96
+ }
97
+ else {
98
+ validateTextStyles(sectionToken, diagnostics, text.value);
99
+ }
100
+ }
101
+ const fontFamily = findEntry(root, "fontFamily");
102
+ const fontFamilyMap = validateMapEntry(sectionToken, diagnostics, fontFamily, "Typography.fontFamily");
103
+ if (fontFamilyMap !== undefined) {
104
+ validateLeaves(sectionToken, diagnostics, fontFamilyMap, ["fontFamily"], (scalar, path) => {
105
+ if (scalar.valueType !== "string") {
106
+ addError(sectionToken, diagnostics, "invalid-value-type", `${path} must be a string or token reference.`, scalar, path);
107
+ }
108
+ });
109
+ }
110
+ const measure = findEntry(root, "measure");
111
+ const measureMap = validateMapEntry(sectionToken, diagnostics, measure, "Typography.measure");
112
+ if (measureMap !== undefined) {
113
+ validateLeaves(sectionToken, diagnostics, measureMap, ["measure"], (scalar, path) => {
114
+ validateDimensionScalar(sectionToken, diagnostics, scalar, path.split(".").slice(1));
115
+ });
116
+ }
117
+ const textMap = text?.value.kind === "map" ? text.value : undefined;
118
+ if (textMap !== undefined && !hasAnyEntry(textMap, ["body", "body-md"])) {
119
+ addWarning(sectionToken, diagnostics, "missing-anchor", "Typography should define a body or body-md text style.", textMap, "Typography.text");
120
+ }
121
+ }
122
+ function validateTextStyles(sectionToken, diagnostics, textMap) {
123
+ for (const style of textMap.entries) {
124
+ if (style.value.kind !== "map") {
125
+ addError(sectionToken, diagnostics, "invalid-value-type", `Typography text style '${style.key}' must be a map.`, style.value, `Typography.text.${style.key}`);
126
+ continue;
127
+ }
128
+ for (const required of ["fontFamily", "fontSize", "lineHeight"]) {
129
+ requireEntry(sectionToken, diagnostics, style.value, required, `Typography.text.${style.key}`);
130
+ }
131
+ warnUnknownMapKeys(sectionToken, diagnostics, style.value, textStyleFields, `Typography.text.${style.key}`);
132
+ for (const field of style.value.entries) {
133
+ const path = `Typography.text.${style.key}.${field.key}`;
134
+ if (field.value.kind !== "scalar") {
135
+ addError(sectionToken, diagnostics, "invalid-value-type", `${path} must be a scalar value.`, field.value, path);
136
+ continue;
137
+ }
138
+ if (field.key === "fontSize" || field.key === "letterSpacing") {
139
+ validateDimensionScalar(sectionToken, diagnostics, field.value, [
140
+ "text",
141
+ style.key,
142
+ field.key,
143
+ ]);
144
+ }
145
+ else if (field.key === "fontWeight" || field.key === "lineHeight") {
146
+ validateNumberDimensionOrReference(sectionToken, diagnostics, field.value, path);
147
+ }
148
+ else if (field.value.valueType !== "string") {
149
+ addError(sectionToken, diagnostics, "invalid-value-type", `${path} must be a string or token reference.`, field.value, path);
150
+ }
151
+ }
152
+ }
153
+ }
154
+ function validateLayout(sectionToken, diagnostics) {
155
+ const root = sectionToken.parsed.root;
156
+ warnUnknownKeys(sectionToken, diagnostics, ["spacing", "container", "grid", "breakpoint"]);
157
+ const spacing = requireEntry(sectionToken, diagnostics, root, "spacing");
158
+ const spacingMap = validateMapEntry(sectionToken, diagnostics, spacing, "Layout.spacing");
159
+ if (spacingMap !== undefined) {
160
+ validateDimensionMap(sectionToken, diagnostics, spacingMap, ["spacing"], true);
161
+ warnMissingAnchors(sectionToken, diagnostics, spacingMap, ["sm", "md", "lg"], "Layout.spacing");
162
+ }
163
+ for (const key of ["container", "breakpoint"]) {
164
+ const entry = findEntry(root, key);
165
+ const map = validateMapEntry(sectionToken, diagnostics, entry, `Layout.${key}`);
166
+ if (map !== undefined) {
167
+ validateDimensionMap(sectionToken, diagnostics, map, [key], false);
168
+ }
169
+ }
170
+ const grid = findEntry(root, "grid");
171
+ const gridMap = validateMapEntry(sectionToken, diagnostics, grid, "Layout.grid");
172
+ if (gridMap !== undefined) {
173
+ warnUnknownMapKeys(sectionToken, diagnostics, gridMap, ["columns", "gutter"], "Layout.grid");
174
+ for (const entry of gridMap.entries) {
175
+ if (entry.value.kind !== "scalar") {
176
+ addError(sectionToken, diagnostics, "invalid-value-type", `Layout.grid.${entry.key} must be a scalar value.`, entry.value, `Layout.grid.${entry.key}`);
177
+ }
178
+ else if (entry.key === "columns") {
179
+ validateNumberOrReference(sectionToken, diagnostics, entry.value, `Layout.grid.${entry.key}`);
180
+ }
181
+ else if (entry.key === "gutter") {
182
+ validateDimensionScalar(sectionToken, diagnostics, entry.value, ["grid", entry.key]);
183
+ }
184
+ }
185
+ }
186
+ }
187
+ function validateElevation(sectionToken, diagnostics) {
188
+ const root = sectionToken.parsed.root;
189
+ warnUnknownKeys(sectionToken, diagnostics, ["shadow", "zIndex"]);
190
+ const shadow = findEntry(root, "shadow");
191
+ const shadowMap = validateMapEntry(sectionToken, diagnostics, shadow, "Elevation.shadow");
192
+ if (shadowMap !== undefined) {
193
+ validateLeaves(sectionToken, diagnostics, shadowMap, ["shadow"], (scalar, path) => {
194
+ if (scalar.valueType !== "string") {
195
+ addError(sectionToken, diagnostics, "invalid-value-type", `${path} must be a string or token reference.`, scalar, path);
196
+ }
197
+ });
198
+ }
199
+ const zIndex = findEntry(root, "zIndex");
200
+ const zIndexMap = validateMapEntry(sectionToken, diagnostics, zIndex, "Elevation.zIndex");
201
+ if (zIndexMap !== undefined) {
202
+ validateLeaves(sectionToken, diagnostics, zIndexMap, ["zIndex"], (scalar, path) => {
203
+ validateNumberOrReference(sectionToken, diagnostics, scalar, path);
204
+ });
205
+ }
206
+ }
207
+ function validateShapes(sectionToken, diagnostics) {
208
+ const root = sectionToken.parsed.root;
209
+ warnUnknownKeys(sectionToken, diagnostics, ["radius", "borderWidth", "borderStyle"]);
210
+ const radius = requireEntry(sectionToken, diagnostics, root, "radius");
211
+ const radiusMap = validateMapEntry(sectionToken, diagnostics, radius, "Shapes.radius");
212
+ if (radiusMap !== undefined) {
213
+ validateDimensionMap(sectionToken, diagnostics, radiusMap, ["radius"], true);
214
+ warnMissingAnchors(sectionToken, diagnostics, radiusMap, ["none", "sm", "md", "full"], "Shapes.radius");
215
+ }
216
+ const borderWidth = findEntry(root, "borderWidth");
217
+ const borderWidthMap = validateMapEntry(sectionToken, diagnostics, borderWidth, "Shapes.borderWidth");
218
+ if (borderWidthMap !== undefined) {
219
+ validateDimensionMap(sectionToken, diagnostics, borderWidthMap, ["borderWidth"], false);
220
+ }
221
+ const borderStyle = findEntry(root, "borderStyle");
222
+ const borderStyleMap = validateMapEntry(sectionToken, diagnostics, borderStyle, "Shapes.borderStyle");
223
+ if (borderStyleMap !== undefined) {
224
+ validateLeaves(sectionToken, diagnostics, borderStyleMap, ["borderStyle"], (scalar, path) => {
225
+ if (scalar.valueType !== "string" ||
226
+ (!isWholeReference(String(scalar.value)) && !isBorderStyle(String(scalar.value)))) {
227
+ addError(sectionToken, diagnostics, "invalid-border-style", `${path} must be a CSS border-style keyword or token reference.`, scalar, path);
228
+ }
229
+ });
230
+ }
231
+ }
232
+ function validateComponents(sectionToken, diagnostics) {
233
+ for (const component of sectionToken.parsed.root.entries) {
234
+ if (component.value.kind !== "map") {
235
+ addError(sectionToken, diagnostics, "invalid-value-type", `Component '${component.key}' must be a map.`, component.value, `Components.${component.key}`);
236
+ continue;
237
+ }
238
+ const base = findEntry(component.value, "base");
239
+ const variants = findEntry(component.value, "variants");
240
+ if (base === undefined && variants === undefined) {
241
+ addError(sectionToken, diagnostics, "component-shape", `Component '${component.key}' must define base or variants.`, component.value, `Components.${component.key}`);
242
+ }
243
+ warnUnknownMapKeys(sectionToken, diagnostics, component.value, ["base", "variants"], `Components.${component.key}`);
244
+ const baseMap = validateMapEntry(sectionToken, diagnostics, base, `Components.${component.key}.base`);
245
+ if (baseMap !== undefined) {
246
+ validateComponentProperties(sectionToken, diagnostics, baseMap, `Components.${component.key}.base`);
247
+ }
248
+ const variantsMap = validateMapEntry(sectionToken, diagnostics, variants, `Components.${component.key}.variants`);
249
+ if (variantsMap !== undefined) {
250
+ for (const axis of variantsMap.entries) {
251
+ if (axis.value.kind !== "map") {
252
+ addError(sectionToken, diagnostics, "invalid-value-type", `Variant axis '${axis.key}' must be a map.`, axis.value, `Components.${component.key}.variants.${axis.key}`);
253
+ continue;
254
+ }
255
+ for (const variant of axis.value.entries) {
256
+ if (variant.value.kind === "map") {
257
+ validateComponentProperties(sectionToken, diagnostics, variant.value, `Components.${component.key}.variants.${axis.key}.${variant.key}`);
258
+ }
259
+ else {
260
+ addError(sectionToken, diagnostics, "invalid-value-type", `Variant '${variant.key}' must be a map.`, variant.value, `Components.${component.key}.variants.${axis.key}.${variant.key}`);
261
+ }
262
+ }
263
+ }
264
+ }
265
+ }
266
+ }
267
+ function validateComponentProperties(sectionToken, diagnostics, map, basePath) {
268
+ for (const property of map.entries) {
269
+ const path = `${basePath}.${property.key}`;
270
+ if (!componentProperties.has(property.key)) {
271
+ addWarning(sectionToken, diagnostics, "unknown-component-property", `Unknown component property '${property.key}' is preserved but not exported.`, property.value, path);
272
+ }
273
+ if (property.value.kind !== "scalar") {
274
+ addError(sectionToken, diagnostics, "invalid-value-type", `${path} must be a scalar value.`, property.value, path);
275
+ }
276
+ }
277
+ }
278
+ function validateIconography(sectionToken, diagnostics) {
279
+ const root = sectionToken.parsed.root;
280
+ warnUnknownKeys(sectionToken, diagnostics, [
281
+ "library",
282
+ "style",
283
+ "strokeWidth",
284
+ "grid",
285
+ "size",
286
+ "color",
287
+ ]);
288
+ const library = requireEntry(sectionToken, diagnostics, root, "library");
289
+ const libraryScalar = validateScalarEntry(sectionToken, diagnostics, library, "Iconography.library");
290
+ if (libraryScalar !== undefined && libraryScalar.valueType !== "string") {
291
+ addError(sectionToken, diagnostics, "invalid-value-type", "Iconography.library must be a string.", libraryScalar, "Iconography.library");
292
+ }
293
+ const strokeWidth = findEntry(root, "strokeWidth");
294
+ const strokeWidthScalar = validateScalarEntry(sectionToken, diagnostics, strokeWidth, "Iconography.strokeWidth");
295
+ if (strokeWidthScalar !== undefined) {
296
+ validateNumberOrReference(sectionToken, diagnostics, strokeWidthScalar, "Iconography.strokeWidth");
297
+ }
298
+ const grid = findEntry(root, "grid");
299
+ const gridScalar = validateScalarEntry(sectionToken, diagnostics, grid, "Iconography.grid");
300
+ if (gridScalar !== undefined) {
301
+ validateDimensionScalar(sectionToken, diagnostics, gridScalar, ["grid"]);
302
+ }
303
+ const size = findEntry(root, "size");
304
+ const sizeMap = validateMapEntry(sectionToken, diagnostics, size, "Iconography.size");
305
+ if (sizeMap !== undefined) {
306
+ validateDimensionMap(sectionToken, diagnostics, sizeMap, ["size"], false);
307
+ }
308
+ const style = findEntry(root, "style");
309
+ const styleScalar = validateScalarEntry(sectionToken, diagnostics, style, "Iconography.style");
310
+ if (styleScalar !== undefined && styleScalar.valueType === "string") {
311
+ const value = String(styleScalar.value);
312
+ if (!isWholeReference(value) &&
313
+ !["outlined", "filled", "rounded", "sharp", "duotone"].includes(value)) {
314
+ addWarning(sectionToken, diagnostics, "unknown-icon-style", `Unknown icon style '${value}' is preserved.`, styleScalar, "Iconography.style");
315
+ }
316
+ }
317
+ const color = findEntry(root, "color");
318
+ const colorScalar = validateScalarEntry(sectionToken, diagnostics, color, "Iconography.color");
319
+ if (colorScalar !== undefined) {
320
+ const value = String(colorScalar.value);
321
+ if (colorScalar.valueType !== "string" || !isColorOrReference(value)) {
322
+ addError(sectionToken, diagnostics, "invalid-color", "Iconography.color must be a supported color string or token reference.", colorScalar, "Iconography.color");
323
+ }
324
+ }
325
+ }
326
+ function validateMotion(sectionToken, diagnostics) {
327
+ const root = sectionToken.parsed.root;
328
+ warnUnknownKeys(sectionToken, diagnostics, ["duration", "easing", "reducedMotion"]);
329
+ const duration = findEntry(root, "duration");
330
+ const durationMap = validateMapEntry(sectionToken, diagnostics, duration, "Motion.duration");
331
+ if (durationMap !== undefined) {
332
+ validateDurationMap(sectionToken, diagnostics, durationMap, ["duration"]);
333
+ }
334
+ const reducedMotion = findEntry(root, "reducedMotion");
335
+ const reducedMotionMap = validateMapEntry(sectionToken, diagnostics, reducedMotion, "Motion.reducedMotion");
336
+ if (reducedMotionMap !== undefined) {
337
+ validateDurationMap(sectionToken, diagnostics, reducedMotionMap, ["reducedMotion"]);
338
+ }
339
+ const easing = findEntry(root, "easing");
340
+ const easingMap = validateMapEntry(sectionToken, diagnostics, easing, "Motion.easing");
341
+ if (easingMap !== undefined) {
342
+ validateLeaves(sectionToken, diagnostics, easingMap, ["easing"], (scalar, path) => {
343
+ if (scalar.valueType !== "string" ||
344
+ (!isWholeReference(String(scalar.value)) && !isEasing(String(scalar.value)))) {
345
+ addError(sectionToken, diagnostics, "invalid-easing", `${path} must be a CSS easing keyword/function or token reference.`, scalar, path);
346
+ }
347
+ });
348
+ }
349
+ warnReducedMotion(sectionToken, diagnostics, durationMap, reducedMotionMap);
350
+ }
351
+ function warnReducedMotion(sectionToken, diagnostics, duration, reducedMotion) {
352
+ if (duration?.kind !== "map") {
353
+ return;
354
+ }
355
+ const reducedMotionKeys = reducedMotion?.kind === "map"
356
+ ? new Set(reducedMotion.entries.map((entry) => entry.key))
357
+ : new Set();
358
+ for (const entry of duration.entries) {
359
+ if (entry.value.kind !== "scalar" || entry.value.valueType !== "string") {
360
+ continue;
361
+ }
362
+ const milliseconds = parseTimeMs(String(entry.value.value));
363
+ if (milliseconds !== undefined && milliseconds > 200 && !reducedMotionKeys.has(entry.key)) {
364
+ addWarning(sectionToken, diagnostics, "missing-reduced-motion", `Motion duration '${entry.key}' should define a same-key reducedMotion value.`, entry.value, `Motion.duration.${entry.key}`);
365
+ }
366
+ }
367
+ }
368
+ function validateKeyStyles(sectionToken, diagnostics) {
369
+ validateMapKeyStyles(sectionToken, diagnostics, sectionToken.parsed.root, []);
370
+ }
371
+ function validateMapKeyStyles(sectionToken, diagnostics, map, parentSegments) {
372
+ for (const entry of map.entries) {
373
+ if (!isStructuralKey(sectionToken, parentSegments, entry.key) && !isRecommendedKey(entry)) {
374
+ diagnostics.push({
375
+ severity: "warning",
376
+ rule: "key-style",
377
+ path: [sectionToken.section.name, ...parentSegments, entry.key].join("."),
378
+ message: `Key '${entry.key}' should use kebab-case or a double-quoted numeric scale key.`,
379
+ span: entry.keySpan,
380
+ });
381
+ }
382
+ if (entry.value.kind === "map") {
383
+ validateMapKeyStyles(sectionToken, diagnostics, entry.value, [...parentSegments, entry.key]);
384
+ }
385
+ }
386
+ }
387
+ function isStructuralKey(sectionToken, parentSegments, key) {
388
+ switch (sectionToken.definition.group) {
389
+ case "metadata":
390
+ return parentSegments.length === 0 && ["themes", "defaultTheme"].includes(key);
391
+ case "typography":
392
+ return isTypographyStructuralKey(parentSegments, key);
393
+ case "layout":
394
+ return isLayoutStructuralKey(parentSegments, key);
395
+ case "elevation":
396
+ return parentSegments.length === 0 && ["shadow", "zIndex"].includes(key);
397
+ case "shapes":
398
+ return parentSegments.length === 0 && ["radius", "borderWidth", "borderStyle"].includes(key);
399
+ case "components":
400
+ return isComponentStructuralKey(parentSegments, key);
401
+ case "iconography":
402
+ return (parentSegments.length === 0 &&
403
+ ["library", "style", "strokeWidth", "grid", "size", "color"].includes(key));
404
+ case "motion":
405
+ return parentSegments.length === 0 && ["duration", "easing", "reducedMotion"].includes(key);
406
+ case "colors":
407
+ case undefined:
408
+ return false;
409
+ }
410
+ return false;
411
+ }
412
+ function isTypographyStructuralKey(parentSegments, key) {
413
+ if (parentSegments.length === 0) {
414
+ return ["fontFamily", "baseFontSize", "measure", "text"].includes(key);
415
+ }
416
+ return (parentSegments.length === 2 &&
417
+ parentSegments[0] === "text" &&
418
+ textStyleFields.includes(key));
419
+ }
420
+ function isLayoutStructuralKey(parentSegments, key) {
421
+ if (parentSegments.length === 0) {
422
+ return ["spacing", "container", "grid", "breakpoint"].includes(key);
423
+ }
424
+ return (parentSegments.length === 1 &&
425
+ parentSegments[0] === "grid" &&
426
+ ["columns", "gutter"].includes(key));
427
+ }
428
+ function isComponentStructuralKey(parentSegments, key) {
429
+ if (parentSegments.length === 1) {
430
+ return key === "base" || key === "variants";
431
+ }
432
+ const isBaseProperty = parentSegments.length === 2 && parentSegments[1] === "base";
433
+ const isVariantProperty = parentSegments.length === 4 && parentSegments[1] === "variants";
434
+ return (isBaseProperty || isVariantProperty) && componentProperties.has(key);
435
+ }
436
+ function isRecommendedKey(entry) {
437
+ if (entry.quotedKey && /^\d+$/.test(entry.key)) {
438
+ return true;
439
+ }
440
+ if (!entry.quotedKey && /^-?\d+(?:\.\d+)?$/.test(entry.key)) {
441
+ return true;
442
+ }
443
+ return /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/.test(entry.key);
444
+ }
445
+ function validateDimensionMap(sectionToken, diagnostics, map, segments, allowZero) {
446
+ if (map.entries.length === 0) {
447
+ addError(sectionToken, diagnostics, "token-minimum", `${sectionToken.section.name}.${segments.join(".")} must contain at least one token.`, map);
448
+ }
449
+ validateLeaves(sectionToken, diagnostics, map, segments, (scalar, path) => {
450
+ validateDimensionScalar(sectionToken, diagnostics, scalar, path.split(".").slice(1), allowZero);
451
+ });
452
+ }
453
+ function validateDurationMap(sectionToken, diagnostics, map, segments) {
454
+ validateLeaves(sectionToken, diagnostics, map, segments, (scalar, path) => {
455
+ if (!isTimeOrReference(scalar)) {
456
+ addError(sectionToken, diagnostics, "invalid-time", `${path} must be a CSS time string, numeric 0, or token reference.`, scalar, path);
457
+ }
458
+ });
459
+ }
460
+ function validateDimensionScalar(sectionToken, diagnostics, scalar, segments, allowZero = true) {
461
+ const path = `${sectionToken.section.name}.${segments.join(".")}`;
462
+ if (scalar.kind !== "scalar" || !isDimensionOrReference(scalar, allowZero)) {
463
+ addError(sectionToken, diagnostics, "invalid-dimension", `${path} must be a CSS dimension, numeric 0, or token reference.`, scalar, path);
464
+ }
465
+ }
466
+ function validateNumberOrReference(sectionToken, diagnostics, scalar, path) {
467
+ if (scalar.valueType !== "number" && !isWholeReference(String(scalar.value))) {
468
+ addError(sectionToken, diagnostics, "invalid-value-type", `${path} must be a number or token reference.`, scalar, path);
469
+ }
470
+ }
471
+ function validateNumberDimensionOrReference(sectionToken, diagnostics, scalar, path) {
472
+ if (scalar.valueType === "number" ||
473
+ isWholeReference(String(scalar.value)) ||
474
+ isDimension(String(scalar.value))) {
475
+ return;
476
+ }
477
+ addError(sectionToken, diagnostics, "invalid-value-type", `${path} must be a number, CSS dimension, or token reference.`, scalar, path);
478
+ }
479
+ function validateLeaves(sectionToken, diagnostics, node, segments, validate) {
480
+ if (node.kind === "scalar") {
481
+ validate(node, `${sectionToken.section.name}.${segments.join(".")}`);
482
+ return;
483
+ }
484
+ if (node.kind === "list") {
485
+ addError(sectionToken, diagnostics, "invalid-value-type", `${sectionToken.section.name}.${segments.join(".")} must not be a list.`, node);
486
+ return;
487
+ }
488
+ for (const entry of node.entries) {
489
+ validateLeaves(sectionToken, diagnostics, entry.value, [...segments, entry.key], validate);
490
+ }
491
+ }
492
+ function validateMapEntry(sectionToken, diagnostics, entry, path) {
493
+ if (entry === undefined) {
494
+ return undefined;
495
+ }
496
+ if (entry.value.kind !== "map") {
497
+ addError(sectionToken, diagnostics, "invalid-value-type", `${path} must be a map.`, entry.value, path);
498
+ return undefined;
499
+ }
500
+ return entry.value;
501
+ }
502
+ function validateScalarEntry(sectionToken, diagnostics, entry, path) {
503
+ if (entry === undefined) {
504
+ return undefined;
505
+ }
506
+ if (entry.value.kind !== "scalar") {
507
+ addError(sectionToken, diagnostics, "invalid-value-type", `${path} must be a scalar value.`, entry.value, path);
508
+ return undefined;
509
+ }
510
+ return entry.value;
511
+ }
512
+ function requireEntry(sectionToken, diagnostics, map, key, basePath = sectionToken.section.name) {
513
+ const entry = findEntry(map, key);
514
+ if (entry === undefined) {
515
+ addError(sectionToken, diagnostics, "required-token", `${basePath} must define '${key}'.`, map, `${basePath}.${key}`);
516
+ }
517
+ return entry;
518
+ }
519
+ function warnUnknownKeys(sectionToken, diagnostics, allowedKeys) {
520
+ warnUnknownMapKeys(sectionToken, diagnostics, sectionToken.parsed.root, allowedKeys, sectionToken.section.name);
521
+ }
522
+ function warnUnknownMapKeys(sectionToken, diagnostics, map, allowedKeys, basePath) {
523
+ const allowed = new Set(allowedKeys);
524
+ for (const entry of map.entries) {
525
+ if (!allowed.has(entry.key)) {
526
+ addWarning(sectionToken, diagnostics, "unknown-key", `Unknown key '${entry.key}' is preserved but not exported by current tooling.`, entry.value, `${basePath}.${entry.key}`);
527
+ }
528
+ }
529
+ }
530
+ function warnMissingAnchors(sectionToken, diagnostics, map, anchors, path = sectionToken.section.name) {
531
+ const missing = anchors.filter((anchor) => findEntry(map, anchor) === undefined);
532
+ if (missing.length > 0) {
533
+ addWarning(sectionToken, diagnostics, "missing-anchor", `${path} should define recommended anchors: ${missing.join(", ")}.`, map, path);
534
+ }
535
+ }
536
+ function countScalarLeaves(node) {
537
+ if (node.kind === "scalar") {
538
+ return 1;
539
+ }
540
+ if (node.kind === "list") {
541
+ return node.items.length;
542
+ }
543
+ return node.entries.reduce((count, entry) => count + countScalarLeaves(entry.value), 0);
544
+ }
545
+ function findEntry(map, key) {
546
+ return map.entries.find((entry) => entry.key === key);
547
+ }
548
+ function hasAnyEntry(map, keys) {
549
+ return keys.some((key) => findEntry(map, key) !== undefined);
550
+ }
551
+ function addError(sectionToken, diagnostics, rule, message, node, path) {
552
+ addDiagnostic(sectionToken, diagnostics, "error", rule, message, node, path);
553
+ }
554
+ function addWarning(sectionToken, diagnostics, rule, message, node, path) {
555
+ addDiagnostic(sectionToken, diagnostics, "warning", rule, message, node, path);
556
+ }
557
+ function addDiagnostic(sectionToken, diagnostics, severity, rule, message, node, path) {
558
+ const diagnostic = {
559
+ severity,
560
+ rule,
561
+ message,
562
+ span: node.span,
563
+ };
564
+ if (path !== undefined) {
565
+ diagnostic.path = path;
566
+ }
567
+ else {
568
+ diagnostic.path = sectionToken.section.name;
569
+ }
570
+ diagnostics.push(diagnostic);
571
+ }
572
+ function isColorOrReference(value) {
573
+ return (isWholeReference(value) ||
574
+ /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value) ||
575
+ /^(?:rgb|rgba|hsl|hsla|oklab|oklch)\([^)]*\)$/.test(value) ||
576
+ /^color\(display-p3 [^)]*\)$/.test(value) ||
577
+ value === "transparent");
578
+ }
579
+ function isDimensionOrReference(scalar, allowZero) {
580
+ if (scalar.valueType === "number") {
581
+ return allowZero && scalar.value === 0;
582
+ }
583
+ const value = String(scalar.value);
584
+ return isWholeReference(value) || isDimension(value);
585
+ }
586
+ function isTimeOrReference(scalar) {
587
+ if (scalar.valueType === "number") {
588
+ return scalar.value === 0;
589
+ }
590
+ const value = String(scalar.value);
591
+ return isWholeReference(value) || parseTimeMs(value) !== undefined;
592
+ }
593
+ function parseTimeMs(value) {
594
+ const match = /^(\d+(?:\.\d+)?)(ms|s)$/.exec(value);
595
+ if (match?.[1] === undefined || match[2] === undefined) {
596
+ return undefined;
597
+ }
598
+ const amount = Number(match[1]);
599
+ return match[2] === "s" ? amount * 1000 : amount;
600
+ }
601
+ function isDimension(value) {
602
+ return /^-?\d+(?:\.\d+)?(?:px|rem|em|ch|ex|vw|vh|vmin|vmax|%|svh|lvh|dvh)$/.test(value);
603
+ }
604
+ function isBorderStyle(value) {
605
+ return [
606
+ "none",
607
+ "hidden",
608
+ "dotted",
609
+ "dashed",
610
+ "solid",
611
+ "double",
612
+ "groove",
613
+ "ridge",
614
+ "inset",
615
+ "outset",
616
+ ].includes(value);
617
+ }
618
+ function isEasing(value) {
619
+ return (["linear", "ease", "ease-in", "ease-out", "ease-in-out", "step-start", "step-end"].includes(value) || /^(?:cubic-bezier|linear|steps)\([^)]*\)$/.test(value));
620
+ }
621
+ function isWholeReference(value) {
622
+ return /^\{[^{}\s]+\}$/.test(value);
623
+ }
624
+ //# sourceMappingURL=section-schemas.js.map