guard-my-design-system 1.7.0 → 1.8.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.
package/README.md CHANGED
@@ -59,8 +59,18 @@ it updates that same comment. It never adds more comments:
59
59
  - **A palette colour where a theme variable exists.** On a shadcn repo whose
60
60
  theme file holds the variables, `text-slate-500` in the app's own code is
61
61
  flagged and the theme file named. Off on utility-class installs.
62
-
63
- It reads the repo the way the roast report does. On a shadcn repo the
62
+ - **A colour or a pixel size written onto a kit component.** On a product
63
+ built on MUI, Mantine, Chakra UI or Ant Design, `color: '#667085'` in an
64
+ `sx` prop or a style object is flagged and the finding says whether the
65
+ theme already holds that colour, or tells you to add it there once. A
66
+ pixel size such as `p: '12px'` is turned into the theme's spacing step. The
67
+ advice is in the kit's own words: `sx` paths on MUI, props on Mantine,
68
+ style props on Chakra, the theme config on Ant Design.
69
+
70
+ It reads the repo the way the roast report does. On a kit repo the theme's
71
+ colours are the token set, and a colour the theme already holds is flagged
72
+ on a kit component all the same: writing it by hand is the exact mistake the
73
+ check exists for. On a shadcn repo the
64
74
  installed catalogue, installed registries and kit blocks are not judged:
65
75
  `shadcn add` is not a sin. On a repo that publishes a shadcn registry only
66
76
  the published folders are judged. `!important` in an embedded widget's
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guard-my-design-system",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "Your design system dies one pull request at a time. This makes sure it doesn't. A guard that judges only the lines a change adds, against the system the repo already has, and names the on-system value the author probably meant.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,7 +11,7 @@
11
11
  "src/"
12
12
  ],
13
13
  "dependencies": {
14
- "roast-my-design-system": "8.4.5"
14
+ "roast-my-design-system": "8.4.6"
15
15
  },
16
16
  "keywords": [
17
17
  "design-system",
package/src/judge.mjs CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  isCodeFile, isStyleFile, typefaceOf, GENERIC_FONTS,
13
13
  definedComponents, exemptReason,
14
14
  EXTRA_KINDS, extraValue, fontDeclarations,
15
- WIDGET_CSS_RE, isLibraryClass, PALETTE_CLASS_RE, blankComments,
15
+ WIDGET_CSS_RE, isLibraryClass, PALETTE_CLASS_RE, blankComments, kitPaintFindings,
16
16
  } from 'roast-my-design-system/engine';
17
17
 
18
18
  // Folder membership, the way the engine's own splits do it.
@@ -185,6 +185,35 @@ export function judge(added, system, { readFile } = {}) {
185
185
  }
186
186
  }
187
187
 
188
+ // A product built on a kit (MUI, Mantine, Chakra UI, Ant Design), roast
189
+ // 8.4.6: a colour or a pixel size written onto a kit component where the
190
+ // theme has a value. The engine judges the whole file (the import that makes
191
+ // it a kit file sits at the top, where the diff never looks) and the guard
192
+ // keeps the hits on added lines. On a kit file the kit rule owns colours
193
+ // and the pixel sizes it named, so the generic rules stay quiet about the
194
+ // same value: one line, one finding, the same words as the roast report's
195
+ // live checks.
196
+ const kit = prof.kit ?? null;
197
+ const kitJudged = new Map();
198
+ const kitLines = (file) => {
199
+ if (!kit) return null;
200
+ if (!kitJudged.has(file)) {
201
+ const w = wholeText(file);
202
+ const j = w == null ? null : kitPaintFindings(w, kit, { file });
203
+ if (!j || j.exempt) kitJudged.set(file, null);
204
+ else {
205
+ const byLine = new Map();
206
+ for (const f of j.findings) {
207
+ const ln = w.slice(0, f.index).split('\n').length;
208
+ if (!byLine.has(ln)) byLine.set(ln, []);
209
+ byLine.get(ln).push(f);
210
+ }
211
+ kitJudged.set(file, byLine);
212
+ }
213
+ }
214
+ return kitJudged.get(file);
215
+ };
216
+
188
217
  const findings = [];
