guard-my-design-system 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -1
- package/package.json +2 -2
- package/src/judge.mjs +26 -3
- package/src/report.mjs +4 -1
package/README.md
CHANGED
|
@@ -40,7 +40,9 @@ it updates that same comment. It never adds more comments:
|
|
|
40
40
|
## What it catches
|
|
41
41
|
|
|
42
42
|
- **A hard-coded colour where a token exists.** The finding names the token:
|
|
43
|
-
`var(--blue-500)`, not just a hex code.
|
|
43
|
+
`var(--blue-500)`, not just a hex code. A token's own value pasted into a
|
|
44
|
+
component or a stylesheet counts too: the finding says use the name, not
|
|
45
|
+
the value. This works across colour notations:
|
|
44
46
|
a hex stray is matched to an hsl or oklch token, including shadcn's
|
|
45
47
|
bare-triplet variables. Dark-theme values count as the system too, so a
|
|
46
48
|
stray in a dark block snaps to the dark token, never its light twin.
|
|
@@ -54,6 +56,11 @@ it updates that same comment. It never adds more comments:
|
|
|
54
56
|
- **An inline `style={{ }}` block.** Styling written there is invisible to the
|
|
55
57
|
system and to every agent that reads the file. Blocks built from variables
|
|
56
58
|
are decided elsewhere, so they are left alone.
|
|
59
|
+
- **A button built from scratch where the repo already has a Button.** A
|
|
60
|
+
styled button, or a button tag dressed as one, in a file whose package
|
|
61
|
+
can import a Button that at least 20 files already use. The finding
|
|
62
|
+
gives the import line. Rows, tabs, close crosses and select triggers
|
|
63
|
+
built on a button tag are left alone.
|
|
57
64
|
- **A second definition of a component you already have.** The finding names
|
|
58
65
|
the file that already defines it, and how many places use that one.
|
|
59
66
|
- **A new import of a duplicate component.** When a name is defined in more
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "guard-my-design-system",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.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": "9.
|
|
14
|
+
"roast-my-design-system": "9.2.0"
|
|
15
15
|
},
|
|
16
16
|
"keywords": [
|
|
17
17
|
"design-system",
|
package/src/judge.mjs
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
definedComponents, exemptReason,
|
|
14
14
|
EXTRA_KINDS, extraValue, fontDeclarations,
|
|
15
15
|
WIDGET_CSS_RE, isLibraryClass, PALETTE_CLASS_RE, blankComments, kitPaintFindings,
|
|
16
|
-
tokenTwinFindings, avoidedImportFindings, isChartFile, chartFindings,
|
|
16
|
+
tokenTwinFindings, avoidedImportFindings, isChartFile, chartFindings, handmadeButtonFindings,
|
|
17
17
|
} from 'roast-my-design-system/engine';
|
|
18
18
|
|
|
19
19
|
// Folder membership, the way the engine's own splits do it.
|
|
@@ -163,6 +163,9 @@ export function judge(added, system, { readFile, readBase } = {}) {
|
|
|
163
163
|
}
|
|
164
164
|
// "use var(--blue-500)", not "go hunt this hex": name a value when the
|
|
165
165
|
// system defines it as a custom property.
|
|
166
|
+
const statesTokens = (file) => (system.tokenSources ?? []).some((t) => samePath(t, file));
|
|
167
|
+
// on a kit repo the theme is where a colour is decided (mcp/knowledge reads it the same way)
|
|
168
|
+
const themeFile = system.profile?.kit?.themeFiles?.[0] ?? system.tokenFile ?? null;
|
|
166
169
|
const named = (value) => {
|
|
167
170
|
// shadcn-style tokens are defined as bare triplets (--primary: 222.2 47.4%
|
|
168
171
|
// 11.2%) but normalised to hsl(...); try the unwrapped form too.
|
|
@@ -259,7 +262,21 @@ export function judge(added, system, { readFile, readBase } = {}) {
|
|
|
259
262
|
|
|
260
263
|
for (const c of seen.colors) {
|
|
261
264
|
if (onKit || chart) break; // the kit rule or the chart rule owns colours here
|
|
262
|
-
if (tokenSet.has(c.value))
|
|
265
|
+
if (tokenSet.has(c.value)) {
|
|
266
|
+
// A token's raw value is the definition only inside a file that
|
|
267
|
+
// states the palette (system.tokenSources, roast 9.1.3). Anywhere
|
|
268
|
+
// else it is the value pasted where the name belongs: the system
|
|
269
|
+
// cannot see it, and the next reader copies the hex.
|
|
270
|
+
if (!system.tokenSources || statesTokens(file)) continue;
|
|
271
|
+
const n = named(c.value);
|
|
272
|
+
findings.push({
|
|
273
|
+
file, line, kind: 'color', value: c.value,
|
|
274
|
+
advice: n !== c.value
|
|
275
|
+
? `this is already the token ${n}; use the name, not the value`
|
|
276
|
+
: `the theme already holds this value${themeFile ? ` (${themeFile})` : ''}; read it from there rather than pasting it`,
|
|
277
|
+
});
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
263
280
|
const near = c.value.startsWith('#') ? nearestColor(c.value, system.tokens) : null;
|
|
264
281
|
findings.push({
|
|
265
282
|
file, line, kind: 'color', value: c.value,
|
|
@@ -414,7 +431,13 @@ export function judge(added, system, { readFile, readBase } = {}) {
|
|
|
414
431
|
others: system.tokenDefs.filter((d) => !samePath(d.file, file)),
|
|
415
432
|
tailwind: prof.kind === 'tailwind' || /@theme\b/.test(w),
|
|
416
433
|
}) : [])
|
|
417
|
-
:
|
|
434
|
+
: [
|
|
435
|
+
...(system.duplicates ? avoidedImportFindings(w, { file, before: baseText(file), dupes: system.duplicates }) : []),
|
|
436
|
+
// a button built from scratch where the file's own package can
|
|
437
|
+
// import the repo's Button (roast 9.2.0); a warning in the engine,
|
|
438
|
+
// a finding here, on the line the button starts
|
|
439
|
+
...(system.buttons?.length ? handmadeButtonFindings(w, { file }, system) : []),
|
|
440
|
+
];
|
|
418
441
|
for (const f of hits) {
|
|
419
442
|
const line = lineAt(w, f.index);
|
|
420
443
|
if (!lines.has(line)) continue;
|
package/src/report.mjs
CHANGED
|
@@ -26,11 +26,14 @@ const KIND_LABEL = {
|
|
|
26
26
|
// painting by hand where the repo keeps none
|
|
27
27
|
'chart-colour': 'chart colour written by hand',
|
|
28
28
|
'chart-palette': 'chart painted by hand',
|
|
29
|
+
// a styled.button or a dressed button tag where the repo has a Button
|
|
30
|
+
// (roast 9.2.0); the engine's sentence names the Button and its import
|
|
31
|
+
'handmade-button': 'button built from scratch',
|
|
29
32
|
};
|
|
30
33
|
const labelOf = (f) => f.label ?? KIND_LABEL[f.kind];
|
|
31
34
|
|
|
32
35
|
// Kinds whose label already says everything; printing the value repeats it.
|
|
33
|
-
const VALUELESS = new Set(['important', 'inline', 'twin-token', 'avoided-copy', 'chart-palette']);
|
|
36
|
+
const VALUELESS = new Set(['important', 'inline', 'twin-token', 'avoided-copy', 'chart-palette', 'handmade-button']);
|
|
34
37
|
|
|
35
38
|
const FOOTER = 'Full picture of the whole codebase: `npx roast-my-design-system`';
|
|
36
39
|
|