roast-my-design-system 4.2.2 → 4.2.3
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/bin/roast.mjs +6 -3
- package/package.json +1 -1
- package/skills/roast-my-design-system/scripts/diagnose/index.mjs +1 -1
- package/skills/roast-my-design-system/scripts/harvest/components.mjs +1 -1
- package/skills/roast-my-design-system/scripts/harvest/index.mjs +29 -7
- package/skills/roast-my-design-system/scripts/harvest/walk.mjs +5 -1
- package/skills/roast-my-design-system/scripts/lib/version.mjs +1 -1
package/bin/roast.mjs
CHANGED
|
@@ -95,9 +95,10 @@ const tmp = mkdtempSync(join(tmpdir(), 'roast-'));
|
|
|
95
95
|
const harvestPath = join(tmp, 'harvest.json');
|
|
96
96
|
const summaryPath = join(tmp, 'summary.json');
|
|
97
97
|
|
|
98
|
-
function run(script, args) {
|
|
98
|
+
function run(script, args, env) {
|
|
99
99
|
// --json keeps stdout clean for the JSON payload; child chatter is dropped
|
|
100
|
-
const r = spawnSync(process.execPath, [join(SCRIPTS, script), ...args],
|
|
100
|
+
const r = spawnSync(process.execPath, [join(SCRIPTS, script), ...args],
|
|
101
|
+
{ stdio: asJson ? 'ignore' : 'inherit', ...(env ? { env: { ...process.env, ...env } } : {}) });
|
|
101
102
|
if (r.status !== 0) {
|
|
102
103
|
rmSync(tmp, { recursive: true, force: true });
|
|
103
104
|
process.exit(r.status ?? 1);
|
|
@@ -106,8 +107,10 @@ function run(script, args) {
|
|
|
106
107
|
const say = (s) => { if (!asJson) console.log(s); };
|
|
107
108
|
|
|
108
109
|
say(`roast-my-design-system ${VERSION} · read-only scan, nothing leaves your machine\n`);
|
|
110
|
+
// the harvest goes to a temp dir this wrapper deletes right after; tell the
|
|
111
|
+
// script so it does not print a path that will be gone seconds later
|
|
109
112
|
run('harvest/index.mjs', [target, '--out', harvestPath,
|
|
110
|
-
...excludes.flatMap((e) => ['--exclude', e])]);
|
|
113
|
+
...excludes.flatMap((e) => ['--exclude', e])], { ROAST_EPHEMERAL_OUT: '1' });
|
|
111
114
|
say('');
|
|
112
115
|
run('diagnose/index.mjs', [harvestPath, '--out', outPath, '--theme', theme, '--summary', summaryPath,
|
|
113
116
|
...(commissionedBy ? ['--by', commissionedBy] : [])]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "roast-my-design-system",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.3",
|
|
4
4
|
"description": "Your AI can write the UI. This makes sure it writes your UI. A deterministic scanner counts every colour, spacing value and duplicate component, scores you 0-100 against 34 public repos, scopes the scan with .roastignore, injects agent rules with --apply.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"design-system",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Diagnose — step 2 of the
|
|
3
|
+
* Diagnose — step 2 of the pipeline. Renders a harvest.json into a
|
|
4
4
|
* single self-contained HTML report: the visceral, shareable "here's your
|
|
5
5
|
* mess" page with real file paths. No dependencies, no server — one file.
|
|
6
6
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Component harvest — every component defined in the repo, with its REAL prop
|
|
3
3
|
* signature and where it is actually used. Parsers lifted from 1.0's
|
|
4
|
-
* compile-skill.mjs
|
|
4
|
+
* compile-skill.mjs — brace/string-aware regex parsing,
|
|
5
5
|
* no AST dependency — then extended to scan the whole repo, not one ui/ dir.
|
|
6
6
|
*/
|
|
7
7
|
import { readFileSync } from 'node:fs';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Harvest — step 1 of the
|
|
3
|
+
* Harvest — step 1 of the pipeline. Non-destructive scan of a repo:
|
|
4
4
|
* components (real props, real usages), all styling, implicit tokens with
|
|
5
5
|
* frequency counts, duplicates, context files. Writes harvest.json; prints a
|
|
6
6
|
* one-screen summary (the seed of the step-2 diagnosis).
|
|
@@ -159,23 +159,45 @@ writeFileSync(outPath, JSON.stringify(harvest, null, 2));
|
|
|
159
159
|
// ---------- one-screen summary ----------
|
|
160
160
|
const nonPage = components.filter((c) => !c.isPage);
|
|
161
161
|
const hexColors = tokens.colors.filter((c) => c.value.startsWith('#'));
|
|
162
|
-
|
|
162
|
+
// A truncated capture like "rgba(var(--ink-rgb)" reads as a glitch in the
|
|
163
|
+
// terminal; the token reference inside it is the real story, so show that.
|
|
164
|
+
const showVal = (v) => {
|
|
165
|
+
const m = /var\((--[A-Za-z0-9_-]+)/.exec(v);
|
|
166
|
+
const balanced = (v.match(/\(/g) ?? []).length === (v.match(/\)/g) ?? []).length;
|
|
167
|
+
return m && !balanced ? `var(${m[1]})` : v;
|
|
168
|
+
};
|
|
169
|
+
// A "top" list is only news when something repeats; all-×1 says nothing.
|
|
170
|
+
const top = (list, n = 5) => list.some((e) => e.count > 1)
|
|
171
|
+
? ` top: ${list.slice(0, n).map((e) => `${showVal(e.value)} ×${e.count}`).join(', ')}`
|
|
172
|
+
: (list.length ? ', none repeated' : '');
|
|
163
173
|
|
|
164
174
|
console.log(`\nHarvest: ${profile.name ?? target}`);
|
|
165
175
|
console.log(` framework: ${profile.framework}${profile.typescript ? ' + TS' : ''} design system: ${profile.designSystem.kind}${profile.designSystem.name ? ` (${profile.designSystem.name})` : ''} styling: ${profile.stylingDeps.join(', ') || 'none detected'}`);
|
|
166
176
|
console.log(` files: ${files.code.length} code, ${files.styles.length} style`);
|
|
167
177
|
if (exclusions.patterns.length) {
|
|
168
|
-
|
|
169
|
-
|
|
178
|
+
// Same voice as the report header: source named once, slashes on folders,
|
|
179
|
+
// and the total at the end because that is the number that lands.
|
|
180
|
+
const groups = [...new Set(exclusions.patterns.map((p) => p.source))].map((src) => {
|
|
181
|
+
const own = exclusions.patterns.filter((p) => p.source === src);
|
|
182
|
+
return `(${src}): ${own.map((p, i) => `${p.pattern}/ ${p.files}${i === 0 ? ' files' : ''}`).join(', ')}`;
|
|
183
|
+
});
|
|
184
|
+
const total = exclusions.patterns.reduce((sum, p) => sum + p.files, 0);
|
|
185
|
+
console.log(` excluded by you ${groups.join(' · ')} · ${total} files kept out of this scan`);
|
|
170
186
|
}
|
|
171
187
|
console.log(`\n components: ${components.length} defined (${nonPage.length} reusable, ${components.length - nonPage.length} pages)`);
|
|
172
188
|
console.log(` duplicates: ${duplicates.exactDuplicates.length} exact same-name, ${duplicates.families.length} name families`);
|
|
173
189
|
for (const d of duplicates.exactDuplicates.slice(0, 3)) console.log(` · ${d.name} defined in ${d.files.length} files`);
|
|
174
190
|
for (const f of duplicates.families.slice(0, 3)) console.log(` · ${f.root} family: ${f.members.map((m) => m.name).join(', ')}`);
|
|
175
|
-
console.log(`\n colours: ${tokens.colors.length} distinct (${hexColors.length} hex, of which ${tokens.greyCount} greys)
|
|
176
|
-
console.log(` spacing: ${tokens.spacing.length} distinct CSS values
|
|
191
|
+
console.log(`\n colours: ${tokens.colors.length} distinct (${hexColors.length} hex, of which ${tokens.greyCount} greys)${top(tokens.colors, 4)}`);
|
|
192
|
+
console.log(` spacing: ${tokens.spacing.length} distinct CSS values${top(tokens.spacing, 5)}`);
|
|
177
193
|
console.log(` radii: ${tokens.radii.length} font sizes: ${tokens.fontSizes.length} font families: ${tokens.fontFamilies.length} shadows: ${tokens.shadows.length}`);
|
|
178
194
|
console.log(` tailwind: ${tokens.tailwind.colors.length} colour utils, ${tokens.tailwind.spacing.length} spacing utils, ${tokens.tailwind.textSizes.length} text sizes`);
|
|
179
195
|
console.log(` inline styles: ${tokens.inlineStyles.count} blocks`);
|
|
180
196
|
console.log(` context files: ${context.map((c) => c.file).join(', ') || 'none'}`);
|
|
181
|
-
|
|
197
|
+
// The npx wrapper writes the harvest to a temp dir it deletes right after;
|
|
198
|
+
// pointing the user at a path that will not exist is noise there.
|
|
199
|
+
if (process.env.ROAST_EPHEMERAL_OUT === '1') {
|
|
200
|
+
console.log(`\n scanned in ${harvest.tookMs}ms\n`);
|
|
201
|
+
} else {
|
|
202
|
+
console.log(`\n → ${outPath} (${harvest.tookMs}ms)\n`);
|
|
203
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* File walker + repo profile. Adapted from
|
|
2
|
+
* File walker + repo profile. Adapted from an earlier internal repo inspector,
|
|
3
3
|
* widened: the harvester must see EVERYTHING that styles the app — code,
|
|
4
4
|
* stylesheets of any flavor, and config — not just a happy-path shadcn layout.
|
|
5
5
|
*/
|
|
@@ -142,11 +142,15 @@ export function profileRepo(root, files) {
|
|
|
142
142
|
deps = { ...deps, ...pkg.dependencies, ...pkg.devDependencies };
|
|
143
143
|
const monorepo = pkgFiles.some((f) => f !== 'package.json');
|
|
144
144
|
|
|
145
|
+
// Hand-built sites are a real category, not a detection failure: "unknown"
|
|
146
|
+
// next to a good score reads like a shrug, so name what is actually there.
|
|
147
|
+
const htmlFiles = files.other.filter((f) => /\.html?$/.test(f)).length;
|
|
145
148
|
const framework =
|
|
146
149
|
deps.next ? 'next'
|
|
147
150
|
: deps['@remix-run/react'] || deps['@react-router/dev'] ? 'remix'
|
|
148
151
|
: deps.vite && deps.react ? 'vite-react'
|
|
149
152
|
: deps.react ? 'react'
|
|
153
|
+
: htmlFiles > 0 ? 'static HTML/CSS'
|
|
150
154
|
: 'unknown';
|
|
151
155
|
|
|
152
156
|
const componentsJson = readJSON(join(root, 'components.json'));
|