guard-my-design-system 1.0.2 → 1.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 +12 -2
- package/index.mjs +17 -0
- package/package.json +2 -2
- package/src/diff.mjs +1 -1
- package/src/judge.mjs +28 -5
package/README.md
CHANGED
|
@@ -8,6 +8,16 @@ A pull request guard that checks **only the lines a change adds**, says nothing
|
|
|
8
8
|
about the past, and points each new sin at the on-system value the author
|
|
9
9
|
probably meant. Old wiring is not its business; new sparks are.
|
|
10
10
|
|
|
11
|
+
It speaks in nearests, not scoldings:
|
|
12
|
+
|
|
13
|
+
> `Card.tsx:24` — new colour `#4a7be8`. Nearest token: `#3b6fe0`.
|
|
14
|
+
>
|
|
15
|
+
> `site.css:31` — new spacing value `13px`. Nearest existing value: `12px`.
|
|
16
|
+
>
|
|
17
|
+
> `site.css:33` — new typeface `Comic Sans MS`. First typeface declared in this codebase.
|
|
18
|
+
>
|
|
19
|
+
> `site.css:35` — `!important`. The cascade admitting defeat; raise specificity or fix the source order.
|
|
20
|
+
|
|
11
21
|
It learns your system by scanning your repo with the
|
|
12
22
|
[roast-my-design-system](https://github.com/gregkozakiewicz/roast-my-design-system)
|
|
13
23
|
engine, then judges the diff against what it learned. No config files, no rules
|
|
@@ -83,7 +93,7 @@ Judge your uncommitted work before anyone else sees it:
|
|
|
83
93
|
npx guard-my-design-system@latest
|
|
84
94
|
```
|
|
85
95
|
|
|
86
|
-
| Command | What you get |
|
|
96
|
+
| Command | What you get |
|
|
87
97
|
|---|---|
|
|
88
98
|
| <code>npx guard-my-design-system@latest</code> | Your working tree's added lines judged against main, in the terminal |
|
|
89
99
|
| <code>npx guard-my-design-system@latest <path></code> | Judge a different repo than the current directory |
|
|
@@ -138,6 +148,6 @@ by Greg Kozakiewicz*.
|
|
|
138
148
|
|
|
139
149
|
**guard-my-design-system**™ and the GK mark are trademarks of Greg Kozakiewicz.
|
|
140
150
|
Forking is welcome, republishing under this name is not: see
|
|
141
|
-
[brand and attribution](https://gregkozakiewicz.github.io/
|
|
151
|
+
[brand and attribution](https://gregkozakiewicz.github.io/guard-my-design-system/brand.html).
|
|
142
152
|
|
|
143
153
|
Built and designed by <a href="https://gregkozakiewicz.com"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/gregkozakiewicz/roast-my-design-system/main/assets/gk-mark-dark.png?v=3.10.1"><img src="https://raw.githubusercontent.com/gregkozakiewicz/roast-my-design-system/main/assets/gk-mark.png?v=3.10.1" height="15" alt="GK mark"></picture> Greg Kozakiewicz</a>.
|
package/index.mjs
CHANGED
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
* --strict, 2 could not run.
|
|
14
14
|
*/
|
|
15
15
|
import { resolve } from 'node:path';
|
|
16
|
+
import { readFileSync } from 'node:fs';
|
|
17
|
+
import { execFileSync } from 'node:child_process';
|
|
16
18
|
import { learnSystem } from 'roast-my-design-system/engine';
|
|
17
19
|
import { defaultBase, addedLines } from './src/diff.mjs';
|
|
18
20
|
import { judge } from './src/judge.mjs';
|
|
@@ -25,7 +27,22 @@ const opt = (name) => {
|
|
|
25
27
|
return i > -1 && argv[i + 1] ? argv[i + 1] : null;
|
|
26
28
|
};
|
|
27
29
|
|
|
30
|
+
if (flag('version') || argv.includes('-v')) {
|
|
31
|
+
const pkg = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8'));
|
|
32
|
+
console.log(`guard-my-design-system ${pkg.version}`);
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
|
|
28
36
|
const cwd = resolve(argv[0] && !argv[0].startsWith('--') ? argv[0] : '.');
|
|
37
|
+
|
|
38
|
+
// The guard judges diffs, so a git repository is its whole world. Say so
|
|
39
|
+
// calmly instead of letting git's own fatals leak through.
|
|
40
|
+
try {
|
|
41
|
+
execFileSync('git', ['rev-parse', '--is-inside-work-tree'], { cwd, stdio: 'ignore' });
|
|
42
|
+
} catch {
|
|
43
|
+
console.error('guard: this is not a git repository, and the guard judges diffs. Run it inside a repo.');
|
|
44
|
+
process.exit(2);
|
|
45
|
+
}
|
|
29
46
|
const base = opt('base') ?? defaultBase(cwd);
|
|
30
47
|
if (!base) {
|
|
31
48
|
console.error('guard: cannot find a base to diff against (no origin/main, origin/master, main or master). Pass one with --base <ref>.');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "guard-my-design-system",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.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": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"type": "git",
|
|
38
38
|
"url": "git+https://github.com/gregkozakiewicz/guard-my-design-system.git"
|
|
39
39
|
},
|
|
40
|
-
"homepage": "https://github.
|
|
40
|
+
"homepage": "https://gregkozakiewicz.github.io/guard-my-design-system/",
|
|
41
41
|
"bugs": {
|
|
42
42
|
"url": "https://github.com/gregkozakiewicz/guard-my-design-system/issues"
|
|
43
43
|
},
|
package/src/diff.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import { join } from 'node:path';
|
|
|
11
11
|
export function defaultBase(cwd) {
|
|
12
12
|
for (const ref of ['origin/main', 'origin/master', 'main', 'master']) {
|
|
13
13
|
try {
|
|
14
|
-
return execFileSync('git', ['merge-base', 'HEAD', ref], { cwd, encoding: 'utf8' }).trim();
|
|
14
|
+
return execFileSync('git', ['merge-base', 'HEAD', ref], { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
15
15
|
} catch { /* try the next spelling */ }
|
|
16
16
|
}
|
|
17
17
|
return null;
|
package/src/judge.mjs
CHANGED
|
@@ -13,8 +13,20 @@ import {
|
|
|
13
13
|
} from 'roast-my-design-system/engine';
|
|
14
14
|
|
|
15
15
|
// Same honesty exemptions the engine applies: email and print styling must be
|
|
16
|
-
// inline,
|
|
17
|
-
|
|
16
|
+
// inline, always. Artwork-named files (Icon, Logo, Badge…) are exempt only
|
|
17
|
+
// when their added lines actually draw SVG — a Badge component that is plain
|
|
18
|
+
// styled UI gets guarded like everything else.
|
|
19
|
+
const EMAIL_PRINT_RE = /email|(^|[/.])print([/.]|$)/i;
|
|
20
|
+
const ARTWORK_NAME_RE = /(^|\/)[\w.-]*(icon|logo|badge|illustration|artwork)[\w.-]*\.(tsx|jsx)$/i;
|
|
21
|
+
const SVG_MARKUP_RE = /<(svg|path|rect|circle|ellipse|polygon|defs|mask)\b/i;
|
|
22
|
+
|
|
23
|
+
function exemptFiles(added) {
|
|
24
|
+
const svgish = new Set();
|
|
25
|
+
for (const { file, text } of added) {
|
|
26
|
+
if (ARTWORK_NAME_RE.test(file) && SVG_MARKUP_RE.test(text)) svgish.add(file);
|
|
27
|
+
}
|
|
28
|
+
return (file) => EMAIL_PRINT_RE.test(file) || svgish.has(file);
|
|
29
|
+
}
|
|
18
30
|
|
|
19
31
|
const FONT_LINE_RE = /font-family\s*:\s*([^;{}]+)/i;
|
|
20
32
|
|
|
@@ -29,9 +41,10 @@ export function judge(added, system) {
|
|
|
29
41
|
// The system was learned from the tree that already CONTAINS these added
|
|
30
42
|
// lines, so a new value would vouch for itself. A value is only "known"
|
|
31
43
|
// if the repo uses it more times than this change added it.
|
|
44
|
+
const exempt = exemptFiles(added);
|
|
32
45
|
const addedLengths = new Map(), addedFaces = new Map();
|
|
33
46
|
for (const { file, line, text } of added) {
|
|
34
|
-
if (
|
|
47
|
+
if (exempt(file)) continue;
|
|
35
48
|
const css = isStyleFile(file);
|
|
36
49
|
if (!css && !isCodeFile(file)) continue;
|
|
37
50
|
for (const s of extractStyling(text, { css }).spacing) {
|
|
@@ -55,7 +68,7 @@ export function judge(added, system) {
|
|
|
55
68
|
const findings = [];
|
|
56
69
|
|
|
57
70
|
for (const { file, line, text } of added) {
|
|
58
|
-
if (
|
|
71
|
+
if (exempt(file)) continue;
|
|
59
72
|
const css = isStyleFile(file);
|
|
60
73
|
if (!css && !isCodeFile(file)) continue;
|
|
61
74
|
|
|
@@ -111,5 +124,15 @@ export function judge(added, system) {
|
|
|
111
124
|
}
|
|
112
125
|
}
|
|
113
126
|
|
|
114
|
-
|
|
127
|
+
// Two extractors can see the same value on the same line (a hex inside a
|
|
128
|
+
// Tailwind class is also a hex in the raw sweep). One sin, one line.
|
|
129
|
+
const seen = new Set();
|
|
130
|
+
const deduped = findings.filter((f) => {
|
|
131
|
+
const key = `${f.file}|${f.line}|${f.kind}|${f.value}`;
|
|
132
|
+
if (seen.has(key)) return false;
|
|
133
|
+
seen.add(key);
|
|
134
|
+
return true;
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
return deduped.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line);
|
|
115
138
|
}
|