@bridge-ds/core 0.7.0 → 0.9.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.
@@ -10,3 +10,4 @@ export * from './report';
10
10
  export * from './style';
11
11
  export * from './ignore';
12
12
  export * from './markup';
13
+ export * from './missing-pieces';
@@ -1,35 +1,278 @@
1
- import { A, a, b, C, D, c, F, I, L, d, M, N, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, B, E, G, H, J, K, O, P, Q, R, S, T, U, V, W, X, Y, Z } from "../report-iPSnsjYi.js";
1
+ import { i as isAuditable, a as auditFile } from "../report-DY_peRy4.js";
2
+ import { A, b, c, C, D, d, F, I, L, e, M, N, f, g, h, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, B, E, G, H, J, K, O, P, Q, R, S, T, U, V, W, X, Y, Z } from "../report-DY_peRy4.js";
3
+ import { p as pieceId, a as proposedTokenPath, t as tokenLabel } from "../prod-branch-D5Tl87Wf.js";
4
+ const lines = (text) => text.split(/\r?\n/);
5
+ function lineBag(text) {
6
+ const bag = /* @__PURE__ */ new Map();
7
+ for (const l2 of text ? lines(text) : []) {
8
+ const k2 = l2.trim();
9
+ if (k2) bag.set(k2, (bag.get(k2) ?? 0) + 1);
10
+ }
11
+ return bag;
12
+ }
13
+ const offsetOf = (text, line, column) => {
14
+ let off = 0;
15
+ const ls = text.split("\n");
16
+ for (let i = 0; i < line - 1 && i < ls.length; i++) off += ls[i].length + 1;
17
+ return off + Math.max(0, column - 1);
18
+ };
19
+ function enclosingTag(text, offset) {
20
+ for (let i = offset - 1; i >= Math.max(0, offset - 4e3); i--) {
21
+ if (text[i] !== "<" || !/[A-Za-z]/.test(text[i + 1] ?? "")) continue;
22
+ let depth = 0;
23
+ let closed = false;
24
+ for (let j2 = i + 1; j2 < offset; j2++) {
25
+ const c2 = text[j2];
26
+ if (c2 === "{") depth++;
27
+ else if (c2 === "}") depth--;
28
+ else if (c2 === ">" && depth <= 0 && text[j2 - 1] !== "=") {
29
+ closed = true;
30
+ break;
31
+ }
32
+ }
33
+ if (closed) return null;
34
+ const m2 = /^<([A-Za-z][\w.]*)/.exec(text.slice(i));
35
+ return m2 ? { name: m2[1], start: i } : null;
36
+ }
37
+ return null;
38
+ }
39
+ function childText(text, tagStart) {
40
+ let depth = 0;
41
+ for (let j2 = tagStart + 1; j2 < text.length; j2++) {
42
+ const c2 = text[j2];
43
+ if (c2 === "{") depth++;
44
+ else if (c2 === "}") depth--;
45
+ else if (c2 === ">" && depth <= 0 && text[j2 - 1] !== "=") {
46
+ if (text[j2 - 1] === "/") return null;
47
+ const rest = /^([^<{]*)</.exec(text.slice(j2 + 1));
48
+ const t2 = rest?.[1]?.trim() ?? "";
49
+ return t2 && t2.split(/\s+/).length <= 3 && t2.length <= 30 ? t2 : null;
50
+ }
51
+ }
52
+ return null;
53
+ }
54
+ function ruleClasses(text, offset) {
55
+ const open = text.lastIndexOf("{", offset);
56
+ if (open < 0) return [];
57
+ const prev = Math.max(text.lastIndexOf("}", open), text.lastIndexOf(";", open));
58
+ const selector = text.slice(prev + 1, open);
59
+ return [...selector.matchAll(/\.([A-Za-z_][\w-]*)/g)].map((m2) => m2[1]);
60
+ }
61
+ const GENERIC = /* @__PURE__ */ new Set(["status", "badge", "tag", "pill", "chip", "style", "styles", "root", "wrapper", "container", "item", "label", "text", "color", "bg", "is", "has", "variant", "tone", "custom", "new"]);
62
+ function nameFromClass(cls, component) {
63
+ const words = cls.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[\s_-]+/).map((w2) => w2.toLowerCase()).filter(Boolean);
64
+ const comp = component?.toLowerCase();
65
+ const kept = words.filter((w2) => w2 !== comp && !GENERIC.has(w2));
66
+ return kept[kept.length - 1] ?? words[words.length - 1] ?? cls;
67
+ }
68
+ const ASPECT = { color: "tone", radius: "shape", spacing: "size", size: "size", "font-size": "size", "line-height": "size" };
69
+ function aspectOf(values) {
70
+ for (const a of ["tone", "shape", "size"]) if (values.some((v2) => ASPECT[v2.category] === a)) return a;
71
+ return "style";
72
+ }
73
+ const norm = (f2) => f2.category === "color" ? f2.value.toLowerCase() : f2.value.replace(/\s+/g, "");
74
+ function nearestOf(s2) {
75
+ if (!s2) return void 0;
76
+ return { token: s2.token, cssVar: s2.cssVar, value: s2.value, level: s2.level, label: tokenLabel(s2.token) };
77
+ }
78
+ const TOKEN_TYPE = { color: "color", duration: "duration", "font-weight": "fontWeight", "line-height": "number", "z-index": "number", "font-family": "fontFamily", shadow: "shadow", easing: "cubicBezier" };
79
+ const valueTitle = (category, value) => {
80
+ const v2 = category === "color" && /^#[0-9a-f]+$/i.test(value) ? value.toUpperCase() : value;
81
+ const word = { color: "color", radius: "radius", spacing: "spacing", size: "size", "font-size": "text size", "font-weight": "font weight", "line-height": "line height", shadow: "shadow", duration: "duration", easing: "easing", "z-index": "layer", "border-width": "border width", "font-family": "font" };
82
+ return `New ${word[category] ?? category} ${v2}`;
83
+ };
84
+ function missingPieces(input) {
85
+ const { ds } = input;
86
+ const files = input.files.filter((f2) => f2.after !== null && isAuditable(f2.path));
87
+ const fixes = [];
88
+ const variants = /* @__PURE__ */ new Map();
89
+ const tokens = /* @__PURE__ */ new Map();
90
+ const foreign = /* @__PURE__ */ new Map();
91
+ const classUse = (cls) => {
92
+ const re = new RegExp(`<([A-Z][\\w.]*)\\b[^<]*?styles(?:\\.${cls}\\b|\\[['"]${cls}['"]\\])`);
93
+ for (const f2 of files) {
94
+ const m2 = re.exec(f2.after);
95
+ if (m2 && ds.components.has(m2[1].split(".")[0])) return { component: m2[1].split(".")[0], text: childText(f2.after, m2.index) };
96
+ }
97
+ return null;
98
+ };
99
+ for (const f2 of files) {
100
+ const after = f2.after;
101
+ const report = auditFile(f2.path, after, ds);
102
+ const before = lineBag(f2.before);
103
+ const afterLines = lines(after);
104
+ const added = (line) => {
105
+ const k2 = afterLines[line - 1]?.trim() ?? "";
106
+ const n2 = before.get(k2) ?? 0;
107
+ if (n2 > 0) {
108
+ before.set(k2, n2 - 1);
109
+ return false;
110
+ }
111
+ return true;
112
+ };
113
+ const addedLines = /* @__PURE__ */ new Map();
114
+ const isNew = (line) => {
115
+ if (!addedLines.has(line)) addedLines.set(line, added(line));
116
+ return addedLines.get(line);
117
+ };
118
+ const css = report.lang === "css" || report.lang === "scss" || report.lang === "less";
119
+ for (const finding of report.findings) {
120
+ if (!isNew(finding.line)) continue;
121
+ const s2 = finding.suggestion;
122
+ const at = { file: f2.path, line: finding.line };
123
+ if (s2?.level === "exact" || s2?.level === "high" && finding.category !== "color") {
124
+ fixes.push({ ...at, what: finding.value, use: s2.token });
125
+ continue;
126
+ }
127
+ const offset = offsetOf(after, finding.line, finding.column);
128
+ let component = null;
129
+ let name = null;
130
+ if (css) {
131
+ const classes = ruleClasses(after, offset);
132
+ for (const cls of [...classes].reverse()) {
133
+ const use = classUse(cls);
134
+ if (use) {
135
+ component = use.component;
136
+ name = nameFromClass(cls, use.component);
137
+ break;
138
+ }
139
+ }
140
+ if (!component && classes.length) name = nameFromClass(classes[classes.length - 1]);
141
+ } else {
142
+ const tag = enclosingTag(after, offset);
143
+ const root = tag?.name.split(".")[0];
144
+ if (tag && root && ds.components.has(root)) {
145
+ component = root;
146
+ name = childText(after, tag.start);
147
+ const cls = /className=\{?["'`]?([\w-]+)/.exec(after.slice(tag.start, offset))?.[1];
148
+ if (!name && cls) name = nameFromClass(cls, root);
149
+ }
150
+ }
151
+ const hit = { ...at, finding };
152
+ if (component) {
153
+ const label = (name ?? "custom").toLowerCase();
154
+ const key2 = `${component}|${label}`;
155
+ const v2 = variants.get(key2) ?? { component, name: label, hits: [] };
156
+ v2.hits.push(hit);
157
+ variants.set(key2, v2);
158
+ continue;
159
+ }
160
+ const key = `${finding.category}|${norm(finding)}`;
161
+ const t2 = tokens.get(key) ?? { category: finding.category, value: finding.value, name, hits: [] };
162
+ if (!t2.name && name) t2.name = name;
163
+ t2.hits.push(hit);
164
+ tokens.set(key, t2);
165
+ }
166
+ for (const c2 of report.components) {
167
+ if (!isNew(c2.line)) continue;
168
+ if (c2.suggestion) {
169
+ fixes.push({ file: f2.path, line: c2.line, what: c2.name, use: c2.suggestion });
170
+ continue;
171
+ }
172
+ if (c2.kind !== "foreign") continue;
173
+ const e2 = foreign.get(c2.name) ?? { finding: c2, hits: [] };
174
+ e2.hits.push({ file: f2.path, line: c2.line });
175
+ foreign.set(c2.name, e2);
176
+ }
177
+ }
178
+ const where = (hits) => hits.slice(0, 5).map((h2) => ({ file: h2.file, line: h2.line }));
179
+ const pieces = [];
180
+ for (const v2 of variants.values()) {
181
+ const values = [];
182
+ for (const h2 of v2.hits) {
183
+ const pv = { category: h2.finding.category, ...h2.finding.property ? { property: h2.finding.property } : {}, value: h2.finding.value };
184
+ if (!values.some((x2) => x2.category === pv.category && x2.property === pv.property && norm(x2) === norm(pv))) values.push(pv);
185
+ }
186
+ const aspect = aspectOf(values);
187
+ const bg = v2.hits.find((h2) => /background/.test(h2.finding.property ?? ""))?.finding.suggestion;
188
+ const nearest = nearestOf(bg ?? v2.hits.find((h2) => h2.finding.suggestion)?.finding.suggestion);
189
+ const shown = values.map((x2) => `${x2.property ?? x2.category} ${x2.value}`).join(", ");
190
+ pieces.push({
191
+ id: pieceId([v2.component, v2.name, aspect]),
192
+ kind: "variant",
193
+ title: `${v2.component}: ${v2.name} ${aspect}`,
194
+ summary: `${v2.component} restyled by hand (${shown}).${nearest ? ` ${nearest.level === "high" ? "It looks a lot like" : "The closest in the library is"} ${nearest.label}.` : ""}`,
195
+ name: v2.name,
196
+ component: v2.component,
197
+ aspect,
198
+ values,
199
+ ...nearest ? { nearest } : {},
200
+ where: where(v2.hits)
201
+ });
202
+ }
203
+ for (const [name, e2] of foreign) {
204
+ pieces.push({
205
+ id: pieceId(["component", name]),
206
+ kind: "component",
207
+ title: `New component: ${name}`,
208
+ summary: `${name} comes from ${e2.finding.library}, and the library has nothing like it.`,
209
+ name,
210
+ component: name.split(".").pop(),
211
+ values: [],
212
+ where: where(e2.hits)
213
+ });
214
+ }
215
+ for (const t2 of tokens.values()) {
216
+ const nearest = nearestOf(t2.hits[0].finding.suggestion);
217
+ const pname = t2.name ?? `${t2.category === "color" ? "custom" : t2.category}`;
218
+ const path = proposedTokenPath(nearest?.token, pname, t2.category);
219
+ const tokenFiles = nearest && input.set ? [...new Set(input.set.defs.filter((d2) => d2.path === nearest.token).map((d2) => d2.file))] : [];
220
+ const n2 = t2.hits.length;
221
+ pieces.push({
222
+ id: pieceId([t2.category, t2.name ?? t2.value.replace(/^#/, "")]),
223
+ kind: "token",
224
+ title: valueTitle(t2.category, t2.value),
225
+ summary: `Written by hand in ${n2 === 1 ? "one place" : `${n2} places`}.${nearest ? ` ${nearest.level === "high" ? "It looks a lot like" : "The closest in the library is"} ${nearest.label} (${nearest.value}).` : " The library has nothing close."}`,
226
+ name: pname,
227
+ values: [{ category: t2.category, ...t2.hits[0].finding.property ? { property: t2.hits[0].finding.property } : {}, value: t2.value }],
228
+ ...nearest ? { nearest } : {},
229
+ where: where(t2.hits),
230
+ token: { path, type: TOKEN_TYPE[t2.category] ?? "dimension", value: t2.value, files: tokenFiles }
231
+ });
232
+ }
233
+ const order = { variant: 0, component: 1, token: 2 };
234
+ const seen = /* @__PURE__ */ new Set();
235
+ const out = pieces.sort((a, b2) => order[a.kind] - order[b2.kind]).map((p2) => {
236
+ let id = p2.id;
237
+ for (let i = 2; seen.has(id); i++) id = `${p2.id}-${i}`;
238
+ seen.add(id);
239
+ return id === p2.id ? p2 : { ...p2, id };
240
+ });
241
+ return { pieces: out, fixes, files: files.length };
242
+ }
2
243
  export {
3
244
  A as AUDIT_EXTENSIONS,
4
- a as AUDIT_IGNORE_DIRS,
5
- b as AUDIT_SCHEMA_VERSION,
245
+ b as AUDIT_IGNORE_DIRS,
246
+ c as AUDIT_SCHEMA_VERSION,
6
247
  C as COLOR_LITERAL_RE,
7
248
  D as DEFAULT_NATIVE_ELEMENTS,
8
- c as DesignSystemIndex,
249
+ d as DesignSystemIndex,
9
250
  F as FINDING_CATEGORIES,
10
251
  I as IGNORE_DIRECTIVE,
11
252
  L as LOOKALIKE_ATTRS,
12
- d as LOOKALIKE_CLASSES,
253
+ e as LOOKALIKE_CLASSES,
13
254
  M as MARKUP_EXTENSIONS,
14
255
  N as NAMED_COLORS,
15
- e as applyAllowlist,
16
- f as applyIgnoreComments,
17
- g as auditCssText,
18
- h as auditFile,
19
- i as auditMarkdown,
20
- j as blankComments,
21
- k as buildAuditReport,
22
- l as classifyModule,
23
- m as classifyTag,
24
- n as compareWithBaseline,
25
- o as cssPropertyOf,
26
- p as deltaE,
27
- q as domRendererComponent,
28
- r as dsEquivalent,
29
- s as historyPoint,
30
- t as ignoreDirectives,
31
- u as importsOf,
32
- v as isAuditable,
256
+ f as applyAllowlist,
257
+ g as applyIgnoreComments,
258
+ h as auditCssText,
259
+ auditFile,
260
+ j as auditMarkdown,
261
+ k as blankComments,
262
+ l as buildAuditReport,
263
+ childText,
264
+ m as classifyModule,
265
+ n as classifyTag,
266
+ o as compareWithBaseline,
267
+ p as cssPropertyOf,
268
+ q as deltaE,
269
+ r as domRendererComponent,
270
+ s as dsEquivalent,
271
+ enclosingTag,
272
+ t as historyPoint,
273
+ u as ignoreDirectives,
274
+ v as importsOf,
275
+ isAuditable,
33
276
  w as isColorLiteral,
34
277
  x as isNonVisual,
35
278
  y as isRouterLink,
@@ -38,11 +281,14 @@ export {
38
281
  E as lineCol,
39
282
  G as lookalikesInClassList,
40
283
  H as markupBlocks,
284
+ missingPieces,
285
+ nameFromClass,
41
286
  J as nativeEquivalent,
42
287
  K as packageName,
43
288
  O as parseAllowlist,
44
289
  P as parseCssColor,
45
290
  Q as planCssFixes,
291
+ ruleClasses,
46
292
  R as scanClassString,
47
293
  S as scanCode,
48
294
  T as scanCssText,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
1
+ {"version":3,"file":"index.js","sources":["../../src/audit/missing-pieces.ts"],"sourcesContent":["// What a product branch introduces that the library lacks: the adoption audit run on each changed product file at the\n// branch head and at its start (the merge base with main), keeping only what the branch added. Each new hardcoded value\n// or foreign component is then mapped to the nearest library target:\n// - values written on a library component (an inline style, or a CSS module class used on it) form one variant of that\n// component: \"Badge: overdue tone\";\n// - a component from another UI library with no library equivalent is a new component;\n// - any other value is a token the library lacks: \"New color #C2410C\", beside the closest token.\n// Values that equal a token, and components the library already has, are fixes (use the library), not pieces.\n// Pure: file texts in, pieces out. Uses the audit, so the TypeScript compiler (syntax only).\nimport type { LayeredTokenSet } from '../layered';\nimport { pieceId, proposedTokenPath, tokenLabel, type MissingPiece, type PieceAspect, type PieceNearest, type PieceValue } from '../prod-branch';\nimport type { DesignSystemIndex } from './ds-index';\nimport { auditFile, isAuditable } from './report';\nimport type { ComponentFinding, StyleFinding, TokenSuggestion } from './types';\n\nexport interface BranchFile {\n /** Repo-relative path. */\n path: string;\n /** Text at the branch start, null when the branch adds the file. */\n before: string | null;\n /** Text at the branch head, null when the branch removes it. */\n after: string | null;\n}\n\nexport interface MissingPiecesResult {\n pieces: MissingPiece[];\n /** New values equal to a token and new non-library components the library has an equivalent for. */\n fixes: { file: string; line: number; what: string; use: string }[];\n /** Files read. */\n files: number;\n}\n\nconst lines = (text: string) => text.split(/\\r?\\n/);\n\n/** Trimmed lines of a text as a multiset: a finding is new when its line was not there before. */\nfunction lineBag(text: string | null): Map<string, number> {\n const bag = new Map<string, number>();\n for (const l of text ? lines(text) : []) {\n const k = l.trim();\n if (k) bag.set(k, (bag.get(k) ?? 0) + 1);\n }\n return bag;\n}\n\nconst offsetOf = (text: string, line: number, column: number): number => {\n let off = 0;\n const ls = text.split('\\n');\n for (let i = 0; i < line - 1 && i < ls.length; i++) off += ls[i]!.length + 1;\n return off + Math.max(0, column - 1);\n};\n\n/** The JSX element whose opening tag holds `offset` (an inline style value), or null. */\nexport function enclosingTag(text: string, offset: number): { name: string; start: number } | null {\n for (let i = offset - 1; i >= Math.max(0, offset - 4000); i--) {\n if (text[i] !== '<' || !/[A-Za-z]/.test(text[i + 1] ?? '')) continue;\n let depth = 0;\n let closed = false;\n for (let j = i + 1; j < offset; j++) {\n const c = text[j];\n if (c === '{') depth++;\n else if (c === '}') depth--;\n else if (c === '>' && depth <= 0 && text[j - 1] !== '=') {\n closed = true;\n break;\n }\n }\n if (closed) return null;\n const m = /^<([A-Za-z][\\w.]*)/.exec(text.slice(i));\n return m ? { name: m[1]!, start: i } : null;\n }\n return null;\n}\n\n/** The plain text right after an opening tag (`<Badge ...>Overdue</Badge>` gives \"Overdue\"), when short. */\nexport function childText(text: string, tagStart: number): string | null {\n let depth = 0;\n for (let j = tagStart + 1; j < text.length; j++) {\n const c = text[j];\n if (c === '{') depth++;\n else if (c === '}') depth--;\n else if (c === '>' && depth <= 0 && text[j - 1] !== '=') {\n if (text[j - 1] === '/') return null;\n const rest = /^([^<{]*)</.exec(text.slice(j + 1));\n const t = rest?.[1]?.trim() ?? '';\n return t && t.split(/\\s+/).length <= 3 && t.length <= 30 ? t : null;\n }\n }\n return null;\n}\n\n/** The classes of the CSS rule a declaration at `offset` belongs to: `.badge.overdue { color: red }` gives [\"badge\", \"overdue\"]. */\nexport function ruleClasses(text: string, offset: number): string[] {\n const open = text.lastIndexOf('{', offset);\n if (open < 0) return [];\n const prev = Math.max(text.lastIndexOf('}', open), text.lastIndexOf(';', open));\n const selector = text.slice(prev + 1, open);\n return [...selector.matchAll(/\\.([A-Za-z_][\\w-]*)/g)].map((m) => m[1]!);\n}\n\nconst GENERIC = new Set(['status', 'badge', 'tag', 'pill', 'chip', 'style', 'styles', 'root', 'wrapper', 'container', 'item', 'label', 'text', 'color', 'bg', 'is', 'has', 'variant', 'tone', 'custom', 'new']);\n\n/** \"badgeOverdue\" on Badge -> \"overdue\"; \"invoice-status--late\" -> \"late\". */\nexport function nameFromClass(cls: string, component?: string): string {\n const words = cls\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .split(/[\\s_-]+/)\n .map((w) => w.toLowerCase())\n .filter(Boolean);\n const comp = component?.toLowerCase();\n const kept = words.filter((w) => w !== comp && !GENERIC.has(w));\n return kept[kept.length - 1] ?? words[words.length - 1] ?? cls;\n}\n\nconst ASPECT: Record<string, PieceAspect> = { color: 'tone', radius: 'shape', spacing: 'size', size: 'size', 'font-size': 'size', 'line-height': 'size' };\n\nfunction aspectOf(values: PieceValue[]): PieceAspect {\n for (const a of ['tone', 'shape', 'size'] as const) if (values.some((v) => ASPECT[v.category] === a)) return a;\n return 'style';\n}\n\nconst norm = (f: { category: string; value: string }) => (f.category === 'color' ? f.value.toLowerCase() : f.value.replace(/\\s+/g, ''));\n\nfunction nearestOf(s: TokenSuggestion | undefined): PieceNearest | undefined {\n if (!s) return undefined;\n return { token: s.token, cssVar: s.cssVar, value: s.value, level: s.level, label: tokenLabel(s.token) };\n}\n\nconst TOKEN_TYPE: Record<string, string> = { color: 'color', duration: 'duration', 'font-weight': 'fontWeight', 'line-height': 'number', 'z-index': 'number', 'font-family': 'fontFamily', shadow: 'shadow', easing: 'cubicBezier' };\n\nconst valueTitle = (category: string, value: string): string => {\n const v = category === 'color' && /^#[0-9a-f]+$/i.test(value) ? value.toUpperCase() : value;\n const word: Record<string, string> = { color: 'color', radius: 'radius', spacing: 'spacing', size: 'size', 'font-size': 'text size', 'font-weight': 'font weight', 'line-height': 'line height', shadow: 'shadow', duration: 'duration', easing: 'easing', 'z-index': 'layer', 'border-width': 'border width', 'font-family': 'font' };\n return `New ${word[category] ?? category} ${v}`;\n};\n\ninterface Hit {\n file: string;\n line: number;\n finding: StyleFinding;\n}\n\n/**\n * The pieces a product branch adds that the library lacks. `files` are the changed files (only auditable ones are\n * read; pass the product's files). `set` lets token pieces name the token files of the closest token (light and dark).\n */\nexport function missingPieces(input: { files: BranchFile[]; ds: DesignSystemIndex; set?: LayeredTokenSet | undefined }): MissingPiecesResult {\n const { ds } = input;\n const files = input.files.filter((f) => f.after !== null && isAuditable(f.path));\n const fixes: MissingPiecesResult['fixes'] = [];\n const variants = new Map<string, { component: string; name: string; hits: Hit[] }>();\n const tokens = new Map<string, { category: string; value: string; name: string | null; hits: Hit[] }>();\n const foreign = new Map<string, { finding: ComponentFinding; hits: { file: string; line: number }[] }>();\n // Classes of CSS modules: which DS component uses each one, read from every changed file at its head.\n const classUse = (cls: string): { component: string; text: string | null } | null => {\n const re = new RegExp(`<([A-Z][\\\\w.]*)\\\\b[^<]*?styles(?:\\\\.${cls}\\\\b|\\\\[['\"]${cls}['\"]\\\\])`);\n for (const f of files) {\n const m = re.exec(f.after!);\n if (m && ds.components.has(m[1]!.split('.')[0]!)) return { component: m[1]!.split('.')[0]!, text: childText(f.after!, m.index) };\n }\n return null;\n };\n\n for (const f of files) {\n const after = f.after!;\n const report = auditFile(f.path, after, ds);\n const before = lineBag(f.before);\n const afterLines = lines(after);\n // Lines the branch added (a line that was there before is not the branch's doing), counted so repeats stay fair.\n const added = (line: number) => {\n const k = afterLines[line - 1]?.trim() ?? '';\n const n = before.get(k) ?? 0;\n if (n > 0) {\n before.set(k, n - 1);\n return false;\n }\n return true;\n };\n const addedLines = new Map<number, boolean>();\n const isNew = (line: number) => {\n if (!addedLines.has(line)) addedLines.set(line, added(line));\n return addedLines.get(line)!;\n };\n const css = report.lang === 'css' || report.lang === 'scss' || report.lang === 'less';\n for (const finding of report.findings) {\n if (!isNew(finding.line)) continue;\n const s = finding.suggestion;\n const at = { file: f.path, line: finding.line };\n if (s?.level === 'exact' || (s?.level === 'high' && finding.category !== 'color')) {\n fixes.push({ ...at, what: finding.value, use: s.token });\n continue;\n }\n const offset = offsetOf(after, finding.line, finding.column);\n let component: string | null = null;\n let name: string | null = null;\n if (css) {\n const classes = ruleClasses(after, offset);\n for (const cls of [...classes].reverse()) {\n const use = classUse(cls);\n if (use) {\n component = use.component;\n name = nameFromClass(cls, use.component);\n break;\n }\n }\n if (!component && classes.length) name = nameFromClass(classes[classes.length - 1]!);\n } else {\n const tag = enclosingTag(after, offset);\n const root = tag?.name.split('.')[0];\n if (tag && root && ds.components.has(root)) {\n component = root;\n name = childText(after, tag.start);\n const cls = /className=\\{?[\"'`]?([\\w-]+)/.exec(after.slice(tag.start, offset))?.[1];\n if (!name && cls) name = nameFromClass(cls, root);\n }\n }\n const hit: Hit = { ...at, finding };\n if (component) {\n const label = (name ?? 'custom').toLowerCase();\n const key = `${component}|${label}`;\n const v = variants.get(key) ?? { component, name: label, hits: [] };\n v.hits.push(hit);\n variants.set(key, v);\n continue;\n }\n const key = `${finding.category}|${norm(finding)}`;\n const t = tokens.get(key) ?? { category: finding.category, value: finding.value, name, hits: [] };\n if (!t.name && name) t.name = name;\n t.hits.push(hit);\n tokens.set(key, t);\n }\n for (const c of report.components) {\n if (!isNew(c.line)) continue;\n if (c.suggestion) {\n fixes.push({ file: f.path, line: c.line, what: c.name, use: c.suggestion });\n continue;\n }\n if (c.kind !== 'foreign') continue;\n const e = foreign.get(c.name) ?? { finding: c, hits: [] };\n e.hits.push({ file: f.path, line: c.line });\n foreign.set(c.name, e);\n }\n }\n\n const where = (hits: { file: string; line: number }[]) => hits.slice(0, 5).map((h) => ({ file: h.file, line: h.line }));\n const pieces: MissingPiece[] = [];\n for (const v of variants.values()) {\n const values: PieceValue[] = [];\n for (const h of v.hits) {\n const pv = { category: h.finding.category, ...(h.finding.property ? { property: h.finding.property } : {}), value: h.finding.value };\n if (!values.some((x) => x.category === pv.category && x.property === pv.property && norm(x) === norm(pv))) values.push(pv);\n }\n const aspect = aspectOf(values);\n const bg = v.hits.find((h) => /background/.test(h.finding.property ?? ''))?.finding.suggestion;\n const nearest = nearestOf(bg ?? v.hits.find((h) => h.finding.suggestion)?.finding.suggestion);\n const shown = values.map((x) => `${x.property ?? x.category} ${x.value}`).join(', ');\n pieces.push({\n id: pieceId([v.component, v.name, aspect]),\n kind: 'variant',\n title: `${v.component}: ${v.name} ${aspect}`,\n summary: `${v.component} restyled by hand (${shown}).${nearest ? ` ${nearest.level === 'high' ? 'It looks a lot like' : 'The closest in the library is'} ${nearest.label}.` : ''}`,\n name: v.name,\n component: v.component,\n aspect,\n values,\n ...(nearest ? { nearest } : {}),\n where: where(v.hits),\n });\n }\n for (const [name, e] of foreign) {\n pieces.push({\n id: pieceId(['component', name]),\n kind: 'component',\n title: `New component: ${name}`,\n summary: `${name} comes from ${e.finding.library}, and the library has nothing like it.`,\n name,\n component: name.split('.').pop()!,\n values: [],\n where: where(e.hits),\n });\n }\n for (const t of tokens.values()) {\n const nearest = nearestOf(t.hits[0]!.finding.suggestion);\n const pname = t.name ?? `${t.category === 'color' ? 'custom' : t.category}`;\n const path = proposedTokenPath(nearest?.token, pname, t.category);\n const tokenFiles = nearest && input.set ? [...new Set(input.set.defs.filter((d) => d.path === nearest.token).map((d) => d.file))] : [];\n const n = t.hits.length;\n pieces.push({\n id: pieceId([t.category, t.name ?? t.value.replace(/^#/, '')]),\n kind: 'token',\n title: valueTitle(t.category, t.value),\n summary: `Written by hand in ${n === 1 ? 'one place' : `${n} places`}.${nearest ? ` ${nearest.level === 'high' ? 'It looks a lot like' : 'The closest in the library is'} ${nearest.label} (${nearest.value}).` : ' The library has nothing close.'}`,\n name: pname,\n values: [{ category: t.category, ...(t.hits[0]!.finding.property ? { property: t.hits[0]!.finding.property } : {}), value: t.value }],\n ...(nearest ? { nearest } : {}),\n where: where(t.hits),\n token: { path, type: TOKEN_TYPE[t.category] ?? 'dimension', value: t.value, files: tokenFiles },\n });\n }\n // Variants and components first (they read as features), then values; ids stay unique.\n const order: Record<MissingPiece['kind'], number> = { variant: 0, component: 1, token: 2 };\n const seen = new Set<string>();\n const out = pieces\n .sort((a, b) => order[a.kind] - order[b.kind])\n .map((p) => {\n let id = p.id;\n for (let i = 2; seen.has(id); i++) id = `${p.id}-${i}`;\n seen.add(id);\n return id === p.id ? p : { ...p, id };\n });\n return { pieces: out, fixes, files: files.length };\n}\n"],"names":["l","k","j","c","m","t","w","v","f","s","n","key","e","h","x","d","b","p"],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import type { LayeredTokenSet } from '../layered';
2
+ import { type MissingPiece } from '../prod-branch';
3
+ import type { DesignSystemIndex } from './ds-index';
4
+ export interface BranchFile {
5
+ /** Repo-relative path. */
6
+ path: string;
7
+ /** Text at the branch start, null when the branch adds the file. */
8
+ before: string | null;
9
+ /** Text at the branch head, null when the branch removes it. */
10
+ after: string | null;
11
+ }
12
+ export interface MissingPiecesResult {
13
+ pieces: MissingPiece[];
14
+ /** New values equal to a token and new non-library components the library has an equivalent for. */
15
+ fixes: {
16
+ file: string;
17
+ line: number;
18
+ what: string;
19
+ use: string;
20
+ }[];
21
+ /** Files read. */
22
+ files: number;
23
+ }
24
+ /** The JSX element whose opening tag holds `offset` (an inline style value), or null. */
25
+ export declare function enclosingTag(text: string, offset: number): {
26
+ name: string;
27
+ start: number;
28
+ } | null;
29
+ /** The plain text right after an opening tag (`<Badge ...>Overdue</Badge>` gives "Overdue"), when short. */
30
+ export declare function childText(text: string, tagStart: number): string | null;
31
+ /** The classes of the CSS rule a declaration at `offset` belongs to: `.badge.overdue { color: red }` gives ["badge", "overdue"]. */
32
+ export declare function ruleClasses(text: string, offset: number): string[];
33
+ /** "badgeOverdue" on Badge -> "overdue"; "invoice-status--late" -> "late". */
34
+ export declare function nameFromClass(cls: string, component?: string): string;
35
+ /**
36
+ * The pieces a product branch adds that the library lacks. `files` are the changed files (only auditable ones are
37
+ * read; pass the product's files). `set` lets token pieces name the token files of the closest token (light and dark).
38
+ */
39
+ export declare function missingPieces(input: {
40
+ files: BranchFile[];
41
+ ds: DesignSystemIndex;
42
+ set?: LayeredTokenSet | undefined;
43
+ }): MissingPiecesResult;