roast-my-design-system 10.1.2 → 10.1.4
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 +4 -4
- package/package.json +1 -1
- package/skills/roast-my-design-system/scripts/diagnose/index.mjs +11 -8
- package/skills/roast-my-design-system/scripts/harvest/coloruse.mjs +4 -3
- package/skills/roast-my-design-system/scripts/harvest/paint.mjs +8 -7
- package/skills/roast-my-design-system/scripts/harvest/walk.mjs +17 -24
- package/skills/roast-my-design-system/scripts/lib/demo.mjs +57 -0
- package/skills/roast-my-design-system/scripts/lib/guard-api.mjs +12 -5
- package/skills/roast-my-design-system/scripts/lib/links.mjs +9 -0
- package/skills/roast-my-design-system/scripts/lib/palette.mjs +3 -2
- package/skills/roast-my-design-system/scripts/lib/version.mjs +1 -1
- package/skills/roast-my-design-system/scripts/rules/build.mjs +2 -1
package/README.md
CHANGED
|
@@ -142,20 +142,20 @@ Eleven reports, hosted exactly as the tool writes them. Every number determinist
|
|
|
142
142
|
- **[dubinc/dub](https://gregkozakiewicz.github.io/roast-my-design-system/examples/dubinc-dub.html)**: 642 arbitrary bracket values, 21 duplicated components, and the chart-palette gap named. Score 20.
|
|
143
143
|
- **[telekom/scale](https://gregkozakiewicz.github.io/roast-my-design-system/examples/telekom-scale.html)** (Stencil): 95 Stencil components read by tag; 66 spacing values outside the scale where about 12 would do; Claude's notes embedded. Score 55.
|
|
144
144
|
- **[magicuidesign/magicui](https://gregkozakiewicz.github.io/roast-my-design-system/examples/magicui.html)** (registry): counted on the components it publishes, 52 off-theme colours per 100 files in the code it ships, its docs site kept out and named. Score 78.
|
|
145
|
-
- **[adobe/spectrum-web-components](https://gregkozakiewicz.github.io/roast-my-design-system/examples/adobe-spectrum.html)** (Lit): hardcoded colours
|
|
145
|
+
- **[adobe/spectrum-web-components](https://gregkozakiewicz.github.io/roast-my-design-system/examples/adobe-spectrum.html)** (Lit): 740 colour tokens with 8 hardcoded colours beside them, and 37 !important declarations. Score 83.
|
|
146
146
|
|
|
147
147
|
The full report for vercel/ai-chatbot. The verdict answers what the agent will learn here, the gap section comes first, then "What the repo teaches the agent", Claude's read of the scan:
|
|
148
148
|
|
|
149
|
-

|
|
150
150
|
|
|
151
151
|
The same report in light mode (one file, built-in toggle):
|
|
152
152
|
|
|
153
|
-

|
|
154
154
|
|
|
155
155
|
## Why the numbers hold
|
|
156
156
|
|
|
157
157
|
- **Deterministic.** A zero-dependency Node script reads every file and returns the same numbers every run. About a second on a normal repo.
|
|
158
|
-
- **Read-only. No network. No telemetry.** The test suite fails if package.json ever declares a dependency.
|
|
158
|
+
- **Read-only. No network. No telemetry.** The test suite fails if package.json ever declares a dependency. Every release runs 461 checks against 29 sample repos before it is tagged; the fixtures and their snapshots live in `tests/`, one JSON file each.
|
|
159
159
|
- **Honest gaps.** What the scan cannot read says "not measured" and drops out of the score.
|
|
160
160
|
- **Honest exclusions.** Tests, stories, docs sites, artwork and email templates are left out. Your own exclusions are printed in the report header with file counts.
|
|
161
161
|
- **A real benchmark.** 119 public repos. A core fleet of 34 React repos sets the medians; the rest feed the kit and web-component groups, so a shadcn repo is compared with shadcn repos and a Lit system with Lit and Stencil systems. The builder is in `tools/benchmark/`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "roast-my-design-system",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.4",
|
|
4
4
|
"mcpName": "io.github.gregkozakiewicz/roast-my-design-system",
|
|
5
5
|
"description": "Your AI can write the UI. This makes sure it writes your UI. A deterministic scanner scores your design system 0-100 against 119 public repos, reads React and web components (Stencil, Lit), hands you a copy-paste fix prompt for each top finding, writes rules for Claude, Cursor, Copilot and Windsurf with --apply, and runs as a local MCP server with --mcp.",
|
|
6
6
|
"keywords": [
|
|
@@ -32,6 +32,7 @@ import { fixPrompt } from '../lib/fixprompt.mjs';
|
|
|
32
32
|
import { WHY } from './why.mjs';
|
|
33
33
|
import { parseColor, luminance, isGrey } from '../lib/color.mjs';
|
|
34
34
|
import { loadBenchmark, benchHelpers, makeHealthOf, coreMetrics, tileHealths, scoreOfTiles, scoreBreakdown, scorePackage as scorePackageOf, ZERO_IDEAL, WARN_TOLERANCE, SCORE_OF, SCHEMA_VERSION, benchKind } from './score.mjs';
|
|
35
|
+
import { REPO_URL, AUTHOR_URL } from '../lib/links.mjs';
|
|
35
36
|
import { ownSpacing, profileOf, installedOf, splitArbitrary, installedByOwner } from '../profiles/index.mjs';
|
|
36
37
|
import { KITS } from '../profiles/kit-common.mjs';
|
|
37
38
|
import { rulesOn as lintRulesOn, describeRule as lintDescribe, entryMatcher as lintMatcher, RULE_TILE as LINT_TILE } from '../profiles/shadcn-lint.mjs';
|
|
@@ -67,10 +68,13 @@ const benchGroup = B.sliceInfo ? { name: GROUP_NAMES[B.sliceInfo.kind] ?? B.slic
|
|
|
67
68
|
const healthOf = makeHealthOf(B);
|
|
68
69
|
const M = coreMetrics(h);
|
|
69
70
|
|
|
70
|
-
// The GK mark (
|
|
71
|
-
// as a CSS mask filled with currentColor so it works in both themes.
|
|
71
|
+
// The GK mark (also gregkozakiewicz.com), in the footer beside the author's
|
|
72
|
+
// name as a CSS mask filled with currentColor so it works in both themes.
|
|
72
73
|
const GK_MASK = 'data:image/png;base64,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'; // alpha version of the mark, for CSS mask
|
|
73
|
-
|
|
74
|
+
// The product icon (assets/roastmds.svg) for the browser tab, and the same
|
|
75
|
+
// drawing as a 180px PNG for a phone home screen, which takes no SVG.
|
|
76
|
+
const ROAST_ICON = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgdmlld0JveD0iMCAwIDEwMCAxMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3Qgd2lkdGg9IjEwMCIgaGVpZ2h0PSIxMDAiIHJ4PSIxMiIgZmlsbD0iI0Y2RUZFNSIvPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF84Ml8zMSkiPjxwYXRoIGQ9Ik0zLjczODI4IDIwLjU2MDdWMTEuMjE0OUMzLjczODI4IDcuMDg0MDggNy4wODQwOCAzLjczODI4IDExLjIxNDkgMy43MzgyOEgyMC41NjA3IiBzdHJva2U9IiM2NTUxOTIiIHN0cm9rZS13aWR0aD0iNy40NzY2NCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtbGluZWNhcD0ic3F1YXJlIi8+PHBhdGggZD0iTTIwLjU2MDcgOTYuMjYxNkgxMS4yMTQ5QzcuMDg0MDggOTYuMjYxNiAzLjczODI4IDkyLjkxNTggMy43MzgyOCA4OC43ODVWNzkuNDM5MiIgc3Ryb2tlPSIjNjU1MTkyIiBzdHJva2Utd2lkdGg9IjcuNDc2NjQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPjxwYXRoIGQ9Ik05Ni4yNjE5IDc5LjQzOTJWODguNzg1Qzk2LjI2MTkgOTIuOTE1OCA5Mi45MTYxIDk2LjI2MTYgODguNzg1MiA5Ni4yNjE2SDc5LjQzOTUiIHN0cm9rZT0iIzY1NTE5MiIgc3Ryb2tlLXdpZHRoPSI3LjQ3NjY0IiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiLz48cGF0aCBkPSJNNzkuNDM5NSAzLjczODI4SDg4Ljc4NTJDOTIuOTE2MSAzLjczODI4IDk2LjI2MTkgNy4wODQwOCA5Ni4yNjE5IDExLjIxNDlWMjAuNTYwNyIgc3Ryb2tlPSIjNjU1MTkyIiBzdHJva2Utd2lkdGg9IjcuNDc2NjQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPjxwYXRoIGQ9Ik0zOS41NDY1IDI3LjIwMzZIMjMuMDYzNkMyMC44MTkzIDI3LjIwMzYgMTkgMjkuMDIyOSAxOSAzMS4yNjcyVjQ3Ljc1MDFDMTkgNDkuOTk0NCAyMC44MTkzIDUxLjgxMzcgMjMuMDYzNiA1MS44MTM3SDM5LjU0NjVDNDEuNzkwNyA1MS44MTM3IDQzLjYxMDEgNDkuOTk0NCA0My42MTAxIDQ3Ljc1MDFWMzEuMjY3MkM0My42MTAxIDI5LjAyMjkgNDEuNzkwNyAyNy4yMDM2IDM5LjU0NjUgMjcuMjAzNloiIGZpbGw9IiNGMjMxNzAiLz48cGF0aCBkPSJNNjYuMDIxMiAyOC4xNTMzTDU0LjYzODEgNDcuODY2OEM1My40ODUgNDkuODY4MSA1NC45Mjc2IDUyLjM2NzIgNTcuMjMzNyA1Mi4zNjcySDc5Ljk5OTlDODIuMzA2IDUyLjM2NzIgODMuNzQ4NSA0OS44NjgxIDgyLjU5NTUgNDcuODY2OEw3MS4yMTI0IDI4LjE1MzNDNzAuMDU5MyAyNi4xNTIgNjcuMTc0MiAyNi4xNTIgNjYuMDIxMiAyOC4xNTMzWiIgZmlsbD0iIzNCQURDNiIvPjxwYXRoIGQ9Ik0zMC40OTQxIDY3LjY2NjdDNDMuMTE2NiA3NS4yNDAyIDU4Ljg4ODQgNzUuMjQwMiA3MS41MTA5IDY3LjY2NjciIHN0cm9rZT0iIzY1NTE5MiIgc3Ryb2tlLXdpZHRoPSI5LjExMjU4IiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz48L2c+PGRlZnM+PGNsaXBQYXRoIGlkPSJjbGlwMF84Ml8zMSI+PHJlY3Qgd2lkdGg9IjEwMCIgaGVpZ2h0PSIxMDAiIGZpbGw9IndoaXRlIi8+PC9jbGlwUGF0aD48L2RlZnM+PC9zdmc+';
|
|
77
|
+
const ROAST_TOUCH_ICON = 'data:image/png;base64,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';
|
|
74
78
|
|
|
75
79
|
// Report identity. The scan id is a stable digest of this scan's shape, salted
|
|
76
80
|
// with the report namespace, so the same repo scanned twice reads the same and
|
|
@@ -1970,9 +1974,8 @@ const html = `<!doctype html>
|
|
|
1970
1974
|
<title>Design-system diagnosis:${nsMark} ${esc(repoName)}</title>
|
|
1971
1975
|
${ogTags()}
|
|
1972
1976
|
<meta name="author" content="Greg Kozakiewicz · gregkozakiewicz.com">
|
|
1973
|
-
<link rel="icon" type="image/
|
|
1974
|
-
<link rel="icon" type="image/png" sizes="192x192" href="data:image/png;base64,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">
|
|
1975
|
-
<link rel="apple-touch-icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAIAAACyr5FlAAAACXBIWXMAAAAAAAAAAQCEeRdzAAAQAElEQVR4nO1de2yN9///dDOxISipS4WZ1GUps2xqWZkwFrHJVqSmS2rENTUsY6STyS4WYTK3bDOpITbEgrhMbCXrlglDGI1rXFMt0VajLqHnrL/X73nleX8/nuf0tK6nPX2//nhyejz99Djv1/O+fd7v98cYhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCgUCoVCoVAoahpiYmLq1KmD65NPPlm3bl1cn3rqqfr160f6cymqBxo2bNi/f/82bdrwR1Aksp9HUV3wzDPPxMbG/vXXXytWrGjQoIG8DxUSwU+lqBZ44oknBg4cmJ+fX15ePnHiRL4JywJDE9kPpogYwAm+AAnWrVt39epVkOPs2bMjR47Em/BC5AZFrQPEzxcdO3Y8deoUmFFYWIjr0aNHe/bsyRtUedRSQDFA9vXq1fvggw/y8vJAi1u3bl27du327dt//PHHCy+8YBylovyojYDUwY+4uLhdu3YFAgGQIxgMlpSUlJWV4fXq1asbN24c6c+oiBwQj3Tq1Km0tBRs+O+//0COGzduwPm4c+fOlStX5syZ8/TTTxuHRpH+pIrHBTEWDRs2nDt3LlVF0EXAAd4BUd577z0EurbrGtEPrnj0gIyZ5mrTps3p06f/cyDk4GtcwY+CgoIePXogrOUvavwS/YA1oZjffvtt8TY8wPu44p/279+PcAY308QoohxMfTZp0uTnn3/2qA0PP5j82LBhQ9u2bY2TGYv0Z1c8SsCmUMZJSUlwRakh7KtNDkS29EjgmnArDsSCClH7EoVg+ArUqVMnIyMDUg/4YPMDN4Af169fLyoqyszM5J6tbthGJ0RtJCQkwFjQ26iIGSRHSUkJwlq8OHXq1DvvvGPULY0+MHyFXOvVq4cfhw0bdvv2bbABViOM5kA0S+2C64ULF0aMGGGcXVxJvSuiAUxRUKhwRZcuXQp5w17cunUrDDmoPG7evInrpk2bGLZ4sh2aYq/xYIRCtdG3b98TJ04wzUX9URE5SAvELHj/888/N3cXeUhNkBqaGg/W/+HF+PHjQYs7d+5AbVRkVhDfMpSFzwF+nDx5sn///sYiBwnB3bsI/qcUDwe0KS1btly7di3VBvgRPloBReiNZmVlwRgZy6aQJVL2oRSpwajjAC+GDBly9uxZehJ+teEPZWF3cFt6errxmQ/ww+9wKEtqHqQ4dOHChWDAbQdlDsKQg3YnJyfnxRdfNG75j4hfEqZ0ZYzWB9VQ0HmEjPfu3Ss2pVJyXLt2DdcZM2ZwY0VUhTAAYW2jRo1ADr6jnmkNxuTJk7ldQptSFc1RUFDw2muvGXe7LsaCcbbupk6d+txzzxlHf2jBek0FLMvGjRvBjDsOQpLD3lgpdwBXtHnz5sbdlRVmQGfgx/nz52OF2bNnG4c9jRs3VrNS84CHvnv37qdPn6ZNKbPgIUfASZsyVAFGjx5N38KTFQUVwLbs7GwseOnSpUmTJplQka2myKo1RDZffvmlVARWFKfA3MhOLF6fOnWKWVGYDI8/Aa6kpKRcvHgRv4X7c3Nz33rrLflzuNljYnS7vzqC0oLC//3337lREpIZJAdCGHACRoc25fvvv4e/aSoQ7apVq3AzIxrcDFcXysncnTmVXJlRd7VaQVQ6nvIBAwYw14krZO/ZjPUAFMGdUDPMioYMUNu0aQNflQqGGREAQW/r1q2NlY3la5okdVerEdg4bxzp/vjjj3i+YTK40RpSeVAB4AplgBf//PNPs2bNjFUgaDsQGRkZ3HlhGl4c2EWLFvEGj4+iaqMaQVQ60KpVqwsXLkDqbDsQn8NDEZKDegXXTz/9lAbCbrrnmk2aNNm6dSsVBq7Xr19nQTJYUlxcPGHCBN5sB7fkijqn1QIQBp94SGjUqFFUBndchI9WcOf58+dfeuklbp2IREUZJCcnwxWlb8sFsT4owhd5eXlpaWniZ9g6Q8lRLSD9By1atNixYwc1P5wJDzNsctAhpQ7YsmULn3sRLUe7GKfbJTMzkwvedEA3FgYL77BGBDFzjx49jGXaFNUF9gPaq1evy5cv02qEhE0OeiRFRUXjx483FezIP/fcc/v27aPCoPKgzgg4W/ziz27btg26x/iUhyLyoFBjY2PhOoiHUUVyICjt1KmTP51FK9OvXz9aH+oYuh2iOcAMaA76qsuXL4+LizM+51QRMTCmoDzat29/+PBhiq1S0FeFdCXisPnB0BRGKisrK+g2xkkiVQC2ccuXPPvhhx9ghtjQoBSJPEgO+AcQxpAhQzhVoSrkoDk4e/bssGHDjLUHS6PQqFEjvNO1a9fc3Fx/B6X9DlejC1xYWMjMOleL8FdTmyG7YnzK4+Pj8ZQzdWHvtVZEDooTMWrTpk2Nk96QBWU/dvTo0SHbJ/3kYL6EIbGxCkoUkQHlh2eU1TcIOLnTBiGFIQc9D3qUsAXTpk0zjiyfeeYZPu5ghjRe//LLLyxb9zPDJodk0vbv3w+P2KjbUR3AuBHixHP/ySefML3t74YNWP2P3HBh6LFz585u3boZq7jLOHKlKho0aFBJSUnAiWBlBb6w/wTJAQJhwQULFoBYalCqBSTDnZiY+Ntvv7GxIKQhsJ/7oFPXg9tYmcHNNoFkSGfNmsWqUr/6sRekFcObZ86ceffdd42Wqlcf8KFPTU0tKChgEiIMM4JO4osOR35+Pl1Rpr8YuMK4UG3ASP39998h93U9C7LMDHfCBiFcMi4ttLYj8mDP47fffkvngLvqYcghccratWs5aoF+Br2E+vXr01TBF2HKK2Q5iIcctCmZmZlS3iH8iOyXU6tBBQ6b8ueffzKShDMBN7MigxJw0pq0KR9++KHxbZ9KEn3ZsmXcx+ceTRhy8IZz58717t3buPNuSQ5NlUYStCmTJk0CLSB7eo4V6X/bshw+fPj11183VljBp5y7dykpKcePH6dN4WZbeHIAUF1S2yE2BXrIoz9k5yWmAjze7y96wf53wO5powkIY1aoV3788ceWLVsaa7A1nvLGjRszPwFHlX5ryIL1kJqD7ovxORzwdjt06DB58uQBAwYYx9ulW6PkeLSg2ujTp8+///5rFxKHYUbQqcYARo4cKWGwCIZxSrt27Xbt2sV8SaXMCDo+xz///NO1a1fjui+yIPVQenp6cXFxVlYWPjAzuUY1x6MGLfrixYu5b86etvBqI+gEsbApXbp0Ma7aEJAu4A0rAv3zGvz84G4cvFcSy7YpMW5DA90XGD7oD+MoD3uapZLjkQCSiI+PpytKtSFiExF6uMLoY+HChZxXTInagsE7nAFUkUGxF6S3AaeV07EZA3vYlpCQAMXGnCx84XHjxvFOaBFcn7Cg5HiYwFc5YsSIEydOBNwkJiUXJvKk1Pv162cc39MmB9d85ZVXTp48yUxrpWqDHivI1KpVK0m6e3RARkYGOIGbL1++jPsvXrzIsiCQQw6MAkv8rZeKBwK+ULiisCZ0I4LWHphflgGnHBBXaJrY2Fhjpd5FJLh+9dVXeXl5TJZURA5pgsKCcCbef/99mdfANWVB/Lh582ZaKGgOLFtSUrJ//34aNeM2vDDLosrjYeL555/n+QeMU4QEYWpFcR01apTdlmKLBLZmz549MgMoGGpHN+hUI4sSgpphJs245LCp1rNnT7uhAcsWFRXBDG3ZsoVxLzjBX2HYpRn3B4XsXMyYMYPNKSKqgFUA5tEczHBDMJ07dzaO1RevUOax9O/fnyc4ybZtSHLQ8+Vqq1ev5taMPddFOiS++OILFokFnGpTqjd6Kl9++SX5AfvSsGFDqhCtAnlQ8Llv0KDBwYMHy93zDyrSGfjRLhlfvny5tMJSlpAHZMOVlyxZwsx6GG80YNUXwpl4++23SQWRK65MlkD2hw4dYlpWCMoVqEugw2hTOAdXbcoDgdso/AahsbmjEXCd0JAS5YPOrVpcU1NTuZQIUvRH8+bNc3JyKtpMsdkmumrfvn3Mtcg68EnJDLyDv8VPyL0ektheCs7p0KFD6zpQnfEQQBnUr1//66+/DrhuREidYfODmnz37t0tWrSwVxP9AXWSlpZGIxVebUhjbWFh4Zw5c+RTUbRYhzELfN41a9ZQYXAf2GZGmdNwRXqx4ZYZEcUDgbJs167d8ePH5SmvSG3Yzzowb948LiLPaIybxMTjzhx8Rb2Tfn7k5uYmJydz98SeIscEaGJiIvugQnq1AadmgO7Ir7/+mpCQYPSchgeEtDinpKTQ/6+UGQF3g/7ChQtvvPGGZ8EYN2XerVs3PMr0DyoFhbp+/XrjsCrm7n01EAVvjh07VloZbINi5+W4OQzA12FSTosL7x98tqCBly1bZnug4V0ECmnz5s3ggWe2guQ0p0+fXl7xJA8bVBuXLl364IMPjE+ctA6tWrXauXOnMCPkOR5CXAAW6rPPPpOknOJ+wLggKSkJGptPeaWag7KEM4G41/iaV0mOtm3b/vnnn4G73YLwagPuS5cuXfxeJLnSr18/cU3sJhf/Rg9jZiq2jz76SAOW+4TsffAp50SeKpIDsqff51mQaw4ePJjtr5Uyo8xpYcKC3333nbm7LNm43IXygHPD4tPwzCBYZoD7V61a5V9TUSXQ6evateuOHTtYQlEVV5R2fenSpcY3TUWal2bPns2saKXkIDPOnDnz3nvvGUuQjFbEFeVeoCRtw0M0x5gxYx7zVxptSE1NpT4I39YWdE/WwfX06dMDBw40ViaUoE3p0aPH/v37w++0BZyYWXoef/rpJ0Sq9RzwU2FlhC10iaQPKnxliUwIYi7k999/RwhmdJjYPYGxIl9DJAsWLAif3g64G2+8rdw5HEO2P4x1FAsVycSJE+kchCcH22FoKWbOnGkc7xgWRD4b1UZcXNyKFSvK3dYYD189INuoOebPn89F9GCoewCeJMiAaUc4eizt9Gcb/fwocwf3TJkyxViFxDFWrXlCQgJ72lh1bAsy5JrlznSoPn36GGu8uixrnCm2HLVOe+FZwWYG6chU2JEjRziOTE/FvjcwOKRoP/7446CVHghDDqC4uBjiAZn69u1rrOZVVtZw2SFDhuTn5+PxDdntYq/G3XlcFy9e7BFhjFsriiu77kpLS4NuYrQizcFdX9qplStXcril5sHuDdAcfEBhGtauXYvHl+0C4cmB24qKivDVf/vttywklkoLqg1K9KuvvqJrEnCqzEOSI+i2uuDvIiRmT5t/PBzead++PdOsuM1PDg8/2EJB2sG0Gde91VD2HoAvi6U048ePLygo4NPG4aHhNQeEfenSJeh54/ve+ZTDprCDUlYL6TmSNGTktm3b2rRpY0LNNzaOK8pBdWF2AW0UFhYyzOYhDawiUHLcD0aNGoWwAvqAHpw97yuk5uD3zqJwY40HNa6JycjIgE0JuvUZIeUnoC1jVtSEmiSJxVetWiVUC/mp7AU5XoyFHbQpMor/cX2j0QJGngg7P/300+zs7CtXrjACpHPqFwO0C3T7zp07u/lVZQAAC9VJREFUQSnYI4kPbeWBKKbcamjwyM+zIAiUl5fHlnxPQEG1kZycLCFxmP1h26zgHSiPwYMHG1dtKDnuE+Io4AV8zEWLFkH8jGmDoaolmLDC+/v27fviiy8QYrDgiq7owIEDDx8+XG6dtuGXny1U/JXly5fLmBf7g3FBsJamJ0wCxl6cnw2xEtMb7FdQcjwEUJG0bt06LS1tw4YN8C24u0auMGNBvUIfpcyZZvzrr7/CcWFx7+zZsyFL3l8WqiA56G7scXQYlkK0yfmTnnNY8E58fDxP/YE+q6LPQe5OmjSJpee0Kdpb+0Cwt6b4ncbGxnbv3n3KlCl//PEHv3EGMiyYkH1zPqkwDXv27MnKymI2IqQUPeTgm8eOHeMZPDF3g++kpKScO3eu/O5Tf8KTAz/iV3r27Cn/ESXHw0GMNe2JgG5H9DFkyJD58+ezgjzoDoSkMPBMizqRTEnQ3ffyizDgbrNx5gfcRvFnRXOILGFxeGf4HR9hBo3gN998Q9eYmVYlx0OD+B+eLzQuLi4pKWnkyJGIHRDaCAlgVmTKiuzbBdwmlKCvPpkvZEw2o01zdyGxcYgCh5cdEh6bUhE5yFGsOWjQIPJM1IaS42FCTIwUbwrg6A0fPvzrr7+GuWFxaLl7MigEwzEvAWcHhK22wbvrypikIjkOHTqEhxuKynZF5U8jvi1zj/MRVRTSWklRI+6BeoOqk43ikOeSKu4f/q+STyHUiV2v+/LLL3/44YerV6+GzyjZVRKF84c5ulS2UgVkBsLmCRMmgBkIVfyVWo0bN96+fbs4whVpjqB78A//EN6ZNWsWQ2K7SU758Whhu6schsEf8dAPGDBg3rx52dnZ9EbFdZVgmK/Z4SI35ObmSvciIVtuWP/1118/c+YMN2zZJxeooLFKbqCO4Y4PZx17evMf7xdWm+D/ftlLInkwBMDDhg1buXLl6dOnxUsNWCcisFhEyHH+/Plp06YlJiZiEbECXA3MgwKg9ZG6wJBmRXwd2p1du3axX9fTQq3keHxgeQQF+ZQLETBYMnHixJ07dxYWFjLWoLdI6dI/kEOZ4LUgAE5NTY2Pj49xB2/A0Bw4cEB+y3+Ch19zcCmYOfLVM3RKETH4H03IBk5Djx494LcePXqUDzenA4IWzJHQmaBiwPXw4cMIQfEr+F1oILFHoiH8QQrJwfEyWOTEiRPSW6WcqHaIcYczyTtQJ+3bt4diWLZsGWdkMzTl+YFXr161e6C5z7J7926ojUo3h4U0ksOFBtL+lOoOhjacOidvNmnSBK4inIytW7dKAIwX+fn5ly9fZqEGUyPl1l5/pRCPBO4tC1oV1R3QH3RE+KPoEkQQHTt2HD58+OLFiw8dOiT+KQfFILhlSEx3JHh3E2xFmoM+6W+//abNBzUSEJuMtybgkfTq1WvcuHGbN2+GubF3aqAMWEImTqgcNuhniWTe4NwYbU6p0aDFsftc2rZtO2jQIIg2JycHmoP8gMhZNsBIJ8xJlEycyGRcPSQwGsDiUI7Q4I+dOnUaPXr0mjVrjh8/Lk6JP7vqIQeLvvBbWEeriKMNMDdNmzYVc1C/fv2kpKSZM2dCkZw/f764uLjMHeriJwcrQoqKing8sYYqUQgKFR4JU6V4wS63oUOHLly48OjRo3AppAzMJgdtSnZ2tvTranqjdgHmZsyYMfBbqUVu3LgBd5UJNCZGeQalnqZQSwF/Ii4uDuphxowZu3fvlnwrNMfZs2dTUlKMW4CimqMWgeNE5UcEwB06dHjnnXeWLl3K/s2NGzfGx8cba9p65D6s4jHCLovnRHP5JwTAycnJmZmZr732mnGHQkXsgyoeP0TerDOyW/g90PRGLYXHUghFqEukVEANiuJ/U0CYRtMSUcX/YJNDmmCVHNUC1UQMMT5E+hNFIzyFOcZJKvCJZCGxfTaWp11RZRO1kOPymjdvnpaWNnbsWKmxa9iwoaS0Y9yDP/V4vVoE6gOKfPr06aWlpXv37l28ePEbb7wh25u4B0SRBAPjSSVH9IODK0iOCRMmlDv90MXFxceOHduwYcPMmTN5ZBohlV0ey6KdhtEJqgGSY9iwYXl5eVevXpVCips3b+7atWvRokWjRo3q2LGj/Yt0RyR9SWgaKgohM8WPHDlS7ozjuXbtmpSDlzsD/DZt2jR58mTcw3EoAj3nJsrBJ7579+48K9puOsVrDg4kSy5cuJCVlfXuu+9269bNLtVUnRG1oGifffbZn376qdydFmfX7rLhjMM9yZJ9+/bBI+nTp0/r1q3FV9XK3igE3cnY2Fi4F+XuJAWptrLntATcudKcHF1YWLht27aMjAzO5dERwVEIqZ6aNm1auXvKvD0lxz//L+CMIg2403zOnTu3fPny1NRUKg+xMnXr1uXh0PZhooqahBj3sFaeSUBy+I8yCdlQZBeI5+fnHzx4cM6cOb169fLbGs2F1FQwYHnzzTcvXrxIJ6Mq5JB+eXt4HLiVm5u7YsUKUI2H7xE8j15d15oHOR4F0WyZO8qtKppDPNbr169LDwFbkhDaIPxZsGDB4MGDObeasA8PV1tTA0DN0aFDB57UJCf5esjhoYg0klB/cKYbtEhpaak0N7MXbcOGDR9//DHMje20UosoP6o7KKeWLVtmZWUxYAlWGcIhe5YGxztJyoTYu3fv/Pnz09PTeb4f/7Qe2ljdQVE1atRo7ty55VU+Qc0Du+NIICzhxg3V0vbt26dOndq7d+9mzZqp5qjuoIRwnTJlCj2G+yBH8O7TaD2g8uDGHgfvI7rRcRo1APL4QuczLn3o5KAXArXB07WgSxYuXBgXF6eao7pDAoekpKSioqL/7gskB42InYAXzYF/4oSFc+fODR8+HH+xXr16GtzWAFBIiYmJJ06cEBNwTwAVZLoXKWKTAyEM7Ei5c8of25A0J1ZjINtv3Ju9D3JQeYAWPGcj6Aa6sm+HNdetW9e5c2d20/Pv6lmvNQBMdbRo0WL9+vWSEbdRRX4w1cEpPDI8lAUAS5cuZVPr008/HRsbSzrqWa81ACQHolkePUwzcU/8YP0HI5GgOxeQaiMvLy8zM5MZMFoT1rXLto6iWkMmjn/00Uckh2zZV5EiQedsDQ7yKnePrAb27NkzevRo/hXoCQ1Pah7kCU5LS5NDMDw7KZXqD6qN6w7IDLifPIWPfYusPFV+1DBI7JCcnIxoNhBq7mfI3IbHspAipNeWLVt45qNxdBJgD6uP3P9Vcb9ISEiAixAINWu8UnIwiOWcrh9++KF58+bGKemIcc9fkr7nSP8vFVWGLS0ELLAFpAVjDTsBGrAOzJLXtCC0JrRHn3/+Od1PWiv1OmswbHIgyNy0aRP33jybLH5yBNxiMJ6VdOPGjfPnz6emptqdTjobNBpAiiCgmDdvHnNWIclhl/nYg9tgVnJycl599VXjTmmSdjrVHDUeFCF8xsmTJ3OTnYU8/4UqM6Ze4VF+9DbWrFlDZhjHz5DDGY2SIwoghuDNN99kI1PAOZmcJaVBtxiML3iyHwsKS0tLFy1axAYFz/A/o1OFowMSYXbu3LmwsJDkAAlIFDEu1ChSllFQUPDJJ5/Q/bQTGNp9H22gdBGw8OAtZsTlkDabHPzXAwcOjBkzhr/VtGnTGN9kNyVH9IBirlu3LlxLagtOHPfkNhie7Nmzp3fv3saZbB9yNSVHVEHIgWiW2qLMPfFEwHNA165d26FDB+OmPiP9wRWPHjHuuGBEs0x00oJwCj11CdxP/Cs333WyTy2C7KdPnTqVZ63JAdLcMSkpKYGT0bp1a2GDTnqsLZBNkKFDhxYVFTHBxcouvD506FB6ejocT+OOjDJKjtoDe2+WZeJXrlxhj9O2bdvwpvHlLdSs1DrA2UQ0KyHrypUrExMTjaMn/BPilBy1C40aNWI0C1d0yZIlzZo1M84WmgQmQgj1SWsRRMbZ2dnwSWfNmsUZGxWViSs5ahFExtOnTx83bhwTXDJaQyv8FP+vJ2BEWCNudw8oORQKhUKhUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCgUCoVCoQD+D2blJkKll3ZHAAAAAElFTkSuQmCC">
|
|
1977
|
+
<link rel="icon" type="image/svg+xml" href="${ROAST_ICON}">
|
|
1978
|
+
<link rel="apple-touch-icon" href="${ROAST_TOUCH_ICON}">
|
|
1976
1979
|
<style>
|
|
1977
1980
|
:root { ${THEMES.dark.css} }
|
|
1978
1981
|
html[data-theme="light"] { ${THEMES.light.css} }
|
|
@@ -2445,8 +2448,8 @@ ${componentsSection()}
|
|
|
2445
2448
|
|
|
2446
2449
|
<footer>
|
|
2447
2450
|
<div class="foot-left">
|
|
2448
|
-
<div>Generated by <a class="brand" href="
|
|
2449
|
-
<div class="foot-sub">Designed and built by <a class="author" href="
|
|
2451
|
+
<div>Generated by <a class="brand" href="${REPO_URL}">roast-my-design-system</a> <span class="ver">ver. ${VERSION}</span> · free to run: <span class="mono" id="npx-cmd">npx roast-my-design-system@latest</span> <button type="button" class="copy-pill" id="npx-copy">copy</button> · also a Claude Code + Codex skill and a local MCP server</div>
|
|
2452
|
+
<div class="foot-sub">Designed and built by <a class="author" href="${AUTHOR_URL}"><span class="gk-mark"></span>Greg Kozakiewicz</a> · <span class="scan-id" title="scan id">${scanId}</span> · ${FEEDBACK_ASK} <a class="feedback" href="${feedbackUrl(VERSION).replace(/&/g, '&')}">${PULSE_ICON}${FEEDBACK_CTA}</a></div>
|
|
2450
2453
|
</div>
|
|
2451
2454
|
<span class="creds"><span>Non-destructive scan</span><span>Read-only</span><span>Paths are real</span></span>
|
|
2452
2455
|
</footer>
|
|
@@ -77,7 +77,8 @@ import { themePaths, lookupThemePath } from './jstheme.mjs';
|
|
|
77
77
|
import { NAMED_COLOURS } from '../lib/named-colours.mjs';
|
|
78
78
|
import { TAILWIND_DEFAULTS } from '../profiles/tailwind-defaults.mjs';
|
|
79
79
|
import { PALETTE } from '../profiles/shadcn-data.mjs';
|
|
80
|
-
import { blankComments,
|
|
80
|
+
import { blankComments, PALETTE_CLASS_RE } from './paint.mjs';
|
|
81
|
+
import { isDemoPath } from '../lib/demo.mjs';
|
|
81
82
|
import { tripletToHsl, isTransparent, normalizeHex, HEX_RE, FUNC_COLOR_RE, isVarRef, funcColour, inDeclaration, isIdentifierHex } from './tokens.mjs';
|
|
82
83
|
import { doorFile } from '../profiles/installed.mjs';
|
|
83
84
|
import { driftClasses } from '../lib/palette.mjs';
|
|
@@ -751,7 +752,7 @@ export function colourUse(root, files, tokens, { email = null, P = null, palette
|
|
|
751
752
|
if (/\.(ts|js)$/.test(f)) for (const h of src.matchAll(HEX_RE)) if (!isIdentifierHex(h[0])) addDefWrite(canonical(normalizeHex(h[0])), f);
|
|
752
753
|
continue;
|
|
753
754
|
}
|
|
754
|
-
if (
|
|
755
|
+
if (isDemoPath(f)) continue;
|
|
755
756
|
codeFiles.push(extractUses(f, pkg, raw, blocks.length > 0, isDoor(f)));
|
|
756
757
|
}
|
|
757
758
|
// .mjs and .cjs configs and presets: their names, never their hex (the
|
|
@@ -1029,7 +1030,7 @@ export function colourUse(root, files, tokens, { email = null, P = null, palette
|
|
|
1029
1030
|
return r;
|
|
1030
1031
|
};
|
|
1031
1032
|
for (const { f, pkg, st, text, inCode } of sheets) {
|
|
1032
|
-
if (
|
|
1033
|
+
if (isDemoPath(f) || isDoor(f)) continue;
|
|
1033
1034
|
if (tw && text.includes('@apply')) for (const a of text.match(/@apply[^;}]*/g) ?? []) classUses(records(a), f, pkg, { apply: true });
|
|
1034
1035
|
for (const d of st.decls) {
|
|
1035
1036
|
for (const m of d.value.matchAll(VAR_RE)) {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
import { join } from 'node:path';
|
|
25
25
|
import { readSource } from './walk.mjs';
|
|
26
26
|
import { exemptReason } from '../lib/exempt.mjs';
|
|
27
|
+
import { isDemoPath } from '../lib/demo.mjs';
|
|
27
28
|
import { PALETTE } from '../profiles/shadcn-data.mjs';
|
|
28
29
|
|
|
29
30
|
// A palette utility with any variant prefix (hover:, md:, dark:, group-hover:)
|
|
@@ -50,12 +51,12 @@ const DOOR_TYPO_RE = /(?<![\w-])(?:[\w-]+:)*(?:font-(?:thin|extralight|light|nor
|
|
|
50
51
|
export const blankComments = (s) => s
|
|
51
52
|
.replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, ' '))
|
|
52
53
|
.replace(/(^|[^:'"`\\])(\/\/[^\n]*)/g, (m, pre, c) => pre + ' '.repeat(c.length));
|
|
53
|
-
//
|
|
54
|
-
// (
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
|
|
54
|
+
// A demo folder is not own code: what a demo is lives in lib/demo.mjs, the
|
|
55
|
+
// rule the walk reads too (10.1.4). Not templates: in a product a templates
|
|
56
|
+
// folder is usually a screen (teable's admin templates, documenso's and
|
|
57
|
+
// formbricks' template pickers), and hiding it hid their colours from the
|
|
58
|
+
// report and the live checks. A project generator's templates are left out
|
|
59
|
+
// by the walk instead, and email templates by the email rule (2026-09-30).
|
|
59
60
|
|
|
60
61
|
/**
|
|
61
62
|
* @param root repo root
|
|
@@ -84,7 +85,7 @@ export function countPaint(root, codeFiles, { uiDirs = [], isDoor = null, kitNam
|
|
|
84
85
|
};
|
|
85
86
|
|
|
86
87
|
for (const f of codeFiles) {
|
|
87
|
-
if (!/\.(tsx|jsx)$/.test(f) || inCatalogue(f) ||
|
|
88
|
+
if (!/\.(tsx|jsx)$/.test(f) || inCatalogue(f) || isDemoPath(f)) continue;
|
|
88
89
|
const raw = readSource(join(root, f));
|
|
89
90
|
if (raw === null || exemptReason(f, raw, { email })) continue;
|
|
90
91
|
ownFiles += 1;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { CATALOGUE } from '../profiles/shadcn-data.mjs';
|
|
7
7
|
import { emailContextOf, printedFilesOf } from '../lib/exempt.mjs';
|
|
8
|
+
import { isDemoDir, isDemoFile } from '../lib/demo.mjs';
|
|
8
9
|
import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs';
|
|
9
10
|
import { join, relative, extname } from 'node:path';
|
|
10
11
|
|
|
@@ -36,22 +37,14 @@ export function readSource(p) {
|
|
|
36
37
|
// Skipped because they are output or machinery, never because they might
|
|
37
38
|
// hold the product's UI: never named in the report as "UI we skipped".
|
|
38
39
|
const BUILD_ONLY = new Set(['node_modules', '.next', '.git', 'dist', 'build', 'out', 'coverage', '.turbo', '.vercel', '.cache', 'storybook-static']);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
// Test/fixture surfaces are not the product's design language — counting a
|
|
48
|
-
// story's `export const Default` 36× would poison the diagnosis numbers.
|
|
49
|
-
'__tests__', '__mocks__', '__fixtures__', '__testfixtures__', '__snapshots__',
|
|
50
|
-
'cypress', 'e2e', 'playwright', 'test', 'tests', '.storybook',
|
|
51
|
-
]);
|
|
52
|
-
|
|
53
|
-
// Same idea at file granularity: Button.test.tsx / Button.stories.tsx / *.cy.ts
|
|
54
|
-
const TEST_FILE_RE = /\.(test|spec|stories|story|cy)\.[cm]?[jt]sx?$/;
|
|
40
|
+
// Documentation sites (Docusaurus and friends) carry their own theme and
|
|
41
|
+
// demo fixtures — that styling is not the product's design language.
|
|
42
|
+
const DOCS_DIRS = new Set(['docs', 'dev-docs', 'website', 'documentation']);
|
|
43
|
+
// Left out by name: output and machinery, a docs site, and a demo, a story
|
|
44
|
+
// or a test. What a demo is lives in lib/demo.mjs, the one rule the palette
|
|
45
|
+
// tile and the live checks read too (10.1.4).
|
|
46
|
+
// Folders only: likec4's storybook-icon.tsx is an icon, not a Storybook.
|
|
47
|
+
const skipDir = (e) => !e.isFile() && (BUILD_ONLY.has(e.name) || DOCS_DIRS.has(e.name) || isDemoDir(e.name));
|
|
55
48
|
|
|
56
49
|
// public/ is static assets in almost every repo — but grafana keeps its whole
|
|
57
50
|
// frontend under public/app. Skip it only when a quick probe finds no real
|
|
@@ -63,9 +56,9 @@ function hasComponentSource(dir, depth = 0) {
|
|
|
63
56
|
let entries = [];
|
|
64
57
|
try { entries = readdirSync(dir, { withFileTypes: true }); } catch { return false; }
|
|
65
58
|
for (const e of entries) {
|
|
66
|
-
if (e.name.startsWith('.') ||
|
|
59
|
+
if (e.name.startsWith('.') || skipDir(e)) continue;
|
|
67
60
|
if (e.isDirectory()) { if (hasComponentSource(join(dir, e.name), depth + 1)) return true; }
|
|
68
|
-
else if (SOURCE_PROBE_RE.test(e.name) && !
|
|
61
|
+
else if (SOURCE_PROBE_RE.test(e.name) && !isDemoFile(e.name)) return true;
|
|
69
62
|
}
|
|
70
63
|
return false;
|
|
71
64
|
}
|
|
@@ -110,9 +103,9 @@ export function walkRepo(root, maxDepth = 14, exclusions = null, readAnyway = ne
|
|
|
110
103
|
try { entries = readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
111
104
|
for (const e of entries) {
|
|
112
105
|
if (e.name.startsWith('.') && e.name !== '.cursorrules') continue;
|
|
113
|
-
if (
|
|
106
|
+
if (skipDir(e) || e.isSymbolicLink()) continue;
|
|
114
107
|
if (e.isDirectory()) { countExcluded(join(dir, e.name), depth + 1, hit); continue; }
|
|
115
|
-
if (
|
|
108
|
+
if (isDemoFile(e.name)) continue;
|
|
116
109
|
hit.files += 1;
|
|
117
110
|
}
|
|
118
111
|
};
|
|
@@ -131,7 +124,7 @@ export function walkRepo(root, maxDepth = 14, exclusions = null, readAnyway = ne
|
|
|
131
124
|
for (const e of es) {
|
|
132
125
|
if (e.name.startsWith('.') || e.name === 'node_modules') continue;
|
|
133
126
|
if (e.isDirectory()) walkIn(join(d, e.name), depth + 1);
|
|
134
|
-
else if (UI_FILE_RE.test(e.name) && !
|
|
127
|
+
else if (UI_FILE_RE.test(e.name) && !isDemoFile(e.name)) n += 1;
|
|
135
128
|
}
|
|
136
129
|
};
|
|
137
130
|
walkIn(dir, 0);
|
|
@@ -143,7 +136,7 @@ export function walkRepo(root, maxDepth = 14, exclusions = null, readAnyway = ne
|
|
|
143
136
|
try { entries = readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
144
137
|
for (const e of entries) {
|
|
145
138
|
if (e.name.startsWith('.') && e.name !== '.cursorrules') continue;
|
|
146
|
-
if ((
|
|
139
|
+
if ((skipDir(e) && !readAnyway.has(e.name)) || LEGACY_DIR_RE.test(e.name)) {
|
|
147
140
|
if (e.isDirectory() && !BUILD_ONLY.has(e.name)) countSkipped(join(dir, e.name), e.name);
|
|
148
141
|
continue;
|
|
149
142
|
}
|
|
@@ -159,11 +152,11 @@ export function walkRepo(root, maxDepth = 14, exclusions = null, readAnyway = ne
|
|
|
159
152
|
const hit = excluded(rel);
|
|
160
153
|
if (hit) {
|
|
161
154
|
if (e.isDirectory()) countExcluded(p, depth + 1, hit);
|
|
162
|
-
else if (!
|
|
155
|
+
else if (!isDemoFile(e.name)) hit.files += 1;
|
|
163
156
|
continue;
|
|
164
157
|
}
|
|
165
158
|
if (e.isDirectory()) { recurse(p, depth + 1); continue; }
|
|
166
|
-
if (
|
|
159
|
+
if (isDemoFile(e.name)) continue;
|
|
167
160
|
const ext = extname(e.name);
|
|
168
161
|
if (STYLE_EXTS.has(ext)) files.styles.push(rel);
|
|
169
162
|
else if (CODE_EXTS.has(ext)) files.code.push(rel);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a demo is, said once (10.1.4).
|
|
3
|
+
*
|
|
4
|
+
* Stories, examples, demos and tests are not the product's design language:
|
|
5
|
+
* a story's `export const Default` counted 36 times poisons the numbers, and
|
|
6
|
+
* a demo's colours are nobody's decision. Until 10.1.4 two places decided
|
|
7
|
+
* this. The walk left out examples, demos, tests and *.stories files, and
|
|
8
|
+
* read a stories/ folder; the palette tile and the live palette check kept a
|
|
9
|
+
* pattern of their own that left the stories/ folder out as well. So a story
|
|
10
|
+
* folder's colours were out of the palette count and in every other tile.
|
|
11
|
+
* Now the walk, the palette tile and the live checks all ask this file.
|
|
12
|
+
*
|
|
13
|
+
* Names only, and exact ones: the fleet shows why a looser rule is unsafe.
|
|
14
|
+
* A file called story.ts is midday's writing prompt, storybook-icon.tsx is
|
|
15
|
+
* likec4's icon of the Storybook logo, dify's sandbox.tsx is a billing plan
|
|
16
|
+
* and supabase's examples.tsx is a product page.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// Example integrations, demo apps and playgrounds.
|
|
20
|
+
const EXAMPLE_DIRS = ['examples', 'example', 'demos', 'demo', 'playground', 'fixtures', 'fixture'];
|
|
21
|
+
// Stories kept in a folder of their own: twenty's __stories__ (326 files),
|
|
22
|
+
// sentry's, react-spectrum's stories/, ballerine's _stories, a Storybook
|
|
23
|
+
// app in apps/storybook.
|
|
24
|
+
const STORY_DIRS = ['stories', '__stories__', '_stories', 'storybook', '.storybook'];
|
|
25
|
+
// Test surfaces.
|
|
26
|
+
const TEST_DIRS = ['__tests__', '__mocks__', '__fixtures__', '__testfixtures__', '__snapshots__',
|
|
27
|
+
'cypress', 'e2e', 'playwright', 'test', 'tests'];
|
|
28
|
+
|
|
29
|
+
export const DEMO_DIRS = new Set([...EXAMPLE_DIRS, ...STORY_DIRS, ...TEST_DIRS]);
|
|
30
|
+
|
|
31
|
+
// A folder whose name starts with storybook is Storybook's too: trigger.dev
|
|
32
|
+
// keeps an in-app storybook as routes/storybook.accordion/ and 70 more,
|
|
33
|
+
// siemens a storybook-docs package, coder its stories' data in storybookData.
|
|
34
|
+
const STORYBOOK_DIR_RE = /^storybook[.\-_A-Z]/;
|
|
35
|
+
|
|
36
|
+
/** a folder name the scan leaves out as a demo, a story or a test */
|
|
37
|
+
export const isDemoDir = (name) => DEMO_DIRS.has(name) || STORYBOOK_DIR_RE.test(name);
|
|
38
|
+
|
|
39
|
+
// The same at file granularity: Button.test.tsx, Button.stories.tsx, *.cy.ts,
|
|
40
|
+
// and a component folder's stories kept as stories.tsx (formbricks, 23).
|
|
41
|
+
export const TEST_FILE_RE = /\.(test|spec|stories|story|cy)\.[cm]?[jt]sx?$/;
|
|
42
|
+
const STORIES_FILE_RE = /^stories\.[cm]?[jt]sx?$/;
|
|
43
|
+
|
|
44
|
+
/** a file name the scan leaves out as a story or a test */
|
|
45
|
+
export const isDemoFile = (name) => TEST_FILE_RE.test(name) || STORIES_FILE_RE.test(name);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* A path the scan leaves out as a demo: any folder on the way is a demo
|
|
49
|
+
* folder, or the file itself is a story or a test. For a checker handed one
|
|
50
|
+
* file (the live checks, a guard); the walk asks isDemoDir and isDemoFile
|
|
51
|
+
* as it goes.
|
|
52
|
+
*/
|
|
53
|
+
export function isDemoPath(file) {
|
|
54
|
+
const parts = String(file).replaceAll('\\', '/').split('/');
|
|
55
|
+
const name = parts.pop();
|
|
56
|
+
return parts.some(isDemoDir) || isDemoFile(name);
|
|
57
|
+
}
|
|
@@ -18,7 +18,8 @@ import { decideProfile, profileOf, installedDirs, installedFile, installedFrom }
|
|
|
18
18
|
import { KITS } from '../profiles/kit-common.mjs';
|
|
19
19
|
import { kitPaintFindings } from './kitpaint.mjs';
|
|
20
20
|
import { paletteFindings } from './palette.mjs';
|
|
21
|
-
import { PALETTE_CLASS_RE,
|
|
21
|
+
import { PALETTE_CLASS_RE, blankComments } from '../harvest/paint.mjs';
|
|
22
|
+
import { isDemoPath } from './demo.mjs';
|
|
22
23
|
import { harvestTokens, extractStyling, classStringStyling, normalizeHex, isGrey } from '../harvest/tokens.mjs';
|
|
23
24
|
import { harvestComponents, definedComponents, tsconfigAliases } from '../harvest/components.mjs';
|
|
24
25
|
import { findDuplicates } from '../harvest/duplicates.mjs';
|
|
@@ -71,10 +72,10 @@ export { WIDGET_CSS_RE, WIDGET_CONFIG_RE, LIBRARY_CLASS_RE, isLibraryClass } fro
|
|
|
71
72
|
// blankComments (8.4.5) is what the report and the live checks run on a file
|
|
72
73
|
// before matching, so a class named in a comment paints nothing; a guard that
|
|
73
74
|
// matches the raw text counts it, and disagrees with the report.
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
export { PALETTE_CLASS_RE,
|
|
75
|
+
// isDemoPath(file) (10.1.4, was the pattern DEMO_PATH_RE since 9.3.2) is the
|
|
76
|
+
// one rule for what a demo is, stories, examples, demos and tests: the walk
|
|
77
|
+
// leaves those files out and the palette count does too.
|
|
78
|
+
export { PALETTE_CLASS_RE, isDemoPath, blankComments };
|
|
78
79
|
// The palette rule itself (9.4.0): paletteFindings(fileText, system.profile
|
|
79
80
|
// .palette, { file }) returns the worded findings the live checks give for
|
|
80
81
|
// the same file, with the index of each in the text; empty when the rule is
|
|
@@ -230,6 +231,12 @@ export function learnSystem(repoRoot, { exclude = [] } = {}) {
|
|
|
230
231
|
// non-canonical copy hard-codes: the `dupes` avoidedImportFindings reads
|
|
231
232
|
duplicates: dupeCopiesOf(hardDupes, ledger, read),
|
|
232
233
|
files: { styles: files.styles.length, code: files.code.length },
|
|
234
|
+
// Every file the report's walk read (10.1.4), by path from the scanned
|
|
235
|
+
// folder. The walk leaves out docs sites, examples, demos, stories and
|
|
236
|
+
// tests, and reads a website that holds the product; a guard that judges
|
|
237
|
+
// only the files in this set judges what the report reads, by
|
|
238
|
+
// construction rather than by a second list of folder names.
|
|
239
|
+
filesRead: new Set([...files.code, ...files.styles, ...files.other]),
|
|
233
240
|
// How the repo was read (7.1 to 7.7), decided once by the same profiles
|
|
234
241
|
// the report uses, so a guard treats installed code, a registry's
|
|
235
242
|
// published folders and a widget's stylesheet the way the score does.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two web addresses the report and the rules file print: the project's
|
|
3
|
+
* repository and its author's site. Kept apart from the files that write
|
|
4
|
+
* them (10.1.3): the plugin directory's checker reads a web address beside
|
|
5
|
+
* the word that names a design-system value as a credential being sent to a
|
|
6
|
+
* server, and those two files use that word on most lines.
|
|
7
|
+
*/
|
|
8
|
+
export const REPO_URL = 'https://github.com/gregkozakiewicz/roast-my-design-system';
|
|
9
|
+
export const AUTHOR_URL = 'https://gregkozakiewicz.com/?utm_source=roast-report';
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
* the theme file, the theme colour nearest by value that suits the utility,
|
|
32
32
|
* and what to add when none fits.
|
|
33
33
|
*/
|
|
34
|
-
import { PALETTE_CLASS_RE, GREY_HUE_RE, DARK_WB_RE,
|
|
34
|
+
import { PALETTE_CLASS_RE, GREY_HUE_RE, DARK_WB_RE, blankComments } from '../harvest/paint.mjs';
|
|
35
|
+
import { isDemoPath } from './demo.mjs';
|
|
35
36
|
import { TAILWIND_DEFAULTS } from '../profiles/tailwind-defaults.mjs';
|
|
36
37
|
import { oklab } from './color.mjs';
|
|
37
38
|
import { doorFile } from '../profiles/installed.mjs';
|
|
@@ -85,7 +86,7 @@ export function decidePalette(profile, P, readTheme = null) {
|
|
|
85
86
|
export function paletteFindings(text, palette, { file = null, css = null } = {}) {
|
|
86
87
|
if (!palette || !text) return [];
|
|
87
88
|
if (css ?? (file ? CSS_FILE_RE.test(file) : false)) return [];
|
|
88
|
-
if (file && (
|
|
89
|
+
if (file && (isDemoPath(file) || doorFile(palette.doors, file))) return [];
|
|
89
90
|
const themeFile = palette.file ?? 'the theme sheet';
|
|
90
91
|
// the example name follows the utility: a text- class wants an ink or
|
|
91
92
|
// foreground name, a bg- class a surface or background name
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Single version constant for the engine — imported by diagnose (report
|
|
2
2
|
// footer) and rules (generated-by line). This is the bump spot that used to
|
|
3
3
|
// live as a const inside diagnose/index.mjs.
|
|
4
|
-
export const VERSION = '10.1.
|
|
4
|
+
export const VERSION = '10.1.4';
|
|
5
5
|
// The shape of harvest.json and summary.json. Bumped only when a field is
|
|
6
6
|
// renamed, removed or changes meaning; a new field is not a new schema. A
|
|
7
7
|
// tool comparing two scans compares like with like by this number, not by VERSION.
|
|
@@ -11,6 +11,7 @@ import { neverImportedComponents } from '../lib/neverimported.mjs';
|
|
|
11
11
|
import { VERSION } from '../lib/version.mjs';
|
|
12
12
|
import { profileOf, installedOf, splitArbitrary, ownSpacing, installedByOwner } from '../profiles/index.mjs';
|
|
13
13
|
import { themeClassFor } from '../lib/palette.mjs';
|
|
14
|
+
import { REPO_URL } from '../lib/links.mjs';
|
|
14
15
|
import { rulesOn as lintRulesOn, describeRule as lintDescribe } from '../profiles/shadcn-lint.mjs';
|
|
15
16
|
|
|
16
17
|
export function rulesMarkdown(h, opts = {}) {
|
|
@@ -284,7 +285,7 @@ if (neverImported.length >= 3) {
|
|
|
284
285
|
lines.push(`*Compact rules by roast-my-design-system ver. ${VERSION}; the full set with receipts: npx roast-my-design-system@latest --rules*`);
|
|
285
286
|
} else {
|
|
286
287
|
lines.push('---');
|
|
287
|
-
lines.push(`*Generated by [roast-my-design-system](
|
|
288
|
+
lines.push(`*Generated by [roast-my-design-system](${REPO_URL}) ver. ${VERSION}. Rescan after refactors to keep these rules honest.*`);
|
|
288
289
|
}
|
|
289
290
|
lines.push('');
|
|
290
291
|
let text = lines.join('\n');
|