189
218
 
190
219
  for (const { file, line, text } of added) {
@@ -194,7 +223,18 @@ export function judge(added, system, { readFile } = {}) {
194
223
 
195
224
  const seen = extractStyling(text, { css });
196
225
 
226
+ const onKit = css ? null : kitLines(file);
227
+ const kitPx = new Set();
228
+ for (const f of onKit?.get(line) ?? []) {
229
+ if (f.rule === 'kit-px') kitPx.add(f.value.split(': ')[1]);
230
+ findings.push({
231
+ file, line, kind: f.rule, value: f.value, label: f.label,
232
+ advice: f.note ? `${f.note}. ${f.fix.replace(/\.$/, '')}` : f.fix.replace(/\.$/, ''),
233
+ });
234
+ }
235
+
197
236
  for (const c of seen.colors) {
237
+ if (onKit) break; // the kit rule owns colours on a kit file
198
238
  if (tokenSet.has(c.value)) continue; // disciplined token use
199
239
  const near = c.value.startsWith('#') ? nearestColor(c.value, system.tokens) : null;
200
240
  findings.push({
@@ -208,6 +248,7 @@ export function judge(added, system, { readFile } = {}) {
208
248
  }
209
249
 
210
250
  for (const s of seen.spacing) {
251
+ if (kitPx.has(s.value)) continue; // the kit rule said it
211
252
  if (knownLengths.has(s.value)) continue; // the codebase already uses it
212
253
  const near = nearestLength(s.value, [...knownLengths]);
213
254
  findings.push({
package/src/report.mjs CHANGED
@@ -16,7 +16,11 @@ const KIND_LABEL = {
16
16
  inline: 'inline style block',
17
17
  component: 'second definition of',
18
18
  palette: 'palette colour where a theme variable exists',
19
+ // on a kit repo the engine words the label with the kit's name (f.label)
20
+ 'kit-colour': 'colour written onto a kit component',
21
+ 'kit-px': 'pixel size on a kit component',
19
22
  };
23
+ const labelOf = (f) => f.label ?? KIND_LABEL[f.kind];
20
24
 
21
25
  // Kinds whose label already says everything; printing the value repeats it.
22
26
  const VALUELESS = new Set(['important', 'inline']);
@@ -29,7 +33,7 @@ export function terminalReport(findings) {
29
33
  }
30
34
  const lines = [`guard-my-design-system: ${findings.length} new issue${findings.length === 1 ? '' : 's'} in this change\n`];
31
35
  for (const f of findings) {
32
- lines.push(` ${f.file}:${f.line} · ${KIND_LABEL[f.kind]} ${VALUELESS.has(f.kind) ? '' : f.value}`.trimEnd() + `. ${capitalise(f.advice)}.`);
36
+ lines.push(` ${f.file}:${f.line} · ${labelOf(f)} ${VALUELESS.has(f.kind) ? '' : f.value}`.trimEnd() + `. ${capitalise(f.advice)}.`);
33
37
  }
34
38
  lines.push('');
35
39
  lines.push(' Only lines added in this change were counted. The existing codebase was not judged.');
@@ -49,7 +53,7 @@ export function markdownReport(findings) {
49
53
  }
50
54
  const out = [`**🛡 guard-my-design-system: ${findings.length} new issue${findings.length === 1 ? '' : 's'} in this pull request**`, ''];
51
55
  for (const f of findings) {
52
- out.push(`- \`${f.file}:${f.line}\` · ${KIND_LABEL[f.kind]} ${VALUELESS.has(f.kind) ? '' : `\`${f.value}\``}`.trimEnd() + `. ${capitalise(f.advice)}.`);
56
+ out.push(`- \`${f.file}:${f.line}\` · ${labelOf(f)} ${VALUELESS.has(f.kind) ? '' : `\`${f.value}\``}`.trimEnd() + `. ${capitalise(f.advice)}.`);
53
57
  }
54
58
  out.push('');
55
59
  out.push(`<sub>Only added lines are checked; the existing codebase is never judged. ${FOOTER}</sub>`);