@agent-scope/cli 1.9.0 → 1.10.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/dist/cli.js +346 -16
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +325 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +327 -4
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/cli.js
CHANGED
|
@@ -255,9 +255,9 @@ function createRL() {
|
|
|
255
255
|
});
|
|
256
256
|
}
|
|
257
257
|
async function ask(rl, question) {
|
|
258
|
-
return new Promise((
|
|
258
|
+
return new Promise((resolve7) => {
|
|
259
259
|
rl.question(question, (answer) => {
|
|
260
|
-
|
|
260
|
+
resolve7(answer.trim());
|
|
261
261
|
});
|
|
262
262
|
});
|
|
263
263
|
}
|
|
@@ -2047,6 +2047,332 @@ function createRenderCommand() {
|
|
|
2047
2047
|
return renderCmd;
|
|
2048
2048
|
}
|
|
2049
2049
|
|
|
2050
|
+
// src/report/baseline.ts
|
|
2051
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, rmSync, writeFileSync as writeFileSync5 } from "fs";
|
|
2052
|
+
import { resolve as resolve5 } from "path";
|
|
2053
|
+
import { generateManifest as generateManifest2 } from "@agent-scope/manifest";
|
|
2054
|
+
import { BrowserPool as BrowserPool3, safeRender as safeRender2 } from "@agent-scope/render";
|
|
2055
|
+
import { ComplianceEngine, TokenResolver } from "@agent-scope/tokens";
|
|
2056
|
+
var DEFAULT_BASELINE_DIR = ".reactscope/baseline";
|
|
2057
|
+
var _pool3 = null;
|
|
2058
|
+
async function getPool3(viewportWidth, viewportHeight) {
|
|
2059
|
+
if (_pool3 === null) {
|
|
2060
|
+
_pool3 = new BrowserPool3({
|
|
2061
|
+
size: { browsers: 1, pagesPerBrowser: 4 },
|
|
2062
|
+
viewportWidth,
|
|
2063
|
+
viewportHeight
|
|
2064
|
+
});
|
|
2065
|
+
await _pool3.init();
|
|
2066
|
+
}
|
|
2067
|
+
return _pool3;
|
|
2068
|
+
}
|
|
2069
|
+
async function shutdownPool3() {
|
|
2070
|
+
if (_pool3 !== null) {
|
|
2071
|
+
await _pool3.close();
|
|
2072
|
+
_pool3 = null;
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
async function renderComponent(filePath, componentName, props, viewportWidth, viewportHeight) {
|
|
2076
|
+
const pool = await getPool3(viewportWidth, viewportHeight);
|
|
2077
|
+
const htmlHarness = await buildComponentHarness(filePath, componentName, props, viewportWidth);
|
|
2078
|
+
const slot = await pool.acquire();
|
|
2079
|
+
const { page } = slot;
|
|
2080
|
+
try {
|
|
2081
|
+
await page.setContent(htmlHarness, { waitUntil: "load" });
|
|
2082
|
+
await page.waitForFunction(
|
|
2083
|
+
() => {
|
|
2084
|
+
const w = window;
|
|
2085
|
+
return w.__SCOPE_RENDER_COMPLETE__ === true;
|
|
2086
|
+
},
|
|
2087
|
+
{ timeout: 15e3 }
|
|
2088
|
+
);
|
|
2089
|
+
const renderError = await page.evaluate(() => {
|
|
2090
|
+
return window.__SCOPE_RENDER_ERROR__ ?? null;
|
|
2091
|
+
});
|
|
2092
|
+
if (renderError !== null) {
|
|
2093
|
+
throw new Error(`Component render error: ${renderError}`);
|
|
2094
|
+
}
|
|
2095
|
+
const rootDir = process.cwd();
|
|
2096
|
+
const classes = await page.evaluate(() => {
|
|
2097
|
+
const set = /* @__PURE__ */ new Set();
|
|
2098
|
+
document.querySelectorAll("[class]").forEach((el) => {
|
|
2099
|
+
for (const c of el.className.split(/\s+/)) {
|
|
2100
|
+
if (c) set.add(c);
|
|
2101
|
+
}
|
|
2102
|
+
});
|
|
2103
|
+
return [...set];
|
|
2104
|
+
});
|
|
2105
|
+
const projectCss = await getCompiledCssForClasses(rootDir, classes);
|
|
2106
|
+
if (projectCss != null && projectCss.length > 0) {
|
|
2107
|
+
await page.addStyleTag({ content: projectCss });
|
|
2108
|
+
}
|
|
2109
|
+
const startMs = performance.now();
|
|
2110
|
+
const rootLocator = page.locator("[data-reactscope-root]");
|
|
2111
|
+
const boundingBox = await rootLocator.boundingBox();
|
|
2112
|
+
if (boundingBox === null || boundingBox.width === 0 || boundingBox.height === 0) {
|
|
2113
|
+
throw new Error(
|
|
2114
|
+
`Component "${componentName}" rendered with zero bounding box \u2014 it may be invisible or not mounted`
|
|
2115
|
+
);
|
|
2116
|
+
}
|
|
2117
|
+
const PAD = 24;
|
|
2118
|
+
const MIN_W = 320;
|
|
2119
|
+
const MIN_H = 200;
|
|
2120
|
+
const clipX = Math.max(0, boundingBox.x - PAD);
|
|
2121
|
+
const clipY = Math.max(0, boundingBox.y - PAD);
|
|
2122
|
+
const rawW = boundingBox.width + PAD * 2;
|
|
2123
|
+
const rawH = boundingBox.height + PAD * 2;
|
|
2124
|
+
const clipW = Math.max(rawW, MIN_W);
|
|
2125
|
+
const clipH = Math.max(rawH, MIN_H);
|
|
2126
|
+
const safeW = Math.min(clipW, viewportWidth - clipX);
|
|
2127
|
+
const safeH = Math.min(clipH, viewportHeight - clipY);
|
|
2128
|
+
const screenshot = await page.screenshot({
|
|
2129
|
+
clip: { x: clipX, y: clipY, width: safeW, height: safeH },
|
|
2130
|
+
type: "png"
|
|
2131
|
+
});
|
|
2132
|
+
const computedStylesRaw = {};
|
|
2133
|
+
const styles = await page.evaluate((sel) => {
|
|
2134
|
+
const el = document.querySelector(sel);
|
|
2135
|
+
if (el === null) return {};
|
|
2136
|
+
const computed = window.getComputedStyle(el);
|
|
2137
|
+
const out = {};
|
|
2138
|
+
for (const prop of [
|
|
2139
|
+
"display",
|
|
2140
|
+
"width",
|
|
2141
|
+
"height",
|
|
2142
|
+
"color",
|
|
2143
|
+
"backgroundColor",
|
|
2144
|
+
"fontSize",
|
|
2145
|
+
"fontFamily",
|
|
2146
|
+
"padding",
|
|
2147
|
+
"margin"
|
|
2148
|
+
]) {
|
|
2149
|
+
out[prop] = computed.getPropertyValue(prop);
|
|
2150
|
+
}
|
|
2151
|
+
return out;
|
|
2152
|
+
}, "[data-reactscope-root] > *");
|
|
2153
|
+
computedStylesRaw["[data-reactscope-root] > *"] = styles;
|
|
2154
|
+
const renderTimeMs = performance.now() - startMs;
|
|
2155
|
+
return {
|
|
2156
|
+
screenshot,
|
|
2157
|
+
width: Math.round(safeW),
|
|
2158
|
+
height: Math.round(safeH),
|
|
2159
|
+
renderTimeMs,
|
|
2160
|
+
computedStyles: computedStylesRaw
|
|
2161
|
+
};
|
|
2162
|
+
} finally {
|
|
2163
|
+
pool.release(slot);
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
function extractComputedStyles(computedStylesRaw) {
|
|
2167
|
+
const flat = {};
|
|
2168
|
+
for (const styles of Object.values(computedStylesRaw)) {
|
|
2169
|
+
Object.assign(flat, styles);
|
|
2170
|
+
}
|
|
2171
|
+
const colors = {};
|
|
2172
|
+
const spacing = {};
|
|
2173
|
+
const typography = {};
|
|
2174
|
+
const borders = {};
|
|
2175
|
+
const shadows = {};
|
|
2176
|
+
for (const [prop, value] of Object.entries(flat)) {
|
|
2177
|
+
if (prop === "color" || prop === "backgroundColor") {
|
|
2178
|
+
colors[prop] = value;
|
|
2179
|
+
} else if (prop === "padding" || prop === "margin") {
|
|
2180
|
+
spacing[prop] = value;
|
|
2181
|
+
} else if (prop === "fontSize" || prop === "fontFamily" || prop === "fontWeight" || prop === "lineHeight") {
|
|
2182
|
+
typography[prop] = value;
|
|
2183
|
+
} else if (prop === "borderRadius" || prop === "borderWidth") {
|
|
2184
|
+
borders[prop] = value;
|
|
2185
|
+
} else if (prop === "boxShadow") {
|
|
2186
|
+
shadows[prop] = value;
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
return { colors, spacing, typography, borders, shadows };
|
|
2190
|
+
}
|
|
2191
|
+
async function runBaseline(options = {}) {
|
|
2192
|
+
const {
|
|
2193
|
+
outputDir = DEFAULT_BASELINE_DIR,
|
|
2194
|
+
componentsGlob,
|
|
2195
|
+
manifestPath,
|
|
2196
|
+
viewportWidth = 375,
|
|
2197
|
+
viewportHeight = 812
|
|
2198
|
+
} = options;
|
|
2199
|
+
const startTime = performance.now();
|
|
2200
|
+
const rootDir = process.cwd();
|
|
2201
|
+
const baselineDir = resolve5(rootDir, outputDir);
|
|
2202
|
+
const rendersDir = resolve5(baselineDir, "renders");
|
|
2203
|
+
if (existsSync5(baselineDir)) {
|
|
2204
|
+
rmSync(baselineDir, { recursive: true, force: true });
|
|
2205
|
+
}
|
|
2206
|
+
mkdirSync4(rendersDir, { recursive: true });
|
|
2207
|
+
let manifest;
|
|
2208
|
+
if (manifestPath !== void 0) {
|
|
2209
|
+
const { readFileSync: readFileSync7 } = await import("fs");
|
|
2210
|
+
const absPath = resolve5(rootDir, manifestPath);
|
|
2211
|
+
if (!existsSync5(absPath)) {
|
|
2212
|
+
throw new Error(`Manifest not found at ${absPath}.`);
|
|
2213
|
+
}
|
|
2214
|
+
manifest = JSON.parse(readFileSync7(absPath, "utf-8"));
|
|
2215
|
+
process.stderr.write(`Loaded manifest from ${manifestPath}
|
|
2216
|
+
`);
|
|
2217
|
+
} else {
|
|
2218
|
+
process.stderr.write("Scanning for React components\u2026\n");
|
|
2219
|
+
manifest = await generateManifest2({ rootDir });
|
|
2220
|
+
const count = Object.keys(manifest.components).length;
|
|
2221
|
+
process.stderr.write(`Found ${count} components.
|
|
2222
|
+
`);
|
|
2223
|
+
}
|
|
2224
|
+
writeFileSync5(resolve5(baselineDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
|
|
2225
|
+
let componentNames = Object.keys(manifest.components);
|
|
2226
|
+
if (componentsGlob !== void 0) {
|
|
2227
|
+
componentNames = componentNames.filter((name) => matchGlob(componentsGlob, name));
|
|
2228
|
+
process.stderr.write(
|
|
2229
|
+
`Filtered to ${componentNames.length} components matching "${componentsGlob}".
|
|
2230
|
+
`
|
|
2231
|
+
);
|
|
2232
|
+
}
|
|
2233
|
+
const total = componentNames.length;
|
|
2234
|
+
if (total === 0) {
|
|
2235
|
+
process.stderr.write("No components to baseline.\n");
|
|
2236
|
+
const emptyReport = {
|
|
2237
|
+
components: {},
|
|
2238
|
+
totalProperties: 0,
|
|
2239
|
+
totalOnSystem: 0,
|
|
2240
|
+
totalOffSystem: 0,
|
|
2241
|
+
aggregateCompliance: 1,
|
|
2242
|
+
auditedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2243
|
+
};
|
|
2244
|
+
writeFileSync5(
|
|
2245
|
+
resolve5(baselineDir, "compliance.json"),
|
|
2246
|
+
JSON.stringify(emptyReport, null, 2),
|
|
2247
|
+
"utf-8"
|
|
2248
|
+
);
|
|
2249
|
+
return {
|
|
2250
|
+
baselineDir,
|
|
2251
|
+
componentCount: 0,
|
|
2252
|
+
failureCount: 0,
|
|
2253
|
+
wallClockMs: performance.now() - startTime
|
|
2254
|
+
};
|
|
2255
|
+
}
|
|
2256
|
+
process.stderr.write(`Rendering ${total} components\u2026
|
|
2257
|
+
`);
|
|
2258
|
+
const computedStylesMap = /* @__PURE__ */ new Map();
|
|
2259
|
+
let completed = 0;
|
|
2260
|
+
let failureCount = 0;
|
|
2261
|
+
const CONCURRENCY = 4;
|
|
2262
|
+
let nextIdx = 0;
|
|
2263
|
+
const renderOne = async (name) => {
|
|
2264
|
+
const descriptor = manifest.components[name];
|
|
2265
|
+
if (descriptor === void 0) return;
|
|
2266
|
+
const filePath = resolve5(rootDir, descriptor.filePath);
|
|
2267
|
+
const outcome = await safeRender2(
|
|
2268
|
+
() => renderComponent(filePath, name, {}, viewportWidth, viewportHeight),
|
|
2269
|
+
{
|
|
2270
|
+
props: {},
|
|
2271
|
+
sourceLocation: {
|
|
2272
|
+
file: descriptor.filePath,
|
|
2273
|
+
line: descriptor.loc.start,
|
|
2274
|
+
column: 0
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
);
|
|
2278
|
+
completed++;
|
|
2279
|
+
const pct = Math.round(completed / total * 100);
|
|
2280
|
+
if (isTTY()) {
|
|
2281
|
+
process.stderr.write(`${renderProgressBar(completed, total, name, pct)}\r`);
|
|
2282
|
+
}
|
|
2283
|
+
if (outcome.crashed) {
|
|
2284
|
+
failureCount++;
|
|
2285
|
+
const errPath = resolve5(rendersDir, `${name}.error.json`);
|
|
2286
|
+
writeFileSync5(
|
|
2287
|
+
errPath,
|
|
2288
|
+
JSON.stringify(
|
|
2289
|
+
{
|
|
2290
|
+
component: name,
|
|
2291
|
+
errorMessage: outcome.error.message,
|
|
2292
|
+
heuristicFlags: outcome.error.heuristicFlags,
|
|
2293
|
+
propsAtCrash: outcome.error.propsAtCrash
|
|
2294
|
+
},
|
|
2295
|
+
null,
|
|
2296
|
+
2
|
|
2297
|
+
),
|
|
2298
|
+
"utf-8"
|
|
2299
|
+
);
|
|
2300
|
+
return;
|
|
2301
|
+
}
|
|
2302
|
+
const result = outcome.result;
|
|
2303
|
+
writeFileSync5(resolve5(rendersDir, `${name}.png`), result.screenshot);
|
|
2304
|
+
const jsonOutput = formatRenderJson(name, {}, result);
|
|
2305
|
+
writeFileSync5(
|
|
2306
|
+
resolve5(rendersDir, `${name}.json`),
|
|
2307
|
+
JSON.stringify(jsonOutput, null, 2),
|
|
2308
|
+
"utf-8"
|
|
2309
|
+
);
|
|
2310
|
+
computedStylesMap.set(name, extractComputedStyles(result.computedStyles));
|
|
2311
|
+
};
|
|
2312
|
+
const worker = async () => {
|
|
2313
|
+
while (nextIdx < componentNames.length) {
|
|
2314
|
+
const i = nextIdx++;
|
|
2315
|
+
const name = componentNames[i];
|
|
2316
|
+
if (name !== void 0) {
|
|
2317
|
+
await renderOne(name);
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
};
|
|
2321
|
+
const workers = [];
|
|
2322
|
+
for (let w = 0; w < Math.min(CONCURRENCY, total); w++) {
|
|
2323
|
+
workers.push(worker());
|
|
2324
|
+
}
|
|
2325
|
+
await Promise.all(workers);
|
|
2326
|
+
await shutdownPool3();
|
|
2327
|
+
if (isTTY()) {
|
|
2328
|
+
process.stderr.write("\n");
|
|
2329
|
+
}
|
|
2330
|
+
const resolver = new TokenResolver([]);
|
|
2331
|
+
const engine = new ComplianceEngine(resolver);
|
|
2332
|
+
const batchReport = engine.auditBatch(computedStylesMap);
|
|
2333
|
+
writeFileSync5(
|
|
2334
|
+
resolve5(baselineDir, "compliance.json"),
|
|
2335
|
+
JSON.stringify(batchReport, null, 2),
|
|
2336
|
+
"utf-8"
|
|
2337
|
+
);
|
|
2338
|
+
const wallClockMs = performance.now() - startTime;
|
|
2339
|
+
const successCount = total - failureCount;
|
|
2340
|
+
process.stderr.write(
|
|
2341
|
+
`
|
|
2342
|
+
Baseline complete: ${successCount}/${total} components rendered` + (failureCount > 0 ? ` (${failureCount} failed)` : "") + ` in ${(wallClockMs / 1e3).toFixed(1)}s
|
|
2343
|
+
`
|
|
2344
|
+
);
|
|
2345
|
+
process.stderr.write(`Snapshot saved to ${baselineDir}
|
|
2346
|
+
`);
|
|
2347
|
+
return { baselineDir, componentCount: total, failureCount, wallClockMs };
|
|
2348
|
+
}
|
|
2349
|
+
function registerBaselineSubCommand(reportCmd) {
|
|
2350
|
+
reportCmd.command("baseline").description("Capture a baseline snapshot (manifest + renders + compliance) for later diffing").option(
|
|
2351
|
+
"-o, --output <dir>",
|
|
2352
|
+
"Output directory for the baseline snapshot",
|
|
2353
|
+
DEFAULT_BASELINE_DIR
|
|
2354
|
+
).option("--components <glob>", "Glob pattern to baseline a subset of components").option("--manifest <path>", "Path to an existing manifest.json to use instead of regenerating").option("--viewport <WxH>", "Viewport size, e.g. 1280x720", "375x812").action(
|
|
2355
|
+
async (opts) => {
|
|
2356
|
+
try {
|
|
2357
|
+
const [wStr, hStr] = opts.viewport.split("x");
|
|
2358
|
+
const viewportWidth = Number.parseInt(wStr ?? "375", 10);
|
|
2359
|
+
const viewportHeight = Number.parseInt(hStr ?? "812", 10);
|
|
2360
|
+
await runBaseline({
|
|
2361
|
+
outputDir: opts.output,
|
|
2362
|
+
componentsGlob: opts.components,
|
|
2363
|
+
manifestPath: opts.manifest,
|
|
2364
|
+
viewportWidth,
|
|
2365
|
+
viewportHeight
|
|
2366
|
+
});
|
|
2367
|
+
} catch (err) {
|
|
2368
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
|
|
2369
|
+
`);
|
|
2370
|
+
process.exit(1);
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
);
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2050
2376
|
// src/tree-formatter.ts
|
|
2051
2377
|
var BRANCH = "\u251C\u2500\u2500 ";
|
|
2052
2378
|
var LAST_BRANCH = "\u2514\u2500\u2500 ";
|
|
@@ -2327,12 +2653,12 @@ function buildStructuredReport(report) {
|
|
|
2327
2653
|
}
|
|
2328
2654
|
|
|
2329
2655
|
// src/tokens/commands.ts
|
|
2330
|
-
import { existsSync as
|
|
2331
|
-
import { resolve as
|
|
2656
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
2657
|
+
import { resolve as resolve6 } from "path";
|
|
2332
2658
|
import {
|
|
2333
2659
|
parseTokenFileSync,
|
|
2334
2660
|
TokenParseError,
|
|
2335
|
-
TokenResolver,
|
|
2661
|
+
TokenResolver as TokenResolver2,
|
|
2336
2662
|
TokenValidationError,
|
|
2337
2663
|
validateTokenFile
|
|
2338
2664
|
} from "@agent-scope/tokens";
|
|
@@ -2358,24 +2684,24 @@ function buildTable2(headers, rows) {
|
|
|
2358
2684
|
}
|
|
2359
2685
|
function resolveTokenFilePath(fileFlag) {
|
|
2360
2686
|
if (fileFlag !== void 0) {
|
|
2361
|
-
return
|
|
2687
|
+
return resolve6(process.cwd(), fileFlag);
|
|
2362
2688
|
}
|
|
2363
|
-
const configPath =
|
|
2364
|
-
if (
|
|
2689
|
+
const configPath = resolve6(process.cwd(), CONFIG_FILE);
|
|
2690
|
+
if (existsSync6(configPath)) {
|
|
2365
2691
|
try {
|
|
2366
2692
|
const raw = readFileSync5(configPath, "utf-8");
|
|
2367
2693
|
const config = JSON.parse(raw);
|
|
2368
2694
|
if (typeof config === "object" && config !== null && "tokens" in config && typeof config.tokens === "object" && config.tokens !== null && typeof config.tokens?.file === "string") {
|
|
2369
2695
|
const file = config.tokens.file;
|
|
2370
|
-
return
|
|
2696
|
+
return resolve6(process.cwd(), file);
|
|
2371
2697
|
}
|
|
2372
2698
|
} catch {
|
|
2373
2699
|
}
|
|
2374
2700
|
}
|
|
2375
|
-
return
|
|
2701
|
+
return resolve6(process.cwd(), DEFAULT_TOKEN_FILE);
|
|
2376
2702
|
}
|
|
2377
2703
|
function loadTokens(absPath) {
|
|
2378
|
-
if (!
|
|
2704
|
+
if (!existsSync6(absPath)) {
|
|
2379
2705
|
throw new Error(
|
|
2380
2706
|
`Token file not found at ${absPath}.
|
|
2381
2707
|
Create a reactscope.tokens.json file or use --file to specify a path.`
|
|
@@ -2421,7 +2747,7 @@ function registerGet2(tokensCmd) {
|
|
|
2421
2747
|
try {
|
|
2422
2748
|
const filePath = resolveTokenFilePath(opts.file);
|
|
2423
2749
|
const { tokens } = loadTokens(filePath);
|
|
2424
|
-
const resolver = new
|
|
2750
|
+
const resolver = new TokenResolver2(tokens);
|
|
2425
2751
|
const resolvedValue = resolver.resolve(tokenPath);
|
|
2426
2752
|
const useJson = opts.format === "json" || opts.format !== "text" && !isTTY2();
|
|
2427
2753
|
if (useJson) {
|
|
@@ -2447,7 +2773,7 @@ function registerList2(tokensCmd) {
|
|
|
2447
2773
|
try {
|
|
2448
2774
|
const filePath = resolveTokenFilePath(opts.file);
|
|
2449
2775
|
const { tokens } = loadTokens(filePath);
|
|
2450
|
-
const resolver = new
|
|
2776
|
+
const resolver = new TokenResolver2(tokens);
|
|
2451
2777
|
const filtered = resolver.list(opts.type, category);
|
|
2452
2778
|
const useJson = opts.format === "json" || opts.format !== "table" && !isTTY2();
|
|
2453
2779
|
if (useJson) {
|
|
@@ -2477,7 +2803,7 @@ function registerSearch(tokensCmd) {
|
|
|
2477
2803
|
try {
|
|
2478
2804
|
const filePath = resolveTokenFilePath(opts.file);
|
|
2479
2805
|
const { tokens } = loadTokens(filePath);
|
|
2480
|
-
const resolver = new
|
|
2806
|
+
const resolver = new TokenResolver2(tokens);
|
|
2481
2807
|
const useJson = opts.format === "json" || opts.format !== "table" && !isTTY2();
|
|
2482
2808
|
const typesToSearch = opts.type ? [opts.type] : [
|
|
2483
2809
|
"color",
|
|
@@ -2560,7 +2886,7 @@ function registerResolve(tokensCmd) {
|
|
|
2560
2886
|
const filePath = resolveTokenFilePath(opts.file);
|
|
2561
2887
|
const absFilePath = filePath;
|
|
2562
2888
|
const { tokens, rawFile } = loadTokens(absFilePath);
|
|
2563
|
-
const resolver = new
|
|
2889
|
+
const resolver = new TokenResolver2(tokens);
|
|
2564
2890
|
resolver.resolve(tokenPath);
|
|
2565
2891
|
const chain = buildResolutionChain(tokenPath, rawFile.tokens);
|
|
2566
2892
|
const useJson = opts.format === "json" || opts.format !== "text" && !isTTY2();
|
|
@@ -2595,7 +2921,7 @@ function registerValidate(tokensCmd) {
|
|
|
2595
2921
|
).option("--file <path>", "Path to token file (overrides config)").option("--format <fmt>", "Output format: json or text (default: auto-detect)").action((opts) => {
|
|
2596
2922
|
try {
|
|
2597
2923
|
const filePath = resolveTokenFilePath(opts.file);
|
|
2598
|
-
if (!
|
|
2924
|
+
if (!existsSync6(filePath)) {
|
|
2599
2925
|
throw new Error(
|
|
2600
2926
|
`Token file not found at ${filePath}.
|
|
2601
2927
|
Create a reactscope.tokens.json file or use --file to specify a path.`
|
|
@@ -2769,6 +3095,10 @@ function createProgram(options = {}) {
|
|
|
2769
3095
|
program2.addCommand(createTokensCommand());
|
|
2770
3096
|
program2.addCommand(createInstrumentCommand());
|
|
2771
3097
|
program2.addCommand(createInitCommand());
|
|
3098
|
+
const existingReportCmd = program2.commands.find((c) => c.name() === "report");
|
|
3099
|
+
if (existingReportCmd !== void 0) {
|
|
3100
|
+
registerBaselineSubCommand(existingReportCmd);
|
|
3101
|
+
}
|
|
2772
3102
|
return program2;
|
|
2773
3103
|
}
|
|
2774
3104
|
|