@bridge-ds/stylelint-plugin 0.0.0-stage → 0.2.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/README.md +47 -2
- package/dist/index.d.ts +18 -0
- package/dist/index.js +1349 -0
- package/package.json +59 -4
package/dist/index.js
ADDED
|
@@ -0,0 +1,1349 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import stylelint from "stylelint";
|
|
3
|
+
|
|
4
|
+
// ../core/src/values.ts
|
|
5
|
+
var ALIAS_RE = /^\{([^{}]+)\}$/;
|
|
6
|
+
var isAlias = (v) => typeof v === "string" && ALIAS_RE.test(v);
|
|
7
|
+
var aliasTarget = (v) => ALIAS_RE.exec(v)?.[1] ?? v;
|
|
8
|
+
function hexToRgba(hex) {
|
|
9
|
+
const m = /^#?([0-9a-f]{3,8})$/i.exec(hex.trim());
|
|
10
|
+
if (!m) return void 0;
|
|
11
|
+
let h = m[1];
|
|
12
|
+
if (h.length === 3 || h.length === 4) h = [...h].map((c) => c + c).join("");
|
|
13
|
+
if (h.length !== 6 && h.length !== 8) return void 0;
|
|
14
|
+
const n = (idx) => parseInt(h.slice(idx, idx + 2), 16) / 255;
|
|
15
|
+
return { r: n(0), g: n(2), b: n(4), a: h.length === 8 ? n(6) : 1 };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// ../core/src/audit/color.ts
|
|
19
|
+
var NAMED_COLORS = {
|
|
20
|
+
aliceblue: "#f0f8ff",
|
|
21
|
+
antiquewhite: "#faebd7",
|
|
22
|
+
aqua: "#00ffff",
|
|
23
|
+
aquamarine: "#7fffd4",
|
|
24
|
+
azure: "#f0ffff",
|
|
25
|
+
beige: "#f5f5dc",
|
|
26
|
+
bisque: "#ffe4c4",
|
|
27
|
+
black: "#000000",
|
|
28
|
+
blanchedalmond: "#ffebcd",
|
|
29
|
+
blue: "#0000ff",
|
|
30
|
+
blueviolet: "#8a2be2",
|
|
31
|
+
brown: "#a52a2a",
|
|
32
|
+
burlywood: "#deb887",
|
|
33
|
+
cadetblue: "#5f9ea0",
|
|
34
|
+
chartreuse: "#7fff00",
|
|
35
|
+
chocolate: "#d2691e",
|
|
36
|
+
coral: "#ff7f50",
|
|
37
|
+
cornflowerblue: "#6495ed",
|
|
38
|
+
cornsilk: "#fff8dc",
|
|
39
|
+
crimson: "#dc143c",
|
|
40
|
+
cyan: "#00ffff",
|
|
41
|
+
darkblue: "#00008b",
|
|
42
|
+
darkcyan: "#008b8b",
|
|
43
|
+
darkgoldenrod: "#b8860b",
|
|
44
|
+
darkgray: "#a9a9a9",
|
|
45
|
+
darkgreen: "#006400",
|
|
46
|
+
darkgrey: "#a9a9a9",
|
|
47
|
+
darkkhaki: "#bdb76b",
|
|
48
|
+
darkmagenta: "#8b008b",
|
|
49
|
+
darkolivegreen: "#556b2f",
|
|
50
|
+
darkorange: "#ff8c00",
|
|
51
|
+
darkorchid: "#9932cc",
|
|
52
|
+
darkred: "#8b0000",
|
|
53
|
+
darksalmon: "#e9967a",
|
|
54
|
+
darkseagreen: "#8fbc8f",
|
|
55
|
+
darkslateblue: "#483d8b",
|
|
56
|
+
darkslategray: "#2f4f4f",
|
|
57
|
+
darkslategrey: "#2f4f4f",
|
|
58
|
+
darkturquoise: "#00ced1",
|
|
59
|
+
darkviolet: "#9400d3",
|
|
60
|
+
deeppink: "#ff1493",
|
|
61
|
+
deepskyblue: "#00bfff",
|
|
62
|
+
dimgray: "#696969",
|
|
63
|
+
dimgrey: "#696969",
|
|
64
|
+
dodgerblue: "#1e90ff",
|
|
65
|
+
firebrick: "#b22222",
|
|
66
|
+
floralwhite: "#fffaf0",
|
|
67
|
+
forestgreen: "#228b22",
|
|
68
|
+
fuchsia: "#ff00ff",
|
|
69
|
+
gainsboro: "#dcdcdc",
|
|
70
|
+
ghostwhite: "#f8f8ff",
|
|
71
|
+
gold: "#ffd700",
|
|
72
|
+
goldenrod: "#daa520",
|
|
73
|
+
gray: "#808080",
|
|
74
|
+
green: "#008000",
|
|
75
|
+
greenyellow: "#adff2f",
|
|
76
|
+
grey: "#808080",
|
|
77
|
+
honeydew: "#f0fff0",
|
|
78
|
+
hotpink: "#ff69b4",
|
|
79
|
+
indianred: "#cd5c5c",
|
|
80
|
+
indigo: "#4b0082",
|
|
81
|
+
ivory: "#fffff0",
|
|
82
|
+
khaki: "#f0e68c",
|
|
83
|
+
lavender: "#e6e6fa",
|
|
84
|
+
lavenderblush: "#fff0f5",
|
|
85
|
+
lawngreen: "#7cfc00",
|
|
86
|
+
lemonchiffon: "#fffacd",
|
|
87
|
+
lightblue: "#add8e6",
|
|
88
|
+
lightcoral: "#f08080",
|
|
89
|
+
lightcyan: "#e0ffff",
|
|
90
|
+
lightgoldenrodyellow: "#fafad2",
|
|
91
|
+
lightgray: "#d3d3d3",
|
|
92
|
+
lightgreen: "#90ee90",
|
|
93
|
+
lightgrey: "#d3d3d3",
|
|
94
|
+
lightpink: "#ffb6c1",
|
|
95
|
+
lightsalmon: "#ffa07a",
|
|
96
|
+
lightseagreen: "#20b2aa",
|
|
97
|
+
lightskyblue: "#87cefa",
|
|
98
|
+
lightslategray: "#778899",
|
|
99
|
+
lightslategrey: "#778899",
|
|
100
|
+
lightsteelblue: "#b0c4de",
|
|
101
|
+
lightyellow: "#ffffe0",
|
|
102
|
+
lime: "#00ff00",
|
|
103
|
+
limegreen: "#32cd32",
|
|
104
|
+
linen: "#faf0e6",
|
|
105
|
+
magenta: "#ff00ff",
|
|
106
|
+
maroon: "#800000",
|
|
107
|
+
mediumaquamarine: "#66cdaa",
|
|
108
|
+
mediumblue: "#0000cd",
|
|
109
|
+
mediumorchid: "#ba55d3",
|
|
110
|
+
mediumpurple: "#9370db",
|
|
111
|
+
mediumseagreen: "#3cb371",
|
|
112
|
+
mediumslateblue: "#7b68ee",
|
|
113
|
+
mediumspringgreen: "#00fa9a",
|
|
114
|
+
mediumturquoise: "#48d1cc",
|
|
115
|
+
mediumvioletred: "#c71585",
|
|
116
|
+
midnightblue: "#191970",
|
|
117
|
+
mintcream: "#f5fffa",
|
|
118
|
+
mistyrose: "#ffe4e1",
|
|
119
|
+
moccasin: "#ffe4b5",
|
|
120
|
+
navajowhite: "#ffdead",
|
|
121
|
+
navy: "#000080",
|
|
122
|
+
oldlace: "#fdf5e6",
|
|
123
|
+
olive: "#808000",
|
|
124
|
+
olivedrab: "#6b8e23",
|
|
125
|
+
orange: "#ffa500",
|
|
126
|
+
orangered: "#ff4500",
|
|
127
|
+
orchid: "#da70d6",
|
|
128
|
+
palegoldenrod: "#eee8aa",
|
|
129
|
+
palegreen: "#98fb98",
|
|
130
|
+
paleturquoise: "#afeeee",
|
|
131
|
+
palevioletred: "#db7093",
|
|
132
|
+
papayawhip: "#ffefd5",
|
|
133
|
+
peachpuff: "#ffdab9",
|
|
134
|
+
peru: "#cd853f",
|
|
135
|
+
pink: "#ffc0cb",
|
|
136
|
+
plum: "#dda0dd",
|
|
137
|
+
powderblue: "#b0e0e6",
|
|
138
|
+
purple: "#800080",
|
|
139
|
+
rebeccapurple: "#663399",
|
|
140
|
+
red: "#ff0000",
|
|
141
|
+
rosybrown: "#bc8f8f",
|
|
142
|
+
royalblue: "#4169e1",
|
|
143
|
+
saddlebrown: "#8b4513",
|
|
144
|
+
salmon: "#fa8072",
|
|
145
|
+
sandybrown: "#f4a460",
|
|
146
|
+
seagreen: "#2e8b57",
|
|
147
|
+
seashell: "#fff5ee",
|
|
148
|
+
sienna: "#a0522d",
|
|
149
|
+
silver: "#c0c0c0",
|
|
150
|
+
skyblue: "#87ceeb",
|
|
151
|
+
slateblue: "#6a5acd",
|
|
152
|
+
slategray: "#708090",
|
|
153
|
+
slategrey: "#708090",
|
|
154
|
+
snow: "#fffafa",
|
|
155
|
+
springgreen: "#00ff7f",
|
|
156
|
+
steelblue: "#4682b4",
|
|
157
|
+
tan: "#d2b48c",
|
|
158
|
+
teal: "#008080",
|
|
159
|
+
thistle: "#d8bfd8",
|
|
160
|
+
tomato: "#ff6347",
|
|
161
|
+
turquoise: "#40e0d0",
|
|
162
|
+
violet: "#ee82ee",
|
|
163
|
+
wheat: "#f5deb3",
|
|
164
|
+
white: "#ffffff",
|
|
165
|
+
whitesmoke: "#f5f5f5",
|
|
166
|
+
yellow: "#ffff00",
|
|
167
|
+
yellowgreen: "#9acd32"
|
|
168
|
+
};
|
|
169
|
+
var num = (s, scale) => s.endsWith("%") ? parseFloat(s) / 100 * scale : parseFloat(s);
|
|
170
|
+
function hslToRgb(h, s, l) {
|
|
171
|
+
const hh = (h % 360 + 360) % 360 / 360;
|
|
172
|
+
if (s === 0) return [l, l, l];
|
|
173
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
174
|
+
const p = 2 * l - q;
|
|
175
|
+
const f = (t) => {
|
|
176
|
+
let x = t;
|
|
177
|
+
if (x < 0) x += 1;
|
|
178
|
+
if (x > 1) x -= 1;
|
|
179
|
+
if (x < 1 / 6) return p + (q - p) * 6 * x;
|
|
180
|
+
if (x < 1 / 2) return q;
|
|
181
|
+
if (x < 2 / 3) return p + (q - p) * (2 / 3 - x) * 6;
|
|
182
|
+
return p;
|
|
183
|
+
};
|
|
184
|
+
return [f(hh + 1 / 3), f(hh), f(hh - 1 / 3)];
|
|
185
|
+
}
|
|
186
|
+
function parseCssColor(input) {
|
|
187
|
+
const s = input.trim().toLowerCase();
|
|
188
|
+
if (s.startsWith("#")) return hexToRgba(s);
|
|
189
|
+
if (NAMED_COLORS[s]) return hexToRgba(NAMED_COLORS[s]);
|
|
190
|
+
const fn = /^(rgba?|hsla?)\(\s*([^)]*)\)$/.exec(s);
|
|
191
|
+
const bare = /^(-?[\d.]+)(deg)?\s+([\d.]+)%\s+([\d.]+)%(\s*\/\s*([\d.]+%?))?$/.exec(s);
|
|
192
|
+
if (bare) {
|
|
193
|
+
const [r2, g2, b2] = hslToRgb(parseFloat(bare[1]), parseFloat(bare[3]) / 100, parseFloat(bare[4]) / 100);
|
|
194
|
+
return { r: r2, g: g2, b: b2, a: bare[6] ? num(bare[6], 1) : 1 };
|
|
195
|
+
}
|
|
196
|
+
if (!fn) return void 0;
|
|
197
|
+
const parts = fn[2].split(/[\s,/]+/).filter(Boolean);
|
|
198
|
+
if (parts.length < 3 || parts.some((p) => !/^-?[\d.]+(%|deg)?$/.test(p))) return void 0;
|
|
199
|
+
const alpha = parts[3] !== void 0 ? num(parts[3], 1) : 1;
|
|
200
|
+
if (fn[1].startsWith("rgb")) {
|
|
201
|
+
const [r2, g2, b2] = parts.slice(0, 3).map((p) => num(p, 255) / 255);
|
|
202
|
+
return { r: r2, g: g2, b: b2, a: alpha };
|
|
203
|
+
}
|
|
204
|
+
const [r, g, b] = hslToRgb(parseFloat(parts[0]), num(parts[1], 1), num(parts[2], 1));
|
|
205
|
+
return { r, g, b, a: alpha };
|
|
206
|
+
}
|
|
207
|
+
var lin = (c) => c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
208
|
+
function toOklab(c) {
|
|
209
|
+
const r = lin(c.r);
|
|
210
|
+
const g = lin(c.g);
|
|
211
|
+
const b = lin(c.b);
|
|
212
|
+
const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
|
|
213
|
+
const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
|
|
214
|
+
const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
|
|
215
|
+
return {
|
|
216
|
+
L: 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
|
|
217
|
+
a: 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
|
|
218
|
+
b: 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function deltaE(x, y) {
|
|
222
|
+
const p = toOklab(x);
|
|
223
|
+
const q = toOklab(y);
|
|
224
|
+
return Math.hypot(p.L - q.L, p.a - q.a, p.b - q.b) * 100 + Math.abs(x.a - y.a) * 50;
|
|
225
|
+
}
|
|
226
|
+
var COLOR_LITERAL_RE = /#(?:[0-9a-fA-F]{8}|[0-9a-fA-F]{6}|[0-9a-fA-F]{3,4})\b|\b(?:rgba?|hsla?)\([^()]*\)/g;
|
|
227
|
+
|
|
228
|
+
// ../core/src/audit/scan-value.ts
|
|
229
|
+
var COLOR_PROP = /^(color|background(-color|-image)?|border(-(top|right|bottom|left|block|inline)(-(start|end))?)?(-color)?|outline(-color)?|fill|stroke|text-decoration(-color)?|caret-color|accent-color|column-rule(-color)?|stop-color|flood-color|lighting-color|-webkit-text-fill-color|scrollbar-color|box-shadow|text-shadow)$/;
|
|
230
|
+
var SPACING_PROP = /^(padding|margin|gap|row-gap|column-gap|grid-gap|grid-row-gap|grid-column-gap|inset|top|right|bottom|left|scroll-margin|scroll-padding)(-(top|right|bottom|left|block|inline|x|y)(-(start|end))?)?$/;
|
|
231
|
+
var SIZE_PROP = /^(min-|max-)?(width|height|block-size|inline-size)$|^flex-basis$/;
|
|
232
|
+
var RADIUS_PROP = /radius$/;
|
|
233
|
+
var BORDER_WIDTH_PROP = /^(border|outline)(-(top|right|bottom|left|block|inline)(-(start|end))?)?(-width)?$|^outline-offset$|^column-rule(-width)?$/;
|
|
234
|
+
var MOTION_PROP = /^(transition|animation)(-duration|-delay)?$/;
|
|
235
|
+
var EASING_PROP = /^(transition|animation)(-timing-function)?$/;
|
|
236
|
+
var SKIP_PROP = /^(content|grid-template(-areas)?|grid-area|src|mask(-image)?|transform|translate|will-change|cursor|quotes|font-feature-settings|font-variation-settings|counter-(reset|increment)|clip-path|d|unicode-range)$/;
|
|
237
|
+
var looseKey = (p) => p.startsWith("$") || p.startsWith("--") || !/^-?[a-z]+(-[a-z]+)*$/.test(p) || !KNOWN_PROPS.has(p);
|
|
238
|
+
var KNOWN_PROPS = new Set(
|
|
239
|
+
"color background background-color background-image border border-top border-right border-bottom border-left border-color border-width border-style border-radius border-top-left-radius border-top-right-radius border-bottom-left-radius border-bottom-right-radius border-top-color border-bottom-color border-left-color border-right-color border-top-width border-bottom-width border-left-width border-right-width border-block border-inline border-block-start border-block-end border-inline-start border-inline-end outline outline-color outline-width outline-offset fill stroke text-decoration text-decoration-color caret-color accent-color box-shadow text-shadow padding padding-top padding-right padding-bottom padding-left padding-block padding-inline padding-x padding-y padding-inline-start padding-inline-end padding-block-start padding-block-end margin margin-top margin-right margin-bottom margin-left margin-block margin-inline margin-x margin-y margin-inline-start margin-inline-end margin-block-start margin-block-end gap row-gap column-gap grid-gap inset top right bottom left width height min-width min-height max-width max-height flex-basis font font-family font-size font-weight line-height letter-spacing transition transition-duration transition-delay transition-timing-function animation animation-duration animation-delay animation-timing-function z-index opacity display position flex flex-direction flex-wrap flex-grow flex-shrink align-items justify-content overflow text-align white-space cursor content transform".split(" ")
|
|
240
|
+
);
|
|
241
|
+
function mask(value) {
|
|
242
|
+
let out = value;
|
|
243
|
+
for (const fn of ["var(", "url(", "env("]) {
|
|
244
|
+
let i = out.indexOf(fn);
|
|
245
|
+
while (i >= 0) {
|
|
246
|
+
let depth = 0;
|
|
247
|
+
let j = i + fn.length - 1;
|
|
248
|
+
for (; j < out.length; j++) {
|
|
249
|
+
if (out[j] === "(") depth++;
|
|
250
|
+
else if (out[j] === ")" && --depth === 0) break;
|
|
251
|
+
}
|
|
252
|
+
out = out.slice(0, i) + " ".repeat(j + 1 - i) + out.slice(j + 1);
|
|
253
|
+
i = out.indexOf(fn, i);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return out.replace(/(["'])(?:(?!\1).)*\1/g, (s) => " ".repeat(s.length));
|
|
257
|
+
}
|
|
258
|
+
var LENGTH_RE = /(?<![\w.#-])(-?\d*\.?\d+)(px|rem|em)\b/g;
|
|
259
|
+
function lengthCategory(prop) {
|
|
260
|
+
if (RADIUS_PROP.test(prop)) return "radius";
|
|
261
|
+
if (SPACING_PROP.test(prop)) return "spacing";
|
|
262
|
+
if (SIZE_PROP.test(prop)) return "size";
|
|
263
|
+
if (BORDER_WIDTH_PROP.test(prop)) return "border-width";
|
|
264
|
+
if (prop === "font-size") return "font-size";
|
|
265
|
+
if (prop === "line-height") return "line-height";
|
|
266
|
+
if (looseKey(prop)) {
|
|
267
|
+
const k = prop.toLowerCase();
|
|
268
|
+
if (/radius|rounded/.test(k)) return "radius";
|
|
269
|
+
if (/space|spacing|gap|padding|margin|gutter|inset/.test(k)) return "spacing";
|
|
270
|
+
if (/font-?size|fontsize/.test(k)) return "font-size";
|
|
271
|
+
if (/width|height|size/.test(k)) return "size";
|
|
272
|
+
}
|
|
273
|
+
return void 0;
|
|
274
|
+
}
|
|
275
|
+
function scanCssValue(property, value) {
|
|
276
|
+
const prop = property.trim().toLowerCase();
|
|
277
|
+
const vars = [...value.matchAll(/var\(\s*(--[\w-]+)/g)].map((m) => m[1]);
|
|
278
|
+
const hits = [];
|
|
279
|
+
const out = { hits, vars, themeRef: false };
|
|
280
|
+
if (SKIP_PROP.test(prop)) return out;
|
|
281
|
+
const v = value.trim();
|
|
282
|
+
if (!v || /^(inherit|initial|unset|revert|none|auto|0|normal|currentcolor|transparent)$/i.test(v)) return out;
|
|
283
|
+
if (/^\$[\w-]+$/.test(v) || /^theme\(/.test(v)) {
|
|
284
|
+
out.themeRef = true;
|
|
285
|
+
return out;
|
|
286
|
+
}
|
|
287
|
+
const masked = mask(value);
|
|
288
|
+
if (prop === "font-family") {
|
|
289
|
+
if (masked.trim() && !/^(inherit|initial|unset)$/i.test(v)) hits.push({ category: "font-family", text: v, index: value.indexOf(v) });
|
|
290
|
+
return out;
|
|
291
|
+
}
|
|
292
|
+
if (prop === "box-shadow" || prop === "text-shadow") {
|
|
293
|
+
const numbers = masked.replace(/(?<![\w.])0(?![\w.%])/g, " ");
|
|
294
|
+
if (/\d/.test(numbers) || masked.search(COLOR_LITERAL_RE) >= 0) hits.push({ category: "shadow", text: v, index: value.indexOf(v) });
|
|
295
|
+
return out;
|
|
296
|
+
}
|
|
297
|
+
for (const m of masked.matchAll(COLOR_LITERAL_RE)) if (parseCssColor(m[0])) hits.push({ category: "color", text: m[0], index: m.index });
|
|
298
|
+
if (COLOR_PROP.test(prop) || prop.startsWith("--") || prop.startsWith("$")) {
|
|
299
|
+
for (const m of masked.matchAll(/(?<![\w#.-])([a-z]+)(?![\w(-])/gi)) {
|
|
300
|
+
const n = m[1].toLowerCase();
|
|
301
|
+
if (NAMED_COLORS[n] && !/^(transparent|currentcolor)$/.test(n)) hits.push({ category: "color", text: m[1], index: m.index });
|
|
302
|
+
}
|
|
303
|
+
if (prop.startsWith("--") && /^-?[\d.]+(deg)?\s+[\d.]+%\s+[\d.]+%/.test(v) && parseCssColor(v)) hits.push({ category: "color", text: v, index: value.indexOf(v) });
|
|
304
|
+
}
|
|
305
|
+
if (prop === "z-index") {
|
|
306
|
+
const n = /^-?\d+$/.test(v) ? Number(v) : NaN;
|
|
307
|
+
if (Number.isFinite(n) && Math.abs(n) > 1) hits.push({ category: "z-index", text: v, index: value.indexOf(v) });
|
|
308
|
+
return out;
|
|
309
|
+
}
|
|
310
|
+
if (prop === "font-weight") {
|
|
311
|
+
if (/^(\d{3}|bold|bolder|lighter)$/i.test(v)) hits.push({ category: "font-weight", text: v, index: value.indexOf(v) });
|
|
312
|
+
return out;
|
|
313
|
+
}
|
|
314
|
+
if (prop === "line-height" && /^\d*\.?\d+$/.test(v) && Number(v) !== 0) {
|
|
315
|
+
hits.push({ category: "line-height", text: v, index: value.indexOf(v) });
|
|
316
|
+
return out;
|
|
317
|
+
}
|
|
318
|
+
if (MOTION_PROP.test(prop)) {
|
|
319
|
+
for (const m of masked.matchAll(/(?<![\w.-])(\d*\.?\d+)(ms|s)\b/g)) if (Number(m[1]) !== 0) hits.push({ category: "duration", text: m[0], index: m.index });
|
|
320
|
+
}
|
|
321
|
+
if (EASING_PROP.test(prop)) {
|
|
322
|
+
for (const m of masked.matchAll(/cubic-bezier\([^)]*\)|(?<![\w-])(ease-in-out|ease-in|ease-out|ease|linear)(?![\w-])/g)) hits.push({ category: "easing", text: m[0], index: m.index });
|
|
323
|
+
}
|
|
324
|
+
const cat = lengthCategory(prop);
|
|
325
|
+
if (cat) {
|
|
326
|
+
for (const m of masked.matchAll(LENGTH_RE)) if (Number(m[1]) !== 0) hits.push({ category: cat, text: m[0].replace(/^-/, ""), index: m.index + (m[0].startsWith("-") ? 1 : 0) });
|
|
327
|
+
}
|
|
328
|
+
hits.sort((a, b) => a.index - b.index);
|
|
329
|
+
return out;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ../core/src/layered.ts
|
|
333
|
+
var aliasOf = (v) => isAlias(v) ? aliasTarget(v) : null;
|
|
334
|
+
function globToRegExp(glob) {
|
|
335
|
+
let re = "";
|
|
336
|
+
for (let i = 0; i < glob.length; i++) {
|
|
337
|
+
const c = glob[i];
|
|
338
|
+
if (c === "*" && glob[i + 1] === "*") {
|
|
339
|
+
if (glob[i + 2] === "/") {
|
|
340
|
+
re += "(?:.*/)?";
|
|
341
|
+
i += 2;
|
|
342
|
+
} else {
|
|
343
|
+
re += ".*";
|
|
344
|
+
i += 1;
|
|
345
|
+
}
|
|
346
|
+
} else if (c === "*") re += "[^/]*";
|
|
347
|
+
else if (c === "?") re += "[^/]";
|
|
348
|
+
else re += c.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
349
|
+
}
|
|
350
|
+
return new RegExp(`^${re}$`);
|
|
351
|
+
}
|
|
352
|
+
var comboKey = (c) => Object.entries(c).map(([k, v]) => `${k}=${v}`).join(",") || "default";
|
|
353
|
+
function flattenLayerFile(json, file, issues) {
|
|
354
|
+
const out = [];
|
|
355
|
+
const walk2 = (node, path, inheritedType) => {
|
|
356
|
+
if (!node || typeof node !== "object" || Array.isArray(node)) return;
|
|
357
|
+
const n = node;
|
|
358
|
+
const type = typeof n.$type === "string" ? n.$type : inheritedType;
|
|
359
|
+
if ("$value" in n) {
|
|
360
|
+
out.push({
|
|
361
|
+
path: path.join("."),
|
|
362
|
+
...type ? { type } : {},
|
|
363
|
+
value: n.$value,
|
|
364
|
+
...typeof n.$description === "string" ? { description: n.$description } : {},
|
|
365
|
+
...n.$extensions && typeof n.$extensions === "object" ? { extensions: n.$extensions } : {},
|
|
366
|
+
...n.$deprecated !== void 0 ? { deprecated: n.$deprecated } : {},
|
|
367
|
+
file
|
|
368
|
+
});
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
for (const [k, v] of Object.entries(n)) {
|
|
372
|
+
if (k.startsWith("$")) continue;
|
|
373
|
+
const p = [...path, k].join(".");
|
|
374
|
+
if (/[{}.]/.test(k)) issues.push({ level: "error", code: "invalid-name", message: `Name "${k}" contains {, } or . which DTCG forbids.`, path: p, file });
|
|
375
|
+
if (v && typeof v === "object" && !Array.isArray(v)) walk2(v, [...path, k], type);
|
|
376
|
+
else issues.push({ level: "error", code: "not-a-token", message: `"${p}" is neither a token (no $value) nor a group.`, path: p, file });
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
walk2(json, []);
|
|
380
|
+
return out;
|
|
381
|
+
}
|
|
382
|
+
var toHex2 = (n) => Math.round(Math.max(0, Math.min(1, n)) * 255).toString(16).padStart(2, "0");
|
|
383
|
+
function compactJson(v) {
|
|
384
|
+
if (Array.isArray(v)) return `[${v.map(compactJson).join(", ")}]`;
|
|
385
|
+
if (v && typeof v === "object") {
|
|
386
|
+
const e = Object.entries(v);
|
|
387
|
+
return e.length ? `{ ${e.map(([k, x]) => `${JSON.stringify(k)}: ${compactJson(x)}`).join(", ")} }` : "{}";
|
|
388
|
+
}
|
|
389
|
+
return JSON.stringify(v);
|
|
390
|
+
}
|
|
391
|
+
function colorToCss(c) {
|
|
392
|
+
if (typeof c === "string") return c;
|
|
393
|
+
if (!c || typeof c !== "object") return String(c);
|
|
394
|
+
const o = c;
|
|
395
|
+
const [r, g, b] = (o.components ?? []).map((x) => typeof x === "number" ? x : 0);
|
|
396
|
+
const alpha = o.alpha ?? 1;
|
|
397
|
+
if (alpha < 1) return `rgb(${[r, g, b].map((x) => Math.round((x ?? 0) * 255)).join(" ")} / ${alpha})`;
|
|
398
|
+
return o.hex ?? `#${[r, g, b].map((x) => toHex2(x ?? 0)).join("")}`;
|
|
399
|
+
}
|
|
400
|
+
var dimToCss = (d) => d && typeof d === "object" && "value" in d ? `${d.value}${d.unit}` : String(d);
|
|
401
|
+
function valueToCss(type, v, ext) {
|
|
402
|
+
if (v === void 0 || v === null) return "";
|
|
403
|
+
switch (type) {
|
|
404
|
+
case "color":
|
|
405
|
+
return colorToCss(v);
|
|
406
|
+
case "dimension":
|
|
407
|
+
case "duration":
|
|
408
|
+
return dimToCss(v);
|
|
409
|
+
case "cubicBezier":
|
|
410
|
+
return Array.isArray(v) ? `cubic-bezier(${v.join(", ")})` : String(v);
|
|
411
|
+
case "fontFamily":
|
|
412
|
+
return Array.isArray(v) ? v.map((n) => /\s/.test(String(n)) ? `"${n}"` : String(n)).join(", ") : String(v);
|
|
413
|
+
case "shadow": {
|
|
414
|
+
if (typeof v === "string") return v;
|
|
415
|
+
const layers = Array.isArray(v) ? v : [v];
|
|
416
|
+
return layers.map((l) => [l.inset ? "inset" : "", dimToCss(l.offsetX), dimToCss(l.offsetY), dimToCss(l.blur), dimToCss(l.spread ?? { value: 0, unit: "px" }), colorToCss(l.color)].filter(Boolean).join(" ")).join(", ");
|
|
417
|
+
}
|
|
418
|
+
default: {
|
|
419
|
+
const unit = ext?.["dev.bridge.unit"];
|
|
420
|
+
if (typeof v === "number" && typeof unit === "string") return `${v}${unit}`;
|
|
421
|
+
return typeof v === "object" ? compactJson(v) : String(v);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
var LayeredTokenSet = class {
|
|
426
|
+
/** `files`: token file path relative to tokens.root -> file text. */
|
|
427
|
+
constructor(config, files) {
|
|
428
|
+
this.config = config;
|
|
429
|
+
this.files = files;
|
|
430
|
+
this.fileNames = [...files.keys()].sort();
|
|
431
|
+
for (const f of this.fileNames) {
|
|
432
|
+
let json;
|
|
433
|
+
try {
|
|
434
|
+
json = JSON.parse(files.get(f));
|
|
435
|
+
} catch (e) {
|
|
436
|
+
this.issues.push({ level: "error", code: "invalid-json", message: `Invalid JSON: ${e.message}`, file: f });
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
this.defs.push(...flattenLayerFile(json, f, this.issues));
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
config;
|
|
443
|
+
files;
|
|
444
|
+
defs = [];
|
|
445
|
+
issues = [];
|
|
446
|
+
fileNames;
|
|
447
|
+
views = /* @__PURE__ */ new Map();
|
|
448
|
+
get modeNames() {
|
|
449
|
+
return Object.keys(this.config.axes);
|
|
450
|
+
}
|
|
451
|
+
get defaultCombo() {
|
|
452
|
+
return Object.fromEntries(Object.entries(this.config.axes).map(([k, m]) => [k, m.default]));
|
|
453
|
+
}
|
|
454
|
+
/** Every combination of the given modes (all modes when omitted); other modes stay at default. */
|
|
455
|
+
combos(only) {
|
|
456
|
+
let out = [this.defaultCombo];
|
|
457
|
+
for (const [name, m] of Object.entries(this.config.axes)) {
|
|
458
|
+
if (only && !only.includes(name)) continue;
|
|
459
|
+
out = out.flatMap((c) => m.values.map((v) => ({ ...c, [name]: v })));
|
|
460
|
+
}
|
|
461
|
+
return out;
|
|
462
|
+
}
|
|
463
|
+
normalizeCombo(partial = {}) {
|
|
464
|
+
const c = { ...this.defaultCombo };
|
|
465
|
+
for (const [k, v] of Object.entries(partial)) {
|
|
466
|
+
if (v === void 0) continue;
|
|
467
|
+
const m = this.config.axes[k];
|
|
468
|
+
if (!m) throw new Error(`Unknown mode "${k}". Modes: ${this.modeNames.join(", ") || "none"}.`);
|
|
469
|
+
if (!m.values.includes(v)) throw new Error(`Unknown ${k} "${v}". Values: ${m.values.join(", ")}.`);
|
|
470
|
+
c[k] = v;
|
|
471
|
+
}
|
|
472
|
+
return c;
|
|
473
|
+
}
|
|
474
|
+
tierOfFile(file) {
|
|
475
|
+
for (const [tier, dirs] of Object.entries(this.config.tokens.tiers)) {
|
|
476
|
+
if (dirs.some((d) => file === d || file.startsWith(`${d.replace(/\/$/, "")}/`) || globToRegExp(d).test(file))) return tier;
|
|
477
|
+
}
|
|
478
|
+
return void 0;
|
|
479
|
+
}
|
|
480
|
+
/** Tier of a token: the lowest layer that defines it in the default mode (an override does not change its tier). */
|
|
481
|
+
tierOf(path) {
|
|
482
|
+
const d = this.view(this.defaultCombo).get(path)?.[0] ?? this.defs.find((x) => x.path === path);
|
|
483
|
+
return d ? this.tierOfFile(d.file) : void 0;
|
|
484
|
+
}
|
|
485
|
+
/** Files of each layer for a combo, in resolution order. */
|
|
486
|
+
layerFiles(combo) {
|
|
487
|
+
const files = [];
|
|
488
|
+
const missing = [];
|
|
489
|
+
for (const l of this.config.tokens.layers) {
|
|
490
|
+
const pattern = l.files.replace(/\{([^}]+)\}/g, (_, k) => combo[k] ?? `{${k}}`);
|
|
491
|
+
const re = globToRegExp(pattern);
|
|
492
|
+
const hit = this.fileNames.filter((f) => re.test(f));
|
|
493
|
+
if (!hit.length && !l.optional) missing.push(pattern);
|
|
494
|
+
for (const f of hit) if (!files.includes(f)) files.push(f);
|
|
495
|
+
}
|
|
496
|
+
return { files, missing };
|
|
497
|
+
}
|
|
498
|
+
/** path -> definitions in layer order (last one wins) for a combo. */
|
|
499
|
+
view(combo) {
|
|
500
|
+
const key = comboKey(combo);
|
|
501
|
+
let v = this.views.get(key);
|
|
502
|
+
if (!v) {
|
|
503
|
+
const { files, missing } = this.layerFiles(combo);
|
|
504
|
+
const map = /* @__PURE__ */ new Map();
|
|
505
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
506
|
+
for (const d of this.defs) (byFile.get(d.file) ?? byFile.set(d.file, []).get(d.file)).push(d);
|
|
507
|
+
for (const f of files) for (const d of byFile.get(f) ?? []) (map.get(d.path) ?? map.set(d.path, []).get(d.path)).push(d);
|
|
508
|
+
v = { map, missing };
|
|
509
|
+
this.views.set(key, v);
|
|
510
|
+
}
|
|
511
|
+
return v.map;
|
|
512
|
+
}
|
|
513
|
+
missingLayers(combo) {
|
|
514
|
+
this.view(combo);
|
|
515
|
+
return this.views.get(comboKey(combo)).missing;
|
|
516
|
+
}
|
|
517
|
+
/** Every token path defined in any combo. */
|
|
518
|
+
allPaths() {
|
|
519
|
+
return [...new Set(this.defs.map((d) => d.path))].sort();
|
|
520
|
+
}
|
|
521
|
+
defsOf(path) {
|
|
522
|
+
return this.defs.filter((d) => d.path === path);
|
|
523
|
+
}
|
|
524
|
+
/** First combo whose layers include `file`, preferring the default. */
|
|
525
|
+
comboForFile(file) {
|
|
526
|
+
const all = [this.defaultCombo, ...this.combos()];
|
|
527
|
+
return all.find((c) => this.layerFiles(c).files.includes(file)) ?? this.defaultCombo;
|
|
528
|
+
}
|
|
529
|
+
resolve(path, combo) {
|
|
530
|
+
const view = this.view(combo);
|
|
531
|
+
const chain = [path];
|
|
532
|
+
const top = view.get(path);
|
|
533
|
+
if (!top) return { path, value: void 0, css: "", chain, definedIn: [], error: `"${path}" is not defined for ${comboKey(combo)}` };
|
|
534
|
+
const def = top[top.length - 1];
|
|
535
|
+
let type = def.type;
|
|
536
|
+
let error;
|
|
537
|
+
const deref = (target, stack) => {
|
|
538
|
+
if (stack.includes(target)) {
|
|
539
|
+
error = `circular alias: ${[...stack, target].join(" -> ")}`;
|
|
540
|
+
return void 0;
|
|
541
|
+
}
|
|
542
|
+
const t = view.get(target);
|
|
543
|
+
if (!t) {
|
|
544
|
+
error = `broken alias {${target}}`;
|
|
545
|
+
return void 0;
|
|
546
|
+
}
|
|
547
|
+
const td = t[t.length - 1];
|
|
548
|
+
type ??= td.type;
|
|
549
|
+
return walk2(td.value, [...stack, target]);
|
|
550
|
+
};
|
|
551
|
+
const walk2 = (v, stack) => {
|
|
552
|
+
const a = aliasOf(v);
|
|
553
|
+
if (a) return deref(a, stack);
|
|
554
|
+
if (Array.isArray(v)) return v.map((x) => walk2(x, stack));
|
|
555
|
+
if (v && typeof v === "object") return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, walk2(x, stack)]));
|
|
556
|
+
return v;
|
|
557
|
+
};
|
|
558
|
+
let cur = def.value;
|
|
559
|
+
const seen = /* @__PURE__ */ new Set([path]);
|
|
560
|
+
for (let a = aliasOf(cur); a; a = aliasOf(cur)) {
|
|
561
|
+
chain.push(a);
|
|
562
|
+
if (seen.has(a)) break;
|
|
563
|
+
seen.add(a);
|
|
564
|
+
const t = view.get(a);
|
|
565
|
+
if (!t) break;
|
|
566
|
+
cur = t[t.length - 1].value;
|
|
567
|
+
}
|
|
568
|
+
const value = walk2(def.value, [path]);
|
|
569
|
+
return { path, ...type ? { type } : {}, value, css: error ? "" : valueToCss(type, value, def.extensions), chain, definedIn: top.map((d) => d.file), ...error ? { error } : {} };
|
|
570
|
+
}
|
|
571
|
+
/** Tokens whose own value aliases `path` directly, across all files. */
|
|
572
|
+
referencedBy(path) {
|
|
573
|
+
const needle = `{${path}}`;
|
|
574
|
+
return this.defs.filter((d) => JSON.stringify(d.value).includes(needle)).map((d) => ({ path: d.path, file: d.file }));
|
|
575
|
+
}
|
|
576
|
+
cssVarName(path) {
|
|
577
|
+
return `${this.config.cssVar.prefix}${path.split(".").join(this.config.cssVar.separator)}`;
|
|
578
|
+
}
|
|
579
|
+
/** Repo-relative path of a token file. */
|
|
580
|
+
repoPath(file) {
|
|
581
|
+
const root = this.config.tokens.root;
|
|
582
|
+
return root === "." || !root ? file : `${root.replace(/\/$/, "")}/${file}`;
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
function selectTokenFiles(config, repoFiles) {
|
|
586
|
+
const root = config.tokens.root.replace(/\/$/, "");
|
|
587
|
+
const rel = (f) => root === "." || !root ? f : f.startsWith(`${root}/`) ? f.slice(root.length + 1) : null;
|
|
588
|
+
const inc = config.tokens.include.map(globToRegExp);
|
|
589
|
+
const exc = config.tokens.exclude.map(globToRegExp);
|
|
590
|
+
return repoFiles.filter((f) => f !== config.contrast?.path).map(rel).filter((f) => f !== null && inc.some((r) => r.test(f)) && !exc.some((r) => r.test(f)));
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// ../core/src/audit/ds-index.ts
|
|
594
|
+
var TIER_RANK_COLOR = { semantic: 0, base: 1, component: 2, brand: 3, density: 3, primitive: 4 };
|
|
595
|
+
var TIER_RANK_SCALE = { semantic: 0, base: 0, brand: 1, density: 2, primitive: 3, component: 5 };
|
|
596
|
+
var COLOR_HINTS = [
|
|
597
|
+
[/^(color|fill|stroke|-webkit-text-fill-color|caret-color|text-decoration(-color)?)$/, /(^|\.)(text|fg|icon|foreground|link)(\.|-|$)/],
|
|
598
|
+
[/background|bgcolor/, /(^|\.)(bg|surface|background|fill|solid|scrim|overlay)(\.|-|$)/],
|
|
599
|
+
[/border|outline|divider|column-rule/, /(^|\.)(border|outline|ring|divider|line|separator)(\.|-|$)/]
|
|
600
|
+
];
|
|
601
|
+
var PATH_FOR = {
|
|
602
|
+
spacing: /(^|\.)space(\.|$)/,
|
|
603
|
+
size: /(^|\.)size(\.|$)/,
|
|
604
|
+
radius: /(^|\.)radius(\.|$)|-radius$|\.radius$/,
|
|
605
|
+
"border-width": /border-width|border\.width|(^|\.)stroke(\.|$)/,
|
|
606
|
+
"font-size": /font\.size|font-size|text\.size/,
|
|
607
|
+
"line-height": /line-height|leading/,
|
|
608
|
+
duration: /duration/,
|
|
609
|
+
"z-index": /^(z|layer|z-index|zindex)\./,
|
|
610
|
+
"font-weight": /weight/
|
|
611
|
+
};
|
|
612
|
+
var toPx = (css) => {
|
|
613
|
+
const m = /^(-?\d*\.?\d+)(px|rem|em)?$/.exec(css.trim());
|
|
614
|
+
if (!m) return void 0;
|
|
615
|
+
return m[2] === "rem" || m[2] === "em" ? Number(m[1]) * 16 : Number(m[1]);
|
|
616
|
+
};
|
|
617
|
+
var toMs = (css) => {
|
|
618
|
+
const m = /^(\d*\.?\d+)(ms|s)$/.exec(css.trim());
|
|
619
|
+
return m ? m[2] === "s" ? Number(m[1]) * 1e3 : Number(m[1]) : void 0;
|
|
620
|
+
};
|
|
621
|
+
var EASE_KEYWORDS = { ease: [0.25, 0.1, 0.25, 1], "ease-in": [0.42, 0, 1, 1], "ease-out": [0, 0, 0.58, 1], "ease-in-out": [0.42, 0, 0.58, 1], linear: [0, 0, 1, 1] };
|
|
622
|
+
var bezier = (css) => {
|
|
623
|
+
const k = EASE_KEYWORDS[css.trim()];
|
|
624
|
+
if (k) return k;
|
|
625
|
+
const m = /cubic-bezier\(([^)]*)\)/.exec(css);
|
|
626
|
+
const n = m?.[1].split(",").map(Number);
|
|
627
|
+
return n && n.length === 4 && n.every(Number.isFinite) ? n : void 0;
|
|
628
|
+
};
|
|
629
|
+
var WEIGHT_WORDS = { normal: 400, bold: 700, bolder: 700, lighter: 300 };
|
|
630
|
+
var firstFamily = (css) => css.split(",")[0].trim().replace(/^["']|["']$/g, "").toLowerCase();
|
|
631
|
+
var shadowNums = (css) => [...css.replace(/rgba?\([^)]*\)|#[0-9a-f]+|hsla?\([^)]*\)/gi, "").matchAll(/-?\d*\.?\d+/g)].map((m) => Number(m[0]));
|
|
632
|
+
function level(conf) {
|
|
633
|
+
return conf >= 0.999 ? "exact" : conf >= 0.75 ? "high" : conf >= 0.5 ? "medium" : "low";
|
|
634
|
+
}
|
|
635
|
+
var round = (n, d = 2) => Math.round(n * 10 ** d) / 10 ** d;
|
|
636
|
+
var DEFAULT_NATIVE_ELEMENTS = ["button", "input", "select", "textarea", "dialog", "table", "a", "img"];
|
|
637
|
+
var DesignSystemIndex = class {
|
|
638
|
+
cssVars = /* @__PURE__ */ new Set();
|
|
639
|
+
packages;
|
|
640
|
+
components;
|
|
641
|
+
foreignLibraries;
|
|
642
|
+
nativeElements;
|
|
643
|
+
tokenCount;
|
|
644
|
+
modes;
|
|
645
|
+
colors = [];
|
|
646
|
+
byType = /* @__PURE__ */ new Map();
|
|
647
|
+
defaultCombo;
|
|
648
|
+
constructor(set, opts) {
|
|
649
|
+
this.packages = opts.packages;
|
|
650
|
+
this.components = new Set(opts.components);
|
|
651
|
+
this.foreignLibraries = opts.foreignLibraries ?? [];
|
|
652
|
+
this.nativeElements = opts.nativeElements ?? DEFAULT_NATIVE_ELEMENTS;
|
|
653
|
+
this.defaultCombo = set.defaultCombo;
|
|
654
|
+
this.modes = set.combos().map(comboKey);
|
|
655
|
+
const paths = set.allPaths();
|
|
656
|
+
this.tokenCount = paths.length;
|
|
657
|
+
const defKey = comboKey(this.defaultCombo);
|
|
658
|
+
const seenColor = /* @__PURE__ */ new Set();
|
|
659
|
+
for (const combo of set.combos()) {
|
|
660
|
+
const key = this.modeLabel(combo);
|
|
661
|
+
const isDefault = comboKey(combo) === defKey;
|
|
662
|
+
const view = set.view(combo);
|
|
663
|
+
for (const p of view.keys()) {
|
|
664
|
+
const r = set.resolve(p, combo);
|
|
665
|
+
if (r.error || !r.css) continue;
|
|
666
|
+
const base = { path: p, cssVar: set.cssVarName(p), type: r.type ?? "", tier: set.tierOf(p) ?? "primitive", css: r.css };
|
|
667
|
+
if (r.type === "color") {
|
|
668
|
+
const rgba = parseCssColor(r.css);
|
|
669
|
+
const id = `${p}|${r.css}`;
|
|
670
|
+
if (!rgba || seenColor.has(id)) continue;
|
|
671
|
+
seenColor.add(id);
|
|
672
|
+
this.colors.push({ ...base, rgba, ...key ? { combo: key } : {} });
|
|
673
|
+
} else if (isDefault) {
|
|
674
|
+
const list2 = this.byType.get(base.type) ?? [];
|
|
675
|
+
list2.push(base);
|
|
676
|
+
this.byType.set(base.type, list2);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
for (const p of paths) this.cssVars.add(set.cssVarName(p));
|
|
681
|
+
}
|
|
682
|
+
modeLabel(combo) {
|
|
683
|
+
const diff = Object.entries(combo).filter(([k, v]) => this.defaultCombo[k] !== v);
|
|
684
|
+
return diff.length ? diff.map(([k, v]) => `${k}=${v}`).join(",") : void 0;
|
|
685
|
+
}
|
|
686
|
+
isDsVar(name) {
|
|
687
|
+
return this.cssVars.has(name);
|
|
688
|
+
}
|
|
689
|
+
isDsModule(mod) {
|
|
690
|
+
return this.packages.some((p) => mod === p || mod.startsWith(`${p}/`));
|
|
691
|
+
}
|
|
692
|
+
entries(types, pathRe) {
|
|
693
|
+
const all = types.flatMap((t) => this.byType.get(t) ?? []);
|
|
694
|
+
if (!pathRe) return all;
|
|
695
|
+
const narrowed = all.filter((e) => pathRe.test(e.path));
|
|
696
|
+
return narrowed.length ? narrowed : all;
|
|
697
|
+
}
|
|
698
|
+
make(e, distance, confidence) {
|
|
699
|
+
return { token: e.path, cssVar: e.cssVar, value: e.css, ...e.combo ? { mode: e.combo } : {}, distance: round(distance), confidence: round(confidence), level: level(confidence) };
|
|
700
|
+
}
|
|
701
|
+
/** Nearest color by deltaE OK across every mode; semantic tokens and tokens that fit the property win near-ties. */
|
|
702
|
+
suggestColor(value, property) {
|
|
703
|
+
const target = parseCssColor(value);
|
|
704
|
+
if (!target || !this.colors.length) return void 0;
|
|
705
|
+
const hint = COLOR_HINTS.find(([re]) => property && re.test(property))?.[1];
|
|
706
|
+
const scored = this.colors.map((e) => ({ e, d: deltaE(target, e.rgba) }));
|
|
707
|
+
const min = Math.min(...scored.map((s) => s.d));
|
|
708
|
+
const tolerance = min <= 0.5 ? 0.5 : min + 2;
|
|
709
|
+
const pick = scored.filter((s) => s.d <= Math.max(tolerance, min)).sort((a, b) => Number(hint ? !hint.test(a.e.path) : 0) - Number(hint ? !hint.test(b.e.path) : 0) || (TIER_RANK_COLOR[a.e.tier] ?? 5) - (TIER_RANK_COLOR[b.e.tier] ?? 5) || Number(Boolean(a.e.combo)) - Number(Boolean(b.e.combo)) || a.d - b.d)[0];
|
|
710
|
+
if (!pick || pick.d > 15) return void 0;
|
|
711
|
+
const conf = pick.d <= 0.5 ? 1 : pick.d <= 2 ? 0.85 : pick.d <= 5 ? 0.6 : 0.3;
|
|
712
|
+
return this.make(pick.e, pick.d, conf);
|
|
713
|
+
}
|
|
714
|
+
nearestNumber(cands, target, read, rank = TIER_RANK_SCALE) {
|
|
715
|
+
const scored = cands.map((e) => ({ e, n: read(e) })).filter((s) => s.n !== void 0).map((s) => ({ e: s.e, d: Math.abs(s.n - target) }));
|
|
716
|
+
if (!scored.length) return void 0;
|
|
717
|
+
const min = Math.min(...scored.map((s) => s.d));
|
|
718
|
+
return scored.filter((s) => s.d === min).sort((a, b) => (rank[a.e.tier] ?? 5) - (rank[b.e.tier] ?? 5) || a.e.path.length - b.e.path.length)[0];
|
|
719
|
+
}
|
|
720
|
+
ratioConfidence(d, target) {
|
|
721
|
+
if (d === 0) return 1;
|
|
722
|
+
const rel = d / Math.max(Math.abs(target), 1);
|
|
723
|
+
return d <= 1 || rel <= 0.05 ? 0.8 : rel <= 0.15 ? 0.55 : rel <= 0.4 ? 0.3 : 0;
|
|
724
|
+
}
|
|
725
|
+
/** Suggest a token for a hardcoded value of a category. Undefined when nothing is close. */
|
|
726
|
+
suggest(category, value, property) {
|
|
727
|
+
switch (category) {
|
|
728
|
+
case "color":
|
|
729
|
+
return this.suggestColor(value, property);
|
|
730
|
+
case "spacing":
|
|
731
|
+
case "size":
|
|
732
|
+
case "radius":
|
|
733
|
+
case "border-width":
|
|
734
|
+
case "font-size": {
|
|
735
|
+
const px = toPx(value);
|
|
736
|
+
if (px === void 0) return void 0;
|
|
737
|
+
const best = this.nearestNumber(this.entries(["dimension"], PATH_FOR[category]), Math.abs(px), (e) => toPx(e.css));
|
|
738
|
+
if (!best) return void 0;
|
|
739
|
+
let conf = this.ratioConfidence(best.d, px);
|
|
740
|
+
if (conf && /em$/.test(value) && !/rem$/.test(value)) conf = Math.min(conf, 0.55);
|
|
741
|
+
return conf ? this.make(best.e, best.d, conf) : void 0;
|
|
742
|
+
}
|
|
743
|
+
case "line-height": {
|
|
744
|
+
const n = Number(value);
|
|
745
|
+
if (!Number.isFinite(n)) return void 0;
|
|
746
|
+
const best = this.nearestNumber(this.entries(["number", "dimension"], PATH_FOR["line-height"]).filter((e) => PATH_FOR["line-height"].test(e.path)), n, (e) => Number(e.css));
|
|
747
|
+
if (!best) return void 0;
|
|
748
|
+
const conf = best.d < 1e-3 ? 1 : best.d <= 0.05 ? 0.8 : best.d <= 0.15 ? 0.5 : 0.25;
|
|
749
|
+
return this.make(best.e, best.d, conf);
|
|
750
|
+
}
|
|
751
|
+
case "duration": {
|
|
752
|
+
const ms = toMs(value);
|
|
753
|
+
if (ms === void 0) return void 0;
|
|
754
|
+
const cands = this.entries(["duration"]).filter((e) => toMs(e.css) !== 0 || ms === 0);
|
|
755
|
+
const best = this.nearestNumber(cands, ms, (e) => toMs(e.css));
|
|
756
|
+
const conf = best ? this.ratioConfidence(best.d, ms) || 0.25 : 0;
|
|
757
|
+
return best ? this.make(best.e, best.d, conf) : void 0;
|
|
758
|
+
}
|
|
759
|
+
case "easing": {
|
|
760
|
+
const target = bezier(value);
|
|
761
|
+
if (!target) return void 0;
|
|
762
|
+
const scored = this.entries(["cubicBezier"]).map((e) => ({ e, b: bezier(e.css) })).filter((s) => s.b).map((s) => ({ e: s.e, d: s.b.reduce((acc, x, i) => acc + Math.abs(x - target[i]), 0) })).sort((a, b) => a.d - b.d || (TIER_RANK_SCALE[a.e.tier] ?? 5) - (TIER_RANK_SCALE[b.e.tier] ?? 5));
|
|
763
|
+
const best = scored.find((s) => s.d === scored[0].d && /motion/.test(s.e.path)) ?? scored[0];
|
|
764
|
+
if (!best) return void 0;
|
|
765
|
+
return this.make(best.e, best.d, best.d < 0.011 ? 1 : best.d <= 0.3 ? 0.55 : 0.3);
|
|
766
|
+
}
|
|
767
|
+
case "z-index": {
|
|
768
|
+
const n = Number(value);
|
|
769
|
+
const cands = [...this.entries(["number"]), ...this.entries([""])].filter((e) => PATH_FOR["z-index"].test(e.path));
|
|
770
|
+
const best = this.nearestNumber(cands, n, (e) => Number(e.css));
|
|
771
|
+
if (!best) return void 0;
|
|
772
|
+
return this.make(best.e, best.d, best.d === 0 ? 1 : best.d <= 10 ? 0.5 : 0.25);
|
|
773
|
+
}
|
|
774
|
+
case "font-weight": {
|
|
775
|
+
const n = WEIGHT_WORDS[value.toLowerCase()] ?? Number(value);
|
|
776
|
+
if (!Number.isFinite(n)) return void 0;
|
|
777
|
+
const cands = [...this.entries(["fontWeight"]), ...this.entries(["number"])].filter((e) => PATH_FOR["font-weight"].test(e.path));
|
|
778
|
+
const best = this.nearestNumber(cands, n, (e) => Number(e.css));
|
|
779
|
+
if (!best) return void 0;
|
|
780
|
+
const conf = best.d === 0 ? String(n) === value ? 1 : 0.9 : best.d <= 100 ? 0.6 : 0.3;
|
|
781
|
+
return this.make(best.e, best.d / 100, conf);
|
|
782
|
+
}
|
|
783
|
+
case "font-family": {
|
|
784
|
+
const fam = firstFamily(value);
|
|
785
|
+
const cands = this.entries(["fontFamily"]);
|
|
786
|
+
const exact = cands.filter((e) => firstFamily(e.css) === fam).sort((a, b) => (TIER_RANK_SCALE[a.tier] ?? 5) - (TIER_RANK_SCALE[b.tier] ?? 5))[0];
|
|
787
|
+
if (exact) return this.make(exact, 0, value.replace(/\s/g, "") === exact.css.replace(/\s/g, "") ? 1 : 0.9);
|
|
788
|
+
const mono = /mono|courier|consol|menlo/.test(value.toLowerCase());
|
|
789
|
+
const generic = cands.filter((e) => mono ? /mono/.test(e.path) : /sans/.test(e.path)).sort((a, b) => (TIER_RANK_SCALE[a.tier] ?? 5) - (TIER_RANK_SCALE[b.tier] ?? 5))[0];
|
|
790
|
+
return generic ? this.make(generic, 1, 0.3) : void 0;
|
|
791
|
+
}
|
|
792
|
+
case "shadow": {
|
|
793
|
+
const norm = (s) => s.replace(/\s+/g, " ").replace(/\s*,\s*/g, ", ").trim().toLowerCase();
|
|
794
|
+
const cands = this.entries(["shadow"]);
|
|
795
|
+
const exact = cands.find((e) => norm(e.css) === norm(value));
|
|
796
|
+
if (exact) return this.make(exact, 0, 1);
|
|
797
|
+
const t = shadowNums(value);
|
|
798
|
+
const scored = cands.map((e) => {
|
|
799
|
+
const n = shadowNums(e.css);
|
|
800
|
+
const len = Math.max(n.length, t.length);
|
|
801
|
+
let d = 0;
|
|
802
|
+
for (let i = 0; i < len; i++) d += Math.abs((n[i] ?? 0) - (t[i] ?? 0));
|
|
803
|
+
return { e, d };
|
|
804
|
+
}).sort((a, b) => a.d - b.d || Number(!/elevation/.test(a.e.path)) - Number(!/elevation/.test(b.e.path)));
|
|
805
|
+
const best = scored[0];
|
|
806
|
+
if (!best) return void 0;
|
|
807
|
+
return this.make(best.e, best.d, best.d <= 2 ? 0.75 : best.d <= 8 ? 0.5 : 0.25);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
/** Same value as the token (safe to replace): exact level and, for lengths, the same unit. */
|
|
812
|
+
static isExact(s) {
|
|
813
|
+
return Boolean(s && s.level === "exact");
|
|
814
|
+
}
|
|
815
|
+
};
|
|
816
|
+
|
|
817
|
+
// ../core/src/audit/report.ts
|
|
818
|
+
function isSafeFix(h, s) {
|
|
819
|
+
if (!s || s.level !== "exact") return false;
|
|
820
|
+
switch (h.category) {
|
|
821
|
+
case "color": {
|
|
822
|
+
const a = parseCssColor(h.text);
|
|
823
|
+
const b = parseCssColor(s.value);
|
|
824
|
+
return Boolean(a && b && s.distance <= 0.5 && Math.abs(a.a - b.a) < 0.01);
|
|
825
|
+
}
|
|
826
|
+
case "spacing":
|
|
827
|
+
case "size":
|
|
828
|
+
case "radius":
|
|
829
|
+
case "border-width":
|
|
830
|
+
case "font-size":
|
|
831
|
+
case "duration":
|
|
832
|
+
case "z-index":
|
|
833
|
+
case "font-weight":
|
|
834
|
+
case "line-height":
|
|
835
|
+
return h.text.trim() === s.value.trim();
|
|
836
|
+
case "shadow":
|
|
837
|
+
return s.distance === 0;
|
|
838
|
+
default:
|
|
839
|
+
return false;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// ../core/src/previews.ts
|
|
844
|
+
var PREVIEW_APP_LABELS = { docs: "Docs", storybook: "Storybook", admin: "Admin demo" };
|
|
845
|
+
|
|
846
|
+
// ../core/src/teams.ts
|
|
847
|
+
var LOGIN = /^[A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9])){0,38}$/;
|
|
848
|
+
var SLUG = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/;
|
|
849
|
+
function parseOwnerEntry(raw) {
|
|
850
|
+
if (typeof raw !== "string") return null;
|
|
851
|
+
const s = raw.trim().replace(/^@/, "");
|
|
852
|
+
const slash = s.indexOf("/");
|
|
853
|
+
if (slash < 0) return LOGIN.test(s) ? { kind: "person", login: s } : null;
|
|
854
|
+
const org = s.slice(0, slash);
|
|
855
|
+
const slug = s.slice(slash + 1);
|
|
856
|
+
return LOGIN.test(org) && SLUG.test(slug) ? { kind: "team", org, slug } : null;
|
|
857
|
+
}
|
|
858
|
+
function ownerEntryProblems(raw) {
|
|
859
|
+
if (raw === void 0) return [];
|
|
860
|
+
const isObj2 = (x) => Boolean(x) && typeof x === "object" && !Array.isArray(x);
|
|
861
|
+
if (!isObj2(raw)) return ['"governance.owners" must be { "design": [people or teams], "tech": [people or teams] }'];
|
|
862
|
+
const out = [];
|
|
863
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
864
|
+
if (k !== "design" && k !== "tech") {
|
|
865
|
+
out.push(`"governance.owners.${k}" is not an owner group (design, tech)`);
|
|
866
|
+
continue;
|
|
867
|
+
}
|
|
868
|
+
if (!Array.isArray(v)) {
|
|
869
|
+
out.push(`"governance.owners.${k}" must be a list of people or teams`);
|
|
870
|
+
continue;
|
|
871
|
+
}
|
|
872
|
+
v.forEach((x, i) => {
|
|
873
|
+
if (typeof x !== "string" || !parseOwnerEntry(x))
|
|
874
|
+
out.push(`"governance.owners.${k}[${i}]" (${JSON.stringify(x)}) is not a GitHub login or team. Write a person as "peterbono" or "@peterbono", a team as "@org/team-slug".`);
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
return out;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// ../core/src/rights.ts
|
|
881
|
+
var OWNER_GROUPS = ["design", "tech"];
|
|
882
|
+
var RIGHTS = ["approveDesign", "approveTech", "ship"];
|
|
883
|
+
var DEFAULT_RIGHTS = { approveDesign: ["design"], approveTech: ["tech"], ship: ["tech"] };
|
|
884
|
+
function normalizeRights(input) {
|
|
885
|
+
const pick = (r) => {
|
|
886
|
+
const v = input?.[r];
|
|
887
|
+
if (!Array.isArray(v)) return [...DEFAULT_RIGHTS[r]];
|
|
888
|
+
return OWNER_GROUPS.filter((g) => v.includes(g));
|
|
889
|
+
};
|
|
890
|
+
return { approveDesign: pick("approveDesign"), approveTech: pick("approveTech"), ship: pick("ship") };
|
|
891
|
+
}
|
|
892
|
+
function rightsProblems(raw) {
|
|
893
|
+
if (raw === void 0) return [];
|
|
894
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return ['"governance.rights" must be { "approveDesign": [groups], "approveTech": [groups], "ship": [groups] }'];
|
|
895
|
+
const out = [];
|
|
896
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
897
|
+
if (!RIGHTS.includes(k)) out.push(`"governance.rights.${k}" is not a right (${RIGHTS.join(", ")})`);
|
|
898
|
+
else if (!Array.isArray(v) || v.length === 0 || !v.every((g) => OWNER_GROUPS.includes(g)))
|
|
899
|
+
out.push(`"governance.rights.${k}" must list one or more of "design", "tech"`);
|
|
900
|
+
}
|
|
901
|
+
return out;
|
|
902
|
+
}
|
|
903
|
+
function visualImpactProblems(raw) {
|
|
904
|
+
if (raw === void 0) return [];
|
|
905
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return ['"visualImpact" must be { "mode": "auto" | "on-demand" }'];
|
|
906
|
+
const out = [];
|
|
907
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
908
|
+
if (k !== "mode") out.push(`"visualImpact.${k}" is not a setting (mode)`);
|
|
909
|
+
else if (v !== "auto" && v !== "on-demand") out.push('"visualImpact.mode" must be "auto" or "on-demand"');
|
|
910
|
+
}
|
|
911
|
+
return out;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// ../core/src/config.ts
|
|
915
|
+
var CONFIG_FILE = "bridge.config.json";
|
|
916
|
+
var DEFAULT_OVERRIDE = { allowed: [], requireReason: true, minReasonLength: 20 };
|
|
917
|
+
var DEFAULT_TOKEN_PATHS = {
|
|
918
|
+
root: "tokens",
|
|
919
|
+
primitives: "primitives",
|
|
920
|
+
brands: "brands",
|
|
921
|
+
semantic: "semantic",
|
|
922
|
+
base: "base",
|
|
923
|
+
density: "density",
|
|
924
|
+
components: "components",
|
|
925
|
+
contrastChecks: "contrast.checks.json"
|
|
926
|
+
};
|
|
927
|
+
var DEFAULT_GOVERNANCE = {
|
|
928
|
+
protectedBranches: ["main", "master"],
|
|
929
|
+
branchPrefix: "bridge/",
|
|
930
|
+
labels: ["source: mcp"],
|
|
931
|
+
guardrails: ["AGENTS.md", "CLAUDE.md", "docs/GOVERNANCE.md", ".stylelintrc.json", ".github/", CONFIG_FILE],
|
|
932
|
+
owners: { design: [], tech: [] }
|
|
933
|
+
};
|
|
934
|
+
var FLAT_TOKEN_KEYS = ["primitives", "brands", "semantic", "base", "density", "components", "contrastChecks"];
|
|
935
|
+
var CANONICAL_TOKEN_KEYS = ["root", "include", "exclude", "tiers", "layers"];
|
|
936
|
+
var FLAT_MODE_KEYS = ["brands", "themes", "densities", "defaults"];
|
|
937
|
+
var AXIS_OF = { brands: "brand", themes: "theme", densities: "density" };
|
|
938
|
+
var ConfigError = class extends Error {
|
|
939
|
+
constructor(problems) {
|
|
940
|
+
super(`${CONFIG_FILE} is invalid: ${problems.join("; ")}`);
|
|
941
|
+
this.problems = problems;
|
|
942
|
+
this.name = "ConfigError";
|
|
943
|
+
}
|
|
944
|
+
problems;
|
|
945
|
+
};
|
|
946
|
+
var trimSlashes = (p) => p.trim().replace(/^\.?\/+|\/+$/g, "");
|
|
947
|
+
var list = (v) => v === void 0 ? [] : Array.isArray(v) ? v : [v];
|
|
948
|
+
var isFlatModes = (m) => Object.keys(m).some((k) => FLAT_MODE_KEYS.includes(k)) || Object.keys(m).length === 0;
|
|
949
|
+
function parseRepoInput(repo, branch) {
|
|
950
|
+
if (!repo) return void 0;
|
|
951
|
+
if (typeof repo === "string") {
|
|
952
|
+
const [owner, name] = repo.split("/");
|
|
953
|
+
return { owner, name, defaultBranch: branch ?? "main" };
|
|
954
|
+
}
|
|
955
|
+
return { owner: repo.owner, name: repo.name, defaultBranch: repo.defaultBranch ?? branch ?? "main" };
|
|
956
|
+
}
|
|
957
|
+
function conventionalLayers(p) {
|
|
958
|
+
return [
|
|
959
|
+
{ files: `${p.primitives}/*.tokens.json`, optional: false },
|
|
960
|
+
{ files: `${p.semantic}/${p.base}.tokens.json`, optional: true },
|
|
961
|
+
{ files: `${p.brands}/{brand}.tokens.json`, optional: false },
|
|
962
|
+
{ files: `${p.semantic}/{theme}.tokens.json`, optional: false },
|
|
963
|
+
{ files: `${p.brands}/{brand}.{theme}.tokens.json`, optional: true },
|
|
964
|
+
{ files: `${p.density}/{density}.tokens.json`, optional: false },
|
|
965
|
+
{ files: `${p.components}/*.tokens.json`, optional: false }
|
|
966
|
+
];
|
|
967
|
+
}
|
|
968
|
+
function defineConfig(input) {
|
|
969
|
+
const t = input.tokens ?? {};
|
|
970
|
+
const root = trimSlashes(t.root ?? DEFAULT_TOKEN_PATHS.root) || ".";
|
|
971
|
+
const tiersIn = t.tiers ? Object.fromEntries(Object.entries(t.tiers).map(([k, v]) => [k, list(v).map(trimSlashes)])) : void 0;
|
|
972
|
+
const firstDir = (tier) => tiersIn?.[tier]?.find((d) => !d.endsWith(".json"));
|
|
973
|
+
const baseFile = tiersIn?.base?.find((d) => d.endsWith(".json"));
|
|
974
|
+
const contrastPath = input.contrast?.path ? trimSlashes(input.contrast.path) : void 0;
|
|
975
|
+
const underRoot = (p) => root === "." ? p : p.startsWith(`${root}/`) ? p.slice(root.length + 1) : void 0;
|
|
976
|
+
const paths = {
|
|
977
|
+
root,
|
|
978
|
+
primitives: trimSlashes(t.primitives ?? firstDir("primitive") ?? DEFAULT_TOKEN_PATHS.primitives),
|
|
979
|
+
brands: trimSlashes(t.brands ?? firstDir("brand") ?? DEFAULT_TOKEN_PATHS.brands),
|
|
980
|
+
semantic: trimSlashes(t.semantic ?? firstDir("semantic") ?? DEFAULT_TOKEN_PATHS.semantic),
|
|
981
|
+
base: trimSlashes(t.base ?? (baseFile ? baseFile.split("/").pop().replace(/(\.tokens)?\.json$/, "") : DEFAULT_TOKEN_PATHS.base)),
|
|
982
|
+
density: trimSlashes(t.density ?? firstDir("density") ?? DEFAULT_TOKEN_PATHS.density),
|
|
983
|
+
components: trimSlashes(t.components ?? firstDir("component") ?? DEFAULT_TOKEN_PATHS.components),
|
|
984
|
+
contrastChecks: trimSlashes(t.contrastChecks ?? (contrastPath ? underRoot(contrastPath) ?? contrastPath : DEFAULT_TOKEN_PATHS.contrastChecks))
|
|
985
|
+
};
|
|
986
|
+
const tiers = tiersIn ?? {
|
|
987
|
+
primitive: [paths.primitives],
|
|
988
|
+
brand: [paths.brands],
|
|
989
|
+
base: [`${paths.semantic}/${paths.base}.tokens.json`],
|
|
990
|
+
semantic: [paths.semantic],
|
|
991
|
+
density: [paths.density],
|
|
992
|
+
component: [paths.components]
|
|
993
|
+
};
|
|
994
|
+
const layers = t.layers?.length ? t.layers.map((l) => typeof l === "string" ? { files: l, optional: false } : { files: l.files, optional: l.optional ?? false }) : conventionalLayers(paths);
|
|
995
|
+
const axes = {};
|
|
996
|
+
const m = input.modes ?? {};
|
|
997
|
+
if (isFlatModes(m)) {
|
|
998
|
+
for (const key of ["brands", "themes", "densities"]) {
|
|
999
|
+
const values = m[key] ?? [];
|
|
1000
|
+
if (!values.length) continue;
|
|
1001
|
+
const axis = AXIS_OF[key];
|
|
1002
|
+
const def = m.defaults?.[axis] ?? values[0];
|
|
1003
|
+
axes[axis] = { values, default: def, parity: axis === "theme" };
|
|
1004
|
+
}
|
|
1005
|
+
} else {
|
|
1006
|
+
for (const [name, a] of Object.entries(m)) axes[name] = { values: a.values, default: a.default ?? a.values[0], parity: a.parity ?? false };
|
|
1007
|
+
}
|
|
1008
|
+
const ordered = (axis) => {
|
|
1009
|
+
const a = axes[axis];
|
|
1010
|
+
return a ? [a.default, ...a.values.filter((v) => v !== a.default)] : [];
|
|
1011
|
+
};
|
|
1012
|
+
const modes = {
|
|
1013
|
+
brands: ordered("brand"),
|
|
1014
|
+
themes: ordered("theme"),
|
|
1015
|
+
densities: ordered("density"),
|
|
1016
|
+
defaults: { brand: axes.brand?.default, theme: axes.theme?.default, density: axes.density?.default }
|
|
1017
|
+
};
|
|
1018
|
+
const pkg = input.changesets?.package ?? input.changeset?.package;
|
|
1019
|
+
const g = input.governance ?? {};
|
|
1020
|
+
const contrastFile = contrastPath ?? [root === "." ? "" : root, paths.contrastChecks].filter(Boolean).join("/");
|
|
1021
|
+
return {
|
|
1022
|
+
repo: parseRepoInput(input.repo, input.branch),
|
|
1023
|
+
tokens: { ...paths, include: input.tokens?.include ?? ["**/*.tokens.json"], exclude: input.tokens?.exclude ?? [], tiers, layers },
|
|
1024
|
+
axes,
|
|
1025
|
+
modes,
|
|
1026
|
+
contrast: { path: contrastFile, modes: input.contrast?.modes },
|
|
1027
|
+
components: input.components ? {
|
|
1028
|
+
dir: trimSlashes(input.components.dir),
|
|
1029
|
+
docsSuffix: input.components.docsSuffix ?? ".docs.json",
|
|
1030
|
+
styleSuffix: input.components.styleSuffix ?? ".module.css",
|
|
1031
|
+
reference: input.components.reference,
|
|
1032
|
+
index: input.components.index,
|
|
1033
|
+
specs: input.components.specs
|
|
1034
|
+
} : void 0,
|
|
1035
|
+
docs: { ...input.docs },
|
|
1036
|
+
changesets: pkg ? { dir: trimSlashes(input.changesets?.dir ?? ".changeset"), package: pkg } : void 0,
|
|
1037
|
+
changeset: pkg ? { package: pkg } : void 0,
|
|
1038
|
+
cssVar: { prefix: input.cssVar?.prefix ?? "--", separator: input.cssVar?.separator ?? "-" },
|
|
1039
|
+
governance: {
|
|
1040
|
+
protectedBranches: g.protectedBranches ?? DEFAULT_GOVERNANCE.protectedBranches,
|
|
1041
|
+
branchPrefix: g.branchPrefix ?? DEFAULT_GOVERNANCE.branchPrefix,
|
|
1042
|
+
labels: g.labels ?? DEFAULT_GOVERNANCE.labels,
|
|
1043
|
+
guardrails: g.guardrails ?? [...DEFAULT_GOVERNANCE.guardrails, contrastFile],
|
|
1044
|
+
owners: { design: g.owners?.design ?? [], tech: g.owners?.tech ?? [] },
|
|
1045
|
+
...g.override ? {
|
|
1046
|
+
override: {
|
|
1047
|
+
allowed: g.override.allowed ?? DEFAULT_OVERRIDE.allowed,
|
|
1048
|
+
requireReason: g.override.requireReason ?? DEFAULT_OVERRIDE.requireReason,
|
|
1049
|
+
minReasonLength: g.override.minReasonLength ?? DEFAULT_OVERRIDE.minReasonLength
|
|
1050
|
+
}
|
|
1051
|
+
} : {},
|
|
1052
|
+
...g.rights ? { rights: normalizeRights(g.rights) } : {}
|
|
1053
|
+
},
|
|
1054
|
+
...input.audit ? { audit: { ...input.audit } } : {},
|
|
1055
|
+
...input.visualImpact ? { visualImpact: { mode: input.visualImpact.mode === "on-demand" ? "on-demand" : "auto" } } : {},
|
|
1056
|
+
...input.integrations ? { integrations: { ...input.integrations } } : {},
|
|
1057
|
+
...input.previews ? {
|
|
1058
|
+
previews: {
|
|
1059
|
+
packages: input.previews.packages ?? [],
|
|
1060
|
+
apps: { ...input.previews.apps },
|
|
1061
|
+
...input.previews.production ? { production: { ...input.previews.production } } : {}
|
|
1062
|
+
}
|
|
1063
|
+
} : {}
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
var isStr = (x) => typeof x === "string" && x.trim().length > 0;
|
|
1067
|
+
var isStrList = (x) => Array.isArray(x) && x.every(isStr);
|
|
1068
|
+
var isObj = (x) => !!x && typeof x === "object" && !Array.isArray(x);
|
|
1069
|
+
function loadConfig(source) {
|
|
1070
|
+
let data = source;
|
|
1071
|
+
if (typeof source === "string") {
|
|
1072
|
+
try {
|
|
1073
|
+
data = JSON.parse(source);
|
|
1074
|
+
} catch (err) {
|
|
1075
|
+
throw new ConfigError([`not valid JSON (${err.message})`]);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
if (!isObj(data)) throw new ConfigError(["top level must be an object"]);
|
|
1079
|
+
const problems = [];
|
|
1080
|
+
const d = data;
|
|
1081
|
+
const repo = d.repo;
|
|
1082
|
+
if (typeof repo === "string") {
|
|
1083
|
+
if (!/^[\w.-]+\/[\w.-]+$/.test(repo)) problems.push('"repo" must be "owner/name" or { "owner", "name" }');
|
|
1084
|
+
} else if (repo !== void 0) {
|
|
1085
|
+
if (!isObj(repo)) problems.push('"repo" must be "owner/name" or { "owner", "name" }');
|
|
1086
|
+
else {
|
|
1087
|
+
if (!isStr(repo.owner)) problems.push('"repo.owner" must be a non-empty string');
|
|
1088
|
+
if (!isStr(repo.name)) problems.push('"repo.name" must be a non-empty string');
|
|
1089
|
+
if (repo.defaultBranch !== void 0 && !isStr(repo.defaultBranch)) problems.push('"repo.defaultBranch" must be a non-empty string');
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
if (d.branch !== void 0 && !isStr(d.branch)) problems.push('"branch" must be a non-empty string');
|
|
1093
|
+
const tokens = d.tokens;
|
|
1094
|
+
if (tokens !== void 0) {
|
|
1095
|
+
if (!isObj(tokens)) problems.push('"tokens" must be an object');
|
|
1096
|
+
else
|
|
1097
|
+
for (const [k, v] of Object.entries(tokens)) {
|
|
1098
|
+
if (FLAT_TOKEN_KEYS.includes(k) || k === "root") {
|
|
1099
|
+
if (!isStr(v)) problems.push(`"tokens.${k}" must be a non-empty string`);
|
|
1100
|
+
} else if (k === "include" || k === "exclude") {
|
|
1101
|
+
if (!isStrList(v)) problems.push(`"tokens.${k}" must be a list of globs`);
|
|
1102
|
+
} else if (k === "tiers") {
|
|
1103
|
+
if (!isObj(v) || !Object.values(v).every((x) => isStr(x) || isStrList(x))) problems.push('"tokens.tiers" must map tier names to directories');
|
|
1104
|
+
} else if (k === "layers") {
|
|
1105
|
+
if (!Array.isArray(v) || !v.length || !v.every((l) => isStr(l) || isObj(l) && isStr(l.files) && (l.optional === void 0 || typeof l.optional === "boolean")))
|
|
1106
|
+
problems.push('"tokens.layers" must be a non-empty list of { "files", "optional"? }');
|
|
1107
|
+
} else if (!CANONICAL_TOKEN_KEYS.includes(k)) problems.push(`"tokens.${k}" is not a known key`);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
const modes = d.modes;
|
|
1111
|
+
let axisNames = [];
|
|
1112
|
+
if (modes !== void 0) {
|
|
1113
|
+
if (!isObj(modes)) problems.push('"modes" must be an object');
|
|
1114
|
+
else if (isFlatModes(modes)) {
|
|
1115
|
+
for (const k of ["brands", "themes", "densities"]) {
|
|
1116
|
+
if (modes[k] !== void 0 && !isStrList(modes[k])) problems.push(`"modes.${k}" must be a list of names`);
|
|
1117
|
+
}
|
|
1118
|
+
const defs = modes.defaults;
|
|
1119
|
+
const lists = { brand: "brands", theme: "themes", density: "densities" };
|
|
1120
|
+
for (const [k, l] of Object.entries(lists)) {
|
|
1121
|
+
const v = defs?.[k];
|
|
1122
|
+
if (v === void 0) continue;
|
|
1123
|
+
if (!isStr(v)) problems.push(`"modes.defaults.${k}" must be a string`);
|
|
1124
|
+
else if (isStrList(modes[l]) && !modes[l].includes(v)) problems.push(`"modes.defaults.${k}" ("${v}") is not in "modes.${l}"`);
|
|
1125
|
+
}
|
|
1126
|
+
axisNames = Object.entries(lists).filter(([, l]) => isStrList(modes[l]) && modes[l].length).map(([k]) => k);
|
|
1127
|
+
} else {
|
|
1128
|
+
for (const [name, a] of Object.entries(modes)) {
|
|
1129
|
+
if (!isObj(a) || !isStrList(a.values) || !a.values.length) {
|
|
1130
|
+
problems.push(`"modes.${name}.values" must be a non-empty list of names`);
|
|
1131
|
+
continue;
|
|
1132
|
+
}
|
|
1133
|
+
if (a.default !== void 0 && (!isStr(a.default) || !a.values.includes(a.default))) problems.push(`"modes.${name}.default" ("${String(a.default)}") is not in "modes.${name}.values"`);
|
|
1134
|
+
if (a.parity !== void 0 && typeof a.parity !== "boolean") problems.push(`"modes.${name}.parity" must be true or false`);
|
|
1135
|
+
axisNames.push(name);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
if (isObj(tokens) && Array.isArray(tokens.layers)) {
|
|
1140
|
+
for (const l of tokens.layers) {
|
|
1141
|
+
const files = typeof l === "string" ? l : isObj(l) ? String(l.files) : "";
|
|
1142
|
+
for (const ph of files.match(/\{([^}]+)\}/g) ?? []) {
|
|
1143
|
+
if (!axisNames.includes(ph.slice(1, -1))) problems.push(`layer "${files}" uses ${ph} but "modes.${ph.slice(1, -1)}" is not defined`);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
const contrast = d.contrast;
|
|
1148
|
+
if (contrast !== void 0 && (!isObj(contrast) || !isStr(contrast.path) || contrast.modes !== void 0 && !isStrList(contrast.modes)))
|
|
1149
|
+
problems.push('"contrast" must be { "path", "modes"? }');
|
|
1150
|
+
const comps = d.components;
|
|
1151
|
+
if (comps !== void 0 && (!isObj(comps) || !isStr(comps.dir))) problems.push('"components.dir" must be a non-empty string');
|
|
1152
|
+
const cs = d.changeset;
|
|
1153
|
+
if (cs !== void 0 && (!isObj(cs) || !isStr(cs.package))) problems.push('"changeset.package" must be a non-empty string');
|
|
1154
|
+
const css = d.changesets;
|
|
1155
|
+
if (css !== void 0 && (!isObj(css) || !isStr(css.package))) problems.push('"changesets.package" must be a non-empty string');
|
|
1156
|
+
const gov = d.governance;
|
|
1157
|
+
if (gov !== void 0) {
|
|
1158
|
+
if (!isObj(gov)) problems.push('"governance" must be an object');
|
|
1159
|
+
else {
|
|
1160
|
+
for (const k of ["protectedBranches", "labels", "guardrails"]) if (gov[k] !== void 0 && !(Array.isArray(gov[k]) && gov[k].every((x) => typeof x === "string"))) problems.push(`"governance.${k}" must be a list of strings`);
|
|
1161
|
+
if (gov.branchPrefix !== void 0 && !isStr(gov.branchPrefix)) problems.push('"governance.branchPrefix" must be a non-empty string');
|
|
1162
|
+
problems.push(...ownerEntryProblems(gov.owners));
|
|
1163
|
+
const ov = gov.override;
|
|
1164
|
+
if (ov !== void 0) {
|
|
1165
|
+
if (!isObj(ov)) problems.push('"governance.override" must be an object');
|
|
1166
|
+
else {
|
|
1167
|
+
if (ov.allowed !== void 0 && !isStrList(ov.allowed)) problems.push('"governance.override.allowed" must be a list of GitHub logins');
|
|
1168
|
+
if (ov.requireReason !== void 0 && typeof ov.requireReason !== "boolean") problems.push('"governance.override.requireReason" must be true or false');
|
|
1169
|
+
if (ov.minReasonLength !== void 0 && !(Number.isInteger(ov.minReasonLength) && ov.minReasonLength >= 0)) problems.push('"governance.override.minReasonLength" must be a whole number, 0 or more');
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
problems.push(...rightsProblems(gov.rights));
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
problems.push(...visualImpactProblems(d.visualImpact));
|
|
1176
|
+
const audit = d.audit;
|
|
1177
|
+
if (audit !== void 0) {
|
|
1178
|
+
if (!isObj(audit)) problems.push('"audit" must be an object');
|
|
1179
|
+
else for (const k of ["packages", "foreignLibraries", "nativeElements", "exclude"]) if (audit[k] !== void 0 && !isStrList(audit[k])) problems.push(`"audit.${k}" must be a list of strings`);
|
|
1180
|
+
}
|
|
1181
|
+
const pv = d.previews;
|
|
1182
|
+
if (pv !== void 0) {
|
|
1183
|
+
if (!isObj(pv)) problems.push('"previews" must be an object');
|
|
1184
|
+
else {
|
|
1185
|
+
if (pv.packages !== void 0 && !isStrList(pv.packages)) problems.push('"previews.packages" must be a list of package names');
|
|
1186
|
+
for (const field of ["apps", "production"]) {
|
|
1187
|
+
const apps = pv[field];
|
|
1188
|
+
if (apps === void 0) continue;
|
|
1189
|
+
if (!isObj(apps)) {
|
|
1190
|
+
problems.push(`"previews.${field}" must be an object`);
|
|
1191
|
+
continue;
|
|
1192
|
+
}
|
|
1193
|
+
for (const [k, v] of Object.entries(apps)) {
|
|
1194
|
+
if (!(k in PREVIEW_APP_LABELS)) problems.push(`"previews.${field}.${k}" is not a known app (${Object.keys(PREVIEW_APP_LABELS).join(", ")})`);
|
|
1195
|
+
else if (!isStr(v) || !/^https?:\/\//.test(v)) problems.push(`"previews.${field}.${k}" must be an http(s) URL${field === "apps" ? " template" : ""}`);
|
|
1196
|
+
else if (field === "production" && /\{\w+\}/.test(v)) problems.push(`"previews.production.${k}" is a fixed URL: placeholders such as {branch} belong in "previews.apps"`);
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
if (problems.length) throw new ConfigError(problems);
|
|
1202
|
+
return defineConfig(d);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
// ../core/src/node/audit.ts
|
|
1206
|
+
import { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
1207
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
1208
|
+
var toPosix = (p) => p.split(sep).join("/");
|
|
1209
|
+
function walk(dir, root, out, skipDirs) {
|
|
1210
|
+
let entries;
|
|
1211
|
+
try {
|
|
1212
|
+
entries = readdirSync(dir);
|
|
1213
|
+
} catch {
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
for (const name of entries) {
|
|
1217
|
+
const abs = join(dir, name);
|
|
1218
|
+
let st;
|
|
1219
|
+
try {
|
|
1220
|
+
st = statSync(abs);
|
|
1221
|
+
} catch {
|
|
1222
|
+
continue;
|
|
1223
|
+
}
|
|
1224
|
+
if (st.isDirectory()) {
|
|
1225
|
+
if (!skipDirs.includes(name)) walk(abs, root, out, skipDirs);
|
|
1226
|
+
} else out.push(toPosix(relative(root, abs)));
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
function findDesignSystemRoot(from) {
|
|
1230
|
+
let dir = resolve(from);
|
|
1231
|
+
for (; ; ) {
|
|
1232
|
+
if (existsSync(join(dir, "bridge.config.json"))) return dir;
|
|
1233
|
+
const up = dirname(dir);
|
|
1234
|
+
if (up === dir) return void 0;
|
|
1235
|
+
dir = up;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
var cache = /* @__PURE__ */ new Map();
|
|
1239
|
+
function loadDesignSystem(root, opts = {}) {
|
|
1240
|
+
const abs = resolve(root);
|
|
1241
|
+
const hit = cache.get(abs);
|
|
1242
|
+
if (hit && !opts.fresh) return hit;
|
|
1243
|
+
const cfgPath = join(abs, "bridge.config.json");
|
|
1244
|
+
if (!existsSync(cfgPath)) throw new Error(`No bridge.config.json in ${abs}: pass the design system repo (--ds <dir>) or run bridge init there.`);
|
|
1245
|
+
const config = loadConfig(readFileSync(cfgPath, "utf8"));
|
|
1246
|
+
const tokensRoot = config.tokens.root.replace(/\/$/, "");
|
|
1247
|
+
const listed = [];
|
|
1248
|
+
walk(join(abs, tokensRoot === "." ? "" : tokensRoot), abs, listed, ["node_modules", ".git", "dist"]);
|
|
1249
|
+
const files = /* @__PURE__ */ new Map();
|
|
1250
|
+
for (const f of selectTokenFiles(config, listed)) files.set(f, readFileSync(join(abs, tokensRoot === "." ? "" : tokensRoot, f), "utf8"));
|
|
1251
|
+
const set = new LayeredTokenSet(config, files);
|
|
1252
|
+
const components = [];
|
|
1253
|
+
let packages = config.audit?.packages;
|
|
1254
|
+
if (config.components) {
|
|
1255
|
+
const dir = join(abs, config.components.dir);
|
|
1256
|
+
if (existsSync(dir)) {
|
|
1257
|
+
for (const name of readdirSync(dir)) if (/^[A-Z][A-Za-z0-9]*$/.test(name) && statSync(join(dir, name)).isDirectory()) components.push(name);
|
|
1258
|
+
}
|
|
1259
|
+
if (!packages) {
|
|
1260
|
+
packages = ["@bridge-ds/react"];
|
|
1261
|
+
for (let d = dir; d.startsWith(abs) && d !== abs; d = dirname(d)) {
|
|
1262
|
+
const pj = join(d, "package.json");
|
|
1263
|
+
if (existsSync(pj)) {
|
|
1264
|
+
const name = JSON.parse(readFileSync(pj, "utf8")).name;
|
|
1265
|
+
if (name && !packages.includes(name)) packages.unshift(name);
|
|
1266
|
+
break;
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
const index = new DesignSystemIndex(set, {
|
|
1272
|
+
packages: packages ?? ["@bridge-ds/react"],
|
|
1273
|
+
components: components.sort(),
|
|
1274
|
+
...config.audit?.foreignLibraries ? { foreignLibraries: config.audit.foreignLibraries } : {},
|
|
1275
|
+
...config.audit?.nativeElements ? { nativeElements: config.audit.nativeElements } : {}
|
|
1276
|
+
});
|
|
1277
|
+
const loaded = { root: abs, config, set, index };
|
|
1278
|
+
cache.set(abs, loaded);
|
|
1279
|
+
return loaded;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
// src/index.ts
|
|
1283
|
+
var {
|
|
1284
|
+
createPlugin,
|
|
1285
|
+
utils: { report, ruleMessages, validateOptions }
|
|
1286
|
+
} = stylelint;
|
|
1287
|
+
var ruleName = "bridge/use-tokens";
|
|
1288
|
+
var messages = ruleMessages(ruleName, {
|
|
1289
|
+
rejected: (value, category, token, tokenValue, level2) => level2 === "exact" ? `Use var(${token}) instead of ${value}: it is the same ${category} (${tokenValue}).` : `Use a token instead of the raw ${category} ${value}: closest is var(${token}) (${tokenValue}, ${level2} match).`
|
|
1290
|
+
});
|
|
1291
|
+
var meta = { url: "https://github.com/peterbono/ai-design-system-demo/tree/main/packages/stylelint-plugin#bridgeuse-tokens", fixable: true };
|
|
1292
|
+
var RANK = { exact: 3, high: 2, medium: 1, low: 0 };
|
|
1293
|
+
var isString = (x) => typeof x === "string";
|
|
1294
|
+
var rule = (primary, secondary) => (root, result) => {
|
|
1295
|
+
const valid = validateOptions(
|
|
1296
|
+
result,
|
|
1297
|
+
ruleName,
|
|
1298
|
+
{ actual: primary, possible: [true, false] },
|
|
1299
|
+
{ actual: secondary, possible: { designSystem: [isString], minConfidence: ["exact", "high", "medium", "low"], categories: [isString], ignoreProperties: [isString] }, optional: true }
|
|
1300
|
+
);
|
|
1301
|
+
if (!valid || !primary) return;
|
|
1302
|
+
const dsRoot = secondary?.designSystem ?? process.env.BRIDGE_DS ?? findDesignSystemRoot(root.source?.input.file ?? process.cwd()) ?? findDesignSystemRoot(process.cwd());
|
|
1303
|
+
let ds;
|
|
1304
|
+
try {
|
|
1305
|
+
if (!dsRoot) throw new Error("no bridge.config.json found");
|
|
1306
|
+
ds = loadDesignSystem(dsRoot).index;
|
|
1307
|
+
} catch (e) {
|
|
1308
|
+
result.warn(`${ruleName}: ${e.message}. Set the "designSystem" option to your design system repo.`, { stylelintType: "invalidOption" });
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
const min = RANK[secondary?.minConfidence ?? "medium"];
|
|
1312
|
+
const ignore = (secondary?.ignoreProperties ?? []).map((p) => p.startsWith("/") && p.lastIndexOf("/") > 0 ? new RegExp(p.slice(1, p.lastIndexOf("/")), p.slice(p.lastIndexOf("/") + 1)) : new RegExp(`^${p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`));
|
|
1313
|
+
root.walkDecls((decl) => {
|
|
1314
|
+
if (ignore.some((r) => r.test(decl.prop))) return;
|
|
1315
|
+
const scan = scanCssValue(decl.prop, decl.value);
|
|
1316
|
+
const found = scan.hits.filter((hit) => !secondary?.categories || secondary.categories.includes(hit.category)).map((hit) => ({ hit, s: ds.suggest(hit.category, hit.text, decl.prop.toLowerCase()) })).filter((x) => Boolean(x.s) && RANK[x.s.level] >= min);
|
|
1317
|
+
const safe = found.filter((x) => isSafeFix(x.hit, x.s));
|
|
1318
|
+
let fixed = false;
|
|
1319
|
+
const fix = () => {
|
|
1320
|
+
if (fixed) return;
|
|
1321
|
+
fixed = true;
|
|
1322
|
+
let v = decl.value;
|
|
1323
|
+
for (const { hit, s } of [...safe].sort((a, b) => b.hit.index - a.hit.index)) v = v.slice(0, hit.index) + `var(${s.cssVar})` + v.slice(hit.index + hit.text.length);
|
|
1324
|
+
decl.value = v;
|
|
1325
|
+
};
|
|
1326
|
+
const offset = decl.prop.length + (decl.raws.between?.length ?? 1);
|
|
1327
|
+
for (const x of found) {
|
|
1328
|
+
report({
|
|
1329
|
+
result,
|
|
1330
|
+
ruleName,
|
|
1331
|
+
message: messages.rejected(x.hit.text, x.hit.category, x.s.cssVar, x.s.value, x.s.level),
|
|
1332
|
+
node: decl,
|
|
1333
|
+
index: offset + x.hit.index,
|
|
1334
|
+
endIndex: offset + x.hit.index + x.hit.text.length,
|
|
1335
|
+
...safe.includes(x) ? { fix } : {}
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
});
|
|
1339
|
+
};
|
|
1340
|
+
rule.ruleName = ruleName;
|
|
1341
|
+
rule.messages = messages;
|
|
1342
|
+
rule.meta = meta;
|
|
1343
|
+
var plugin = createPlugin(ruleName, rule);
|
|
1344
|
+
var index_default = plugin;
|
|
1345
|
+
export {
|
|
1346
|
+
index_default as default,
|
|
1347
|
+
messages,
|
|
1348
|
+
ruleName
|
|
1349
|
+
};
|