@agent-scope/cli 1.18.0 → 1.18.1
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 +373 -75
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +332 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +332 -34
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
3
|
// src/program.ts
|
|
4
|
-
import { readFileSync as
|
|
4
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
5
5
|
import { generateTest, loadTrace } from "@agent-scope/playwright";
|
|
6
6
|
import { Command as Command10 } from "commander";
|
|
7
7
|
|
|
@@ -141,6 +141,21 @@ import { createElement } from "react";
|
|
|
141
141
|
// Suppress "React must be in scope" warnings from old JSX (we use automatic)
|
|
142
142
|
banner: {
|
|
143
143
|
js: "/* @agent-scope/cli component harness */"
|
|
144
|
+
},
|
|
145
|
+
// CSS imports (e.g. `import './styles.css'`) are handled at the page level via
|
|
146
|
+
// globalCSS injection. Tell esbuild to treat CSS files as empty modules so
|
|
147
|
+
// components that import CSS directly (e.g. App.tsx) don't error during bundling.
|
|
148
|
+
loader: {
|
|
149
|
+
".css": "empty",
|
|
150
|
+
".svg": "dataurl",
|
|
151
|
+
".png": "dataurl",
|
|
152
|
+
".jpg": "dataurl",
|
|
153
|
+
".jpeg": "dataurl",
|
|
154
|
+
".gif": "dataurl",
|
|
155
|
+
".webp": "dataurl",
|
|
156
|
+
".ttf": "dataurl",
|
|
157
|
+
".woff": "dataurl",
|
|
158
|
+
".woff2": "dataurl"
|
|
144
159
|
}
|
|
145
160
|
});
|
|
146
161
|
if (result.errors.length > 0) {
|
|
@@ -553,6 +568,57 @@ async function getCompiledCssForClasses(cwd, classes) {
|
|
|
553
568
|
if (deduped.length === 0) return null;
|
|
554
569
|
return build3(deduped);
|
|
555
570
|
}
|
|
571
|
+
async function compileGlobalCssFile(cssFilePath, cwd) {
|
|
572
|
+
const { existsSync: existsSync15, readFileSync: readFileSync13 } = await import("fs");
|
|
573
|
+
const { createRequire: createRequire3 } = await import("module");
|
|
574
|
+
if (!existsSync15(cssFilePath)) return null;
|
|
575
|
+
const raw = readFileSync13(cssFilePath, "utf-8");
|
|
576
|
+
const needsCompile = /@tailwind|@import\s+['"]tailwindcss/.test(raw);
|
|
577
|
+
if (!needsCompile) {
|
|
578
|
+
return raw;
|
|
579
|
+
}
|
|
580
|
+
try {
|
|
581
|
+
const require2 = createRequire3(resolve(cwd, "package.json"));
|
|
582
|
+
let postcss;
|
|
583
|
+
let twPlugin;
|
|
584
|
+
try {
|
|
585
|
+
postcss = require2("postcss");
|
|
586
|
+
twPlugin = require2("tailwindcss");
|
|
587
|
+
} catch {
|
|
588
|
+
return raw;
|
|
589
|
+
}
|
|
590
|
+
let autoprefixerPlugin;
|
|
591
|
+
try {
|
|
592
|
+
autoprefixerPlugin = require2("autoprefixer");
|
|
593
|
+
} catch {
|
|
594
|
+
autoprefixerPlugin = null;
|
|
595
|
+
}
|
|
596
|
+
const plugins = autoprefixerPlugin ? [twPlugin, autoprefixerPlugin] : [twPlugin];
|
|
597
|
+
const result = await postcss(plugins).process(raw, {
|
|
598
|
+
from: cssFilePath,
|
|
599
|
+
to: cssFilePath
|
|
600
|
+
});
|
|
601
|
+
return result.css;
|
|
602
|
+
} catch (err) {
|
|
603
|
+
process.stderr.write(
|
|
604
|
+
`[scope/render] Warning: CSS compilation failed for ${cssFilePath}: ${err instanceof Error ? err.message : String(err)}
|
|
605
|
+
`
|
|
606
|
+
);
|
|
607
|
+
return raw;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
async function loadGlobalCss(globalCssFiles, cwd) {
|
|
611
|
+
if (globalCssFiles.length === 0) return null;
|
|
612
|
+
const parts = [];
|
|
613
|
+
for (const relPath of globalCssFiles) {
|
|
614
|
+
const absPath = resolve(cwd, relPath);
|
|
615
|
+
const css = await compileGlobalCssFile(absPath, cwd);
|
|
616
|
+
if (css !== null && css.trim().length > 0) {
|
|
617
|
+
parts.push(css);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
return parts.length > 0 ? parts.join("\n") : null;
|
|
621
|
+
}
|
|
556
622
|
|
|
557
623
|
// src/ci/commands.ts
|
|
558
624
|
var CI_EXIT = {
|
|
@@ -1065,6 +1131,20 @@ function detectComponentPatterns(rootDir, typescript) {
|
|
|
1065
1131
|
}
|
|
1066
1132
|
return unique;
|
|
1067
1133
|
}
|
|
1134
|
+
var GLOBAL_CSS_CANDIDATES = [
|
|
1135
|
+
"src/styles.css",
|
|
1136
|
+
"src/index.css",
|
|
1137
|
+
"src/global.css",
|
|
1138
|
+
"src/globals.css",
|
|
1139
|
+
"src/app.css",
|
|
1140
|
+
"src/main.css",
|
|
1141
|
+
"styles/globals.css",
|
|
1142
|
+
"styles/global.css",
|
|
1143
|
+
"styles/index.css"
|
|
1144
|
+
];
|
|
1145
|
+
function detectGlobalCSSFiles(rootDir) {
|
|
1146
|
+
return GLOBAL_CSS_CANDIDATES.filter((rel) => existsSync3(join(rootDir, rel)));
|
|
1147
|
+
}
|
|
1068
1148
|
var TAILWIND_STEMS = ["tailwind.config"];
|
|
1069
1149
|
var CSS_EXTS = [".css", ".scss", ".sass", ".less"];
|
|
1070
1150
|
var THEME_SUFFIXES = [".theme.ts", ".theme.js", ".theme.tsx"];
|
|
@@ -1132,13 +1212,15 @@ function detectProject(rootDir) {
|
|
|
1132
1212
|
const packageManager = detectPackageManager(rootDir);
|
|
1133
1213
|
const componentPatterns = detectComponentPatterns(rootDir, typescript);
|
|
1134
1214
|
const tokenSources = detectTokenSources(rootDir);
|
|
1215
|
+
const globalCSSFiles = detectGlobalCSSFiles(rootDir);
|
|
1135
1216
|
return {
|
|
1136
1217
|
framework,
|
|
1137
1218
|
typescript,
|
|
1138
1219
|
tsconfigPath,
|
|
1139
1220
|
componentPatterns,
|
|
1140
1221
|
tokenSources,
|
|
1141
|
-
packageManager
|
|
1222
|
+
packageManager,
|
|
1223
|
+
globalCSSFiles
|
|
1142
1224
|
};
|
|
1143
1225
|
}
|
|
1144
1226
|
|
|
@@ -1150,7 +1232,7 @@ function buildDefaultConfig(detected, tokenFile, outputDir) {
|
|
|
1150
1232
|
components: {
|
|
1151
1233
|
include,
|
|
1152
1234
|
exclude: ["**/*.test.tsx", "**/*.stories.tsx"],
|
|
1153
|
-
wrappers: { providers: [], globalCSS: [] }
|
|
1235
|
+
wrappers: { providers: [], globalCSS: detected.globalCSSFiles ?? [] }
|
|
1154
1236
|
},
|
|
1155
1237
|
render: {
|
|
1156
1238
|
viewport: { default: { width: 1280, height: 800 } },
|
|
@@ -1208,18 +1290,118 @@ function ensureGitignoreEntry(rootDir, entry) {
|
|
|
1208
1290
|
`);
|
|
1209
1291
|
}
|
|
1210
1292
|
}
|
|
1293
|
+
function extractTailwindTokens(tokenSources) {
|
|
1294
|
+
const tailwindSource = tokenSources.find((s) => s.kind === "tailwind-config");
|
|
1295
|
+
if (!tailwindSource) return null;
|
|
1296
|
+
try {
|
|
1297
|
+
let parseBlock2 = function(block) {
|
|
1298
|
+
const result = {};
|
|
1299
|
+
const lineRe = /['"]?(\w[\w.-]*|\d+)['"]?\s*:\s*['"]?(#[0-9a-fA-F]{3,8}|\d+(?:px|rem|em|%)|[\w-]+(?:\/[\w]+)?)['"]?/g;
|
|
1300
|
+
for (const m of block.matchAll(lineRe)) {
|
|
1301
|
+
if (m[1] !== void 0 && m[2] !== void 0) {
|
|
1302
|
+
result[m[1]] = m[2];
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
return result;
|
|
1306
|
+
};
|
|
1307
|
+
var parseBlock = parseBlock2;
|
|
1308
|
+
const raw = readFileSync4(tailwindSource.path, "utf-8");
|
|
1309
|
+
const tokens = {};
|
|
1310
|
+
const colorsKeyIdx = raw.indexOf("colors:");
|
|
1311
|
+
if (colorsKeyIdx !== -1) {
|
|
1312
|
+
const colorsBraceStart = raw.indexOf("{", colorsKeyIdx);
|
|
1313
|
+
if (colorsBraceStart !== -1) {
|
|
1314
|
+
let colorDepth = 0;
|
|
1315
|
+
let colorsBraceEnd = -1;
|
|
1316
|
+
for (let ci = colorsBraceStart; ci < raw.length; ci++) {
|
|
1317
|
+
if (raw[ci] === "{") colorDepth++;
|
|
1318
|
+
else if (raw[ci] === "}") {
|
|
1319
|
+
colorDepth--;
|
|
1320
|
+
if (colorDepth === 0) {
|
|
1321
|
+
colorsBraceEnd = ci;
|
|
1322
|
+
break;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
if (colorsBraceEnd > colorsBraceStart) {
|
|
1327
|
+
const colorSection = raw.slice(colorsBraceStart + 1, colorsBraceEnd);
|
|
1328
|
+
const scaleRe = /(\w+)\s*:\s*\{([^}]+)\}/g;
|
|
1329
|
+
const colorTokens = {};
|
|
1330
|
+
for (const sm of colorSection.matchAll(scaleRe)) {
|
|
1331
|
+
if (sm[1] === void 0 || sm[2] === void 0) continue;
|
|
1332
|
+
const scaleName = sm[1];
|
|
1333
|
+
const scaleValues = parseBlock2(sm[2]);
|
|
1334
|
+
if (Object.keys(scaleValues).length > 0) {
|
|
1335
|
+
const scaleTokens = {};
|
|
1336
|
+
for (const [step, hex] of Object.entries(scaleValues)) {
|
|
1337
|
+
scaleTokens[step] = { value: hex, type: "color" };
|
|
1338
|
+
}
|
|
1339
|
+
colorTokens[scaleName] = scaleTokens;
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
if (Object.keys(colorTokens).length > 0) {
|
|
1343
|
+
tokens["color"] = colorTokens;
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
const spacingMatch = raw.match(/spacing\s*:\s*\{([\s\S]*?)\n\s*\}/);
|
|
1349
|
+
if (spacingMatch?.[1] !== void 0) {
|
|
1350
|
+
const spacingValues = parseBlock2(spacingMatch[1]);
|
|
1351
|
+
if (Object.keys(spacingValues).length > 0) {
|
|
1352
|
+
const spacingTokens = {};
|
|
1353
|
+
for (const [key, val] of Object.entries(spacingValues)) {
|
|
1354
|
+
spacingTokens[key] = { value: val, type: "dimension" };
|
|
1355
|
+
}
|
|
1356
|
+
tokens["spacing"] = spacingTokens;
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
const fontFamilyMatch = raw.match(/fontFamily\s*:\s*\{([\s\S]*?)\n\s*\}/);
|
|
1360
|
+
if (fontFamilyMatch?.[1] !== void 0) {
|
|
1361
|
+
const fontFamilyRe = /(\w+)\s*:\s*\[\s*['"]([^'"]+)['"]/g;
|
|
1362
|
+
const fontTokens = {};
|
|
1363
|
+
for (const fm of fontFamilyMatch[1].matchAll(fontFamilyRe)) {
|
|
1364
|
+
if (fm[1] !== void 0 && fm[2] !== void 0) {
|
|
1365
|
+
fontTokens[fm[1]] = { value: fm[2], type: "fontFamily" };
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
if (Object.keys(fontTokens).length > 0) {
|
|
1369
|
+
tokens["font"] = fontTokens;
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
const borderRadiusMatch = raw.match(/borderRadius\s*:\s*\{([\s\S]*?)\n\s*\}/);
|
|
1373
|
+
if (borderRadiusMatch?.[1] !== void 0) {
|
|
1374
|
+
const radiusValues = parseBlock2(borderRadiusMatch[1]);
|
|
1375
|
+
if (Object.keys(radiusValues).length > 0) {
|
|
1376
|
+
const radiusTokens = {};
|
|
1377
|
+
for (const [key, val] of Object.entries(radiusValues)) {
|
|
1378
|
+
radiusTokens[key] = { value: val, type: "dimension" };
|
|
1379
|
+
}
|
|
1380
|
+
tokens["radius"] = radiusTokens;
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
return Object.keys(tokens).length > 0 ? tokens : null;
|
|
1384
|
+
} catch {
|
|
1385
|
+
return null;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1211
1388
|
function scaffoldConfig(rootDir, config) {
|
|
1212
1389
|
const path = join2(rootDir, "reactscope.config.json");
|
|
1213
1390
|
writeFileSync3(path, `${JSON.stringify(config, null, 2)}
|
|
1214
1391
|
`);
|
|
1215
1392
|
return path;
|
|
1216
1393
|
}
|
|
1217
|
-
function scaffoldTokenFile(rootDir, tokenFile) {
|
|
1394
|
+
function scaffoldTokenFile(rootDir, tokenFile, extractedTokens) {
|
|
1218
1395
|
const path = join2(rootDir, tokenFile);
|
|
1219
1396
|
if (!existsSync4(path)) {
|
|
1220
1397
|
const stub = {
|
|
1221
1398
|
$schema: "https://raw.githubusercontent.com/FlatFilers/Scope/main/packages/tokens/schema.json",
|
|
1222
|
-
|
|
1399
|
+
version: "1.0.0",
|
|
1400
|
+
meta: {
|
|
1401
|
+
name: "Design Tokens",
|
|
1402
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
|
|
1403
|
+
},
|
|
1404
|
+
tokens: extractedTokens ?? {}
|
|
1223
1405
|
};
|
|
1224
1406
|
writeFileSync3(path, `${JSON.stringify(stub, null, 2)}
|
|
1225
1407
|
`);
|
|
@@ -1297,7 +1479,13 @@ async function runInit(options) {
|
|
|
1297
1479
|
}
|
|
1298
1480
|
const cfgPath = scaffoldConfig(rootDir, config);
|
|
1299
1481
|
created.push(cfgPath);
|
|
1300
|
-
const
|
|
1482
|
+
const extractedTokens = extractTailwindTokens(detected.tokenSources);
|
|
1483
|
+
if (extractedTokens !== null) {
|
|
1484
|
+
const tokenGroupCount = Object.keys(extractedTokens).length;
|
|
1485
|
+
process.stdout.write(` Extracted ${tokenGroupCount} token group(s) from Tailwind config
|
|
1486
|
+
`);
|
|
1487
|
+
}
|
|
1488
|
+
const tokPath = scaffoldTokenFile(rootDir, config.tokens.file, extractedTokens ?? void 0);
|
|
1301
1489
|
created.push(tokPath);
|
|
1302
1490
|
const outDirPath = scaffoldOutputDir(rootDir, config.output.dir);
|
|
1303
1491
|
created.push(outDirPath);
|
|
@@ -1409,7 +1597,10 @@ Available: ${available}${hint}`
|
|
|
1409
1597
|
});
|
|
1410
1598
|
}
|
|
1411
1599
|
function registerQuery(manifestCmd) {
|
|
1412
|
-
manifestCmd.command("query").description("Query components by attributes").option("--context <name>", "Find components consuming a context").option("--hook <name>", "Find components using a specific hook").option("--complexity <class>", "Filter by complexity class: simple or complex").option("--side-effects", "Find components with any side effects", false).option("--has-fetch", "Find components with fetch calls", false).option(
|
|
1600
|
+
manifestCmd.command("query").description("Query components by attributes").option("--context <name>", "Find components consuming a context").option("--hook <name>", "Find components using a specific hook").option("--complexity <class>", "Filter by complexity class: simple or complex").option("--side-effects", "Find components with any side effects", false).option("--has-fetch", "Find components with fetch calls", false).option(
|
|
1601
|
+
"--has-prop <spec>",
|
|
1602
|
+
"Find components with a prop matching name or name:type (e.g. 'loading' or 'variant:union')"
|
|
1603
|
+
).option("--composed-by <name>", "Find components that compose the named component").option("--format <fmt>", "Output format: json or table (default: auto-detect)").option("--manifest <path>", "Path to manifest.json", MANIFEST_PATH).action(
|
|
1413
1604
|
(opts) => {
|
|
1414
1605
|
try {
|
|
1415
1606
|
const manifest = loadManifest(opts.manifest);
|
|
@@ -1420,9 +1611,11 @@ function registerQuery(manifestCmd) {
|
|
|
1420
1611
|
if (opts.complexity !== void 0) queryParts.push(`complexity=${opts.complexity}`);
|
|
1421
1612
|
if (opts.sideEffects) queryParts.push("side-effects");
|
|
1422
1613
|
if (opts.hasFetch) queryParts.push("has-fetch");
|
|
1614
|
+
if (opts.hasProp !== void 0) queryParts.push(`has-prop=${opts.hasProp}`);
|
|
1615
|
+
if (opts.composedBy !== void 0) queryParts.push(`composed-by=${opts.composedBy}`);
|
|
1423
1616
|
if (queryParts.length === 0) {
|
|
1424
1617
|
process.stderr.write(
|
|
1425
|
-
"No query flags specified. Use --context, --hook, --complexity, --side-effects,
|
|
1618
|
+
"No query flags specified. Use --context, --hook, --complexity, --side-effects, --has-fetch, --has-prop, or --composed-by.\n"
|
|
1426
1619
|
);
|
|
1427
1620
|
process.exit(1);
|
|
1428
1621
|
}
|
|
@@ -1449,6 +1642,27 @@ function registerQuery(manifestCmd) {
|
|
|
1449
1642
|
if (opts.hasFetch) {
|
|
1450
1643
|
entries = entries.filter(([, d]) => d.sideEffects.fetches.length > 0);
|
|
1451
1644
|
}
|
|
1645
|
+
if (opts.hasProp !== void 0) {
|
|
1646
|
+
const spec = opts.hasProp;
|
|
1647
|
+
const colonIdx = spec.indexOf(":");
|
|
1648
|
+
const propName = colonIdx >= 0 ? spec.slice(0, colonIdx) : spec;
|
|
1649
|
+
const propType = colonIdx >= 0 ? spec.slice(colonIdx + 1) : void 0;
|
|
1650
|
+
entries = entries.filter(([, d]) => {
|
|
1651
|
+
const props = d.props;
|
|
1652
|
+
if (!props || !(propName in props)) return false;
|
|
1653
|
+
if (propType !== void 0) {
|
|
1654
|
+
return props[propName]?.type === propType;
|
|
1655
|
+
}
|
|
1656
|
+
return true;
|
|
1657
|
+
});
|
|
1658
|
+
}
|
|
1659
|
+
if (opts.composedBy !== void 0) {
|
|
1660
|
+
const targetName = opts.composedBy;
|
|
1661
|
+
entries = entries.filter(([, d]) => {
|
|
1662
|
+
const composedBy = d.composedBy;
|
|
1663
|
+
return composedBy !== void 0 && composedBy.includes(targetName);
|
|
1664
|
+
});
|
|
1665
|
+
}
|
|
1452
1666
|
const rows = entries.map(([name, d]) => ({
|
|
1453
1667
|
name,
|
|
1454
1668
|
file: d.filePath,
|
|
@@ -3001,7 +3215,7 @@ function createInstrumentCommand() {
|
|
|
3001
3215
|
}
|
|
3002
3216
|
|
|
3003
3217
|
// src/render-commands.ts
|
|
3004
|
-
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
3218
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
3005
3219
|
import { resolve as resolve9 } from "path";
|
|
3006
3220
|
import {
|
|
3007
3221
|
ALL_CONTEXT_IDS,
|
|
@@ -3137,6 +3351,17 @@ ${msg}`);
|
|
|
3137
3351
|
}
|
|
3138
3352
|
|
|
3139
3353
|
// src/render-commands.ts
|
|
3354
|
+
function loadGlobalCssFilesFromConfig(cwd) {
|
|
3355
|
+
const configPath = resolve9(cwd, "reactscope.config.json");
|
|
3356
|
+
if (!existsSync7(configPath)) return [];
|
|
3357
|
+
try {
|
|
3358
|
+
const raw = readFileSync6(configPath, "utf-8");
|
|
3359
|
+
const cfg = JSON.parse(raw);
|
|
3360
|
+
return cfg.components?.wrappers?.globalCSS ?? [];
|
|
3361
|
+
} catch {
|
|
3362
|
+
return [];
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3140
3365
|
var MANIFEST_PATH6 = ".reactscope/manifest.json";
|
|
3141
3366
|
var DEFAULT_OUTPUT_DIR = ".reactscope/renders";
|
|
3142
3367
|
var _pool3 = null;
|
|
@@ -3157,7 +3382,7 @@ async function shutdownPool3() {
|
|
|
3157
3382
|
_pool3 = null;
|
|
3158
3383
|
}
|
|
3159
3384
|
}
|
|
3160
|
-
function buildRenderer(filePath, componentName, viewportWidth, viewportHeight, wrapperScript) {
|
|
3385
|
+
function buildRenderer(filePath, componentName, viewportWidth, viewportHeight, globalCssFiles = [], projectCwd = process.cwd(), wrapperScript) {
|
|
3161
3386
|
const satori = new SatoriRenderer({
|
|
3162
3387
|
defaultViewport: { width: viewportWidth, height: viewportHeight }
|
|
3163
3388
|
});
|
|
@@ -3166,13 +3391,13 @@ function buildRenderer(filePath, componentName, viewportWidth, viewportHeight, w
|
|
|
3166
3391
|
async renderCell(props, _complexityClass) {
|
|
3167
3392
|
const startMs = performance.now();
|
|
3168
3393
|
const pool = await getPool3(viewportWidth, viewportHeight);
|
|
3394
|
+
const projectCss = await loadGlobalCss(globalCssFiles, projectCwd);
|
|
3169
3395
|
const htmlHarness = await buildComponentHarness(
|
|
3170
3396
|
filePath,
|
|
3171
3397
|
componentName,
|
|
3172
3398
|
props,
|
|
3173
3399
|
viewportWidth,
|
|
3174
|
-
void 0,
|
|
3175
|
-
// projectCss (handled separately)
|
|
3400
|
+
projectCss ?? void 0,
|
|
3176
3401
|
wrapperScript
|
|
3177
3402
|
);
|
|
3178
3403
|
const slot = await pool.acquire();
|
|
@@ -3202,9 +3427,9 @@ function buildRenderer(filePath, componentName, viewportWidth, viewportHeight, w
|
|
|
3202
3427
|
});
|
|
3203
3428
|
return [...set];
|
|
3204
3429
|
});
|
|
3205
|
-
const
|
|
3206
|
-
if (
|
|
3207
|
-
await page.addStyleTag({ content:
|
|
3430
|
+
const projectCss2 = await getCompiledCssForClasses(rootDir, classes);
|
|
3431
|
+
if (projectCss2 != null && projectCss2.length > 0) {
|
|
3432
|
+
await page.addStyleTag({ content: projectCss2 });
|
|
3208
3433
|
}
|
|
3209
3434
|
const renderTimeMs = performance.now() - startMs;
|
|
3210
3435
|
const rootLocator = page.locator("[data-reactscope-root]");
|
|
@@ -3306,26 +3531,59 @@ function registerRenderSingle(renderCmd) {
|
|
|
3306
3531
|
Available: ${available}`
|
|
3307
3532
|
);
|
|
3308
3533
|
}
|
|
3534
|
+
let props = {};
|
|
3535
|
+
if (opts.props !== void 0) {
|
|
3536
|
+
try {
|
|
3537
|
+
props = JSON.parse(opts.props);
|
|
3538
|
+
} catch {
|
|
3539
|
+
throw new Error(`Invalid props JSON: ${opts.props}`);
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3542
|
+
if (descriptor.props !== void 0) {
|
|
3543
|
+
const propDefs = descriptor.props;
|
|
3544
|
+
for (const [propName, propDef] of Object.entries(propDefs)) {
|
|
3545
|
+
if (propName in props) continue;
|
|
3546
|
+
if (!propDef.required && propDef.default !== void 0) continue;
|
|
3547
|
+
if (propDef.type === "node" || propDef.type === "string") {
|
|
3548
|
+
props[propName] = propName === "children" ? componentName : propName;
|
|
3549
|
+
} else if (propDef.type === "union" && propDef.values && propDef.values.length > 0) {
|
|
3550
|
+
props[propName] = propDef.values[0];
|
|
3551
|
+
} else if (propDef.type === "boolean") {
|
|
3552
|
+
props[propName] = false;
|
|
3553
|
+
} else if (propDef.type === "number") {
|
|
3554
|
+
props[propName] = 0;
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3309
3558
|
const { width, height } = parseViewport(opts.viewport);
|
|
3310
3559
|
const rootDir = process.cwd();
|
|
3311
3560
|
const filePath = resolve9(rootDir, descriptor.filePath);
|
|
3312
3561
|
const scopeData = await loadScopeFileForComponent(filePath);
|
|
3313
3562
|
const wrapperScript = scopeData?.hasWrapper === true ? await buildWrapperScript(scopeData.filePath) : void 0;
|
|
3314
3563
|
const scenarios = buildScenarioMap(opts, scopeData);
|
|
3315
|
-
const
|
|
3564
|
+
const globalCssFiles = loadGlobalCssFilesFromConfig(rootDir);
|
|
3565
|
+
const renderer = buildRenderer(
|
|
3566
|
+
filePath,
|
|
3567
|
+
componentName,
|
|
3568
|
+
width,
|
|
3569
|
+
height,
|
|
3570
|
+
globalCssFiles,
|
|
3571
|
+
rootDir,
|
|
3572
|
+
wrapperScript
|
|
3573
|
+
);
|
|
3316
3574
|
process.stderr.write(
|
|
3317
3575
|
`Rendering ${componentName} [${descriptor.complexityClass}] at ${width}\xD7${height}\u2026
|
|
3318
3576
|
`
|
|
3319
3577
|
);
|
|
3320
3578
|
const fmt2 = resolveSingleFormat(opts.format);
|
|
3321
3579
|
let anyFailed = false;
|
|
3322
|
-
for (const [scenarioName,
|
|
3580
|
+
for (const [scenarioName, props2] of Object.entries(scenarios)) {
|
|
3323
3581
|
const isNamed = scenarioName !== "__default__";
|
|
3324
3582
|
const label = isNamed ? `${componentName}:${scenarioName}` : componentName;
|
|
3325
3583
|
const outcome = await safeRender2(
|
|
3326
|
-
() => renderer.renderCell(
|
|
3584
|
+
() => renderer.renderCell(props2, descriptor.complexityClass),
|
|
3327
3585
|
{
|
|
3328
|
-
props,
|
|
3586
|
+
props: props2,
|
|
3329
3587
|
sourceLocation: {
|
|
3330
3588
|
file: descriptor.filePath,
|
|
3331
3589
|
line: descriptor.loc.start,
|
|
@@ -3354,7 +3612,7 @@ Available: ${available}`
|
|
|
3354
3612
|
`
|
|
3355
3613
|
);
|
|
3356
3614
|
} else if (fmt2 === "json") {
|
|
3357
|
-
const json = formatRenderJson(label,
|
|
3615
|
+
const json = formatRenderJson(label, props2, result);
|
|
3358
3616
|
process.stdout.write(`${JSON.stringify(json, null, 2)}
|
|
3359
3617
|
`);
|
|
3360
3618
|
} else {
|
|
@@ -3381,7 +3639,10 @@ Available: ${available}`
|
|
|
3381
3639
|
);
|
|
3382
3640
|
}
|
|
3383
3641
|
function registerRenderMatrix(renderCmd) {
|
|
3384
|
-
renderCmd.command("matrix <component>").description("Render a component across a matrix of prop axes").option(
|
|
3642
|
+
renderCmd.command("matrix <component>").description("Render a component across a matrix of prop axes").option(
|
|
3643
|
+
"--axes <spec>",
|
|
3644
|
+
`Axis definitions: key:v1,v2 space-separated OR JSON object e.g. 'variant:primary,ghost size:sm,lg' or '{"variant":["primary","ghost"],"size":["sm","lg"]}'`
|
|
3645
|
+
).option(
|
|
3385
3646
|
"--contexts <ids>",
|
|
3386
3647
|
"Composition context IDs, comma-separated (e.g. centered,rtl,sidebar)"
|
|
3387
3648
|
).option("--stress <ids>", "Stress preset IDs, comma-separated (e.g. text.long,text.unicode)").option("--sprite <path>", "Write sprite sheet PNG to file").option("--format <fmt>", "Output format: json|png|html|csv (default: auto)").option("--concurrency <n>", "Max parallel renders", "8").option("--manifest <path>", "Path to manifest.json", MANIFEST_PATH6).action(
|
|
@@ -3400,21 +3661,47 @@ Available: ${available}`
|
|
|
3400
3661
|
const { width, height } = { width: 375, height: 812 };
|
|
3401
3662
|
const rootDir = process.cwd();
|
|
3402
3663
|
const filePath = resolve9(rootDir, descriptor.filePath);
|
|
3403
|
-
const
|
|
3664
|
+
const matrixCssFiles = loadGlobalCssFilesFromConfig(rootDir);
|
|
3665
|
+
const renderer = buildRenderer(
|
|
3666
|
+
filePath,
|
|
3667
|
+
componentName,
|
|
3668
|
+
width,
|
|
3669
|
+
height,
|
|
3670
|
+
matrixCssFiles,
|
|
3671
|
+
rootDir
|
|
3672
|
+
);
|
|
3404
3673
|
const axes = [];
|
|
3405
3674
|
if (opts.axes !== void 0) {
|
|
3406
|
-
const
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3675
|
+
const axesRaw = opts.axes.trim();
|
|
3676
|
+
if (axesRaw.startsWith("{")) {
|
|
3677
|
+
let parsed;
|
|
3678
|
+
try {
|
|
3679
|
+
parsed = JSON.parse(axesRaw);
|
|
3680
|
+
} catch {
|
|
3681
|
+
throw new Error(`Invalid JSON in --axes: ${axesRaw}`);
|
|
3411
3682
|
}
|
|
3412
|
-
const name
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3683
|
+
for (const [name, vals] of Object.entries(parsed)) {
|
|
3684
|
+
if (!Array.isArray(vals)) {
|
|
3685
|
+
throw new Error(`Axis "${name}" must be an array of values in JSON format`);
|
|
3686
|
+
}
|
|
3687
|
+
axes.push({ name, values: vals.map(String) });
|
|
3688
|
+
}
|
|
3689
|
+
} else {
|
|
3690
|
+
const axisSpecs = axesRaw.split(/\s+/);
|
|
3691
|
+
for (const spec of axisSpecs) {
|
|
3692
|
+
const colonIdx = spec.indexOf(":");
|
|
3693
|
+
if (colonIdx < 0) {
|
|
3694
|
+
throw new Error(
|
|
3695
|
+
`Invalid axis spec "${spec}". Expected format: name:val1,val2,...`
|
|
3696
|
+
);
|
|
3697
|
+
}
|
|
3698
|
+
const name = spec.slice(0, colonIdx);
|
|
3699
|
+
const values = spec.slice(colonIdx + 1).split(",").map((v) => v.trim());
|
|
3700
|
+
if (name.length === 0 || values.length === 0) {
|
|
3701
|
+
throw new Error(`Invalid axis spec "${spec}"`);
|
|
3702
|
+
}
|
|
3703
|
+
axes.push({ name, values });
|
|
3416
3704
|
}
|
|
3417
|
-
axes.push({ name, values });
|
|
3418
3705
|
}
|
|
3419
3706
|
}
|
|
3420
3707
|
if (opts.contexts !== void 0) {
|
|
@@ -3533,7 +3820,8 @@ function registerRenderAll(renderCmd) {
|
|
|
3533
3820
|
const descriptor = manifest.components[name];
|
|
3534
3821
|
if (descriptor === void 0) return;
|
|
3535
3822
|
const filePath = resolve9(rootDir, descriptor.filePath);
|
|
3536
|
-
const
|
|
3823
|
+
const allCssFiles = loadGlobalCssFilesFromConfig(process.cwd());
|
|
3824
|
+
const renderer = buildRenderer(filePath, name, 375, 812, allCssFiles, process.cwd());
|
|
3537
3825
|
const outcome = await safeRender2(
|
|
3538
3826
|
() => renderer.renderCell({}, descriptor.complexityClass),
|
|
3539
3827
|
{
|
|
@@ -3648,7 +3936,7 @@ function createRenderCommand() {
|
|
|
3648
3936
|
}
|
|
3649
3937
|
|
|
3650
3938
|
// src/report/baseline.ts
|
|
3651
|
-
import { existsSync as
|
|
3939
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
3652
3940
|
import { resolve as resolve10 } from "path";
|
|
3653
3941
|
import { generateManifest as generateManifest3 } from "@agent-scope/manifest";
|
|
3654
3942
|
import { BrowserPool as BrowserPool4, safeRender as safeRender3 } from "@agent-scope/render";
|
|
@@ -3800,18 +4088,18 @@ async function runBaseline(options = {}) {
|
|
|
3800
4088
|
const rootDir = process.cwd();
|
|
3801
4089
|
const baselineDir = resolve10(rootDir, outputDir);
|
|
3802
4090
|
const rendersDir = resolve10(baselineDir, "renders");
|
|
3803
|
-
if (
|
|
4091
|
+
if (existsSync8(baselineDir)) {
|
|
3804
4092
|
rmSync2(baselineDir, { recursive: true, force: true });
|
|
3805
4093
|
}
|
|
3806
4094
|
mkdirSync5(rendersDir, { recursive: true });
|
|
3807
4095
|
let manifest;
|
|
3808
4096
|
if (manifestPath !== void 0) {
|
|
3809
|
-
const { readFileSync:
|
|
4097
|
+
const { readFileSync: readFileSync13 } = await import("fs");
|
|
3810
4098
|
const absPath = resolve10(rootDir, manifestPath);
|
|
3811
|
-
if (!
|
|
4099
|
+
if (!existsSync8(absPath)) {
|
|
3812
4100
|
throw new Error(`Manifest not found at ${absPath}.`);
|
|
3813
4101
|
}
|
|
3814
|
-
manifest = JSON.parse(
|
|
4102
|
+
manifest = JSON.parse(readFileSync13(absPath, "utf-8"));
|
|
3815
4103
|
process.stderr.write(`Loaded manifest from ${manifestPath}
|
|
3816
4104
|
`);
|
|
3817
4105
|
} else {
|
|
@@ -3974,7 +4262,7 @@ function registerBaselineSubCommand(reportCmd) {
|
|
|
3974
4262
|
}
|
|
3975
4263
|
|
|
3976
4264
|
// src/report/diff.ts
|
|
3977
|
-
import { existsSync as
|
|
4265
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "fs";
|
|
3978
4266
|
import { resolve as resolve11 } from "path";
|
|
3979
4267
|
import { generateManifest as generateManifest4 } from "@agent-scope/manifest";
|
|
3980
4268
|
import { BrowserPool as BrowserPool5, safeRender as safeRender4 } from "@agent-scope/render";
|
|
@@ -3982,14 +4270,14 @@ import { ComplianceEngine as ComplianceEngine3, TokenResolver as TokenResolver3
|
|
|
3982
4270
|
var DEFAULT_BASELINE_DIR2 = ".reactscope/baseline";
|
|
3983
4271
|
function loadBaselineCompliance(baselineDir) {
|
|
3984
4272
|
const compliancePath = resolve11(baselineDir, "compliance.json");
|
|
3985
|
-
if (!
|
|
3986
|
-
const raw = JSON.parse(
|
|
4273
|
+
if (!existsSync9(compliancePath)) return null;
|
|
4274
|
+
const raw = JSON.parse(readFileSync7(compliancePath, "utf-8"));
|
|
3987
4275
|
return raw;
|
|
3988
4276
|
}
|
|
3989
4277
|
function loadBaselineRenderJson2(baselineDir, componentName) {
|
|
3990
4278
|
const jsonPath = resolve11(baselineDir, "renders", `${componentName}.json`);
|
|
3991
|
-
if (!
|
|
3992
|
-
return JSON.parse(
|
|
4279
|
+
if (!existsSync9(jsonPath)) return null;
|
|
4280
|
+
return JSON.parse(readFileSync7(jsonPath, "utf-8"));
|
|
3993
4281
|
}
|
|
3994
4282
|
var _pool5 = null;
|
|
3995
4283
|
async function getPool5(viewportWidth, viewportHeight) {
|
|
@@ -4157,18 +4445,18 @@ async function runDiff(options = {}) {
|
|
|
4157
4445
|
const startTime = performance.now();
|
|
4158
4446
|
const rootDir = process.cwd();
|
|
4159
4447
|
const baselineDir = resolve11(rootDir, baselineDirRaw);
|
|
4160
|
-
if (!
|
|
4448
|
+
if (!existsSync9(baselineDir)) {
|
|
4161
4449
|
throw new Error(
|
|
4162
4450
|
`Baseline directory not found at "${baselineDir}". Run \`scope report baseline\` first to create a baseline snapshot.`
|
|
4163
4451
|
);
|
|
4164
4452
|
}
|
|
4165
4453
|
const baselineManifestPath = resolve11(baselineDir, "manifest.json");
|
|
4166
|
-
if (!
|
|
4454
|
+
if (!existsSync9(baselineManifestPath)) {
|
|
4167
4455
|
throw new Error(
|
|
4168
4456
|
`Baseline manifest.json not found at "${baselineManifestPath}". The baseline directory may be incomplete \u2014 re-run \`scope report baseline\`.`
|
|
4169
4457
|
);
|
|
4170
4458
|
}
|
|
4171
|
-
const baselineManifest = JSON.parse(
|
|
4459
|
+
const baselineManifest = JSON.parse(readFileSync7(baselineManifestPath, "utf-8"));
|
|
4172
4460
|
const baselineCompliance = loadBaselineCompliance(baselineDir);
|
|
4173
4461
|
const baselineComponentNames = new Set(Object.keys(baselineManifest.components));
|
|
4174
4462
|
process.stderr.write(
|
|
@@ -4178,10 +4466,10 @@ async function runDiff(options = {}) {
|
|
|
4178
4466
|
let currentManifest;
|
|
4179
4467
|
if (manifestPath !== void 0) {
|
|
4180
4468
|
const absPath = resolve11(rootDir, manifestPath);
|
|
4181
|
-
if (!
|
|
4469
|
+
if (!existsSync9(absPath)) {
|
|
4182
4470
|
throw new Error(`Manifest not found at "${absPath}".`);
|
|
4183
4471
|
}
|
|
4184
|
-
currentManifest = JSON.parse(
|
|
4472
|
+
currentManifest = JSON.parse(readFileSync7(absPath, "utf-8"));
|
|
4185
4473
|
process.stderr.write(`Loaded manifest from ${manifestPath}
|
|
4186
4474
|
`);
|
|
4187
4475
|
} else {
|
|
@@ -4454,7 +4742,7 @@ function registerDiffSubCommand(reportCmd) {
|
|
|
4454
4742
|
}
|
|
4455
4743
|
|
|
4456
4744
|
// src/report/pr-comment.ts
|
|
4457
|
-
import { existsSync as
|
|
4745
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync8 } from "fs";
|
|
4458
4746
|
import { resolve as resolve12 } from "path";
|
|
4459
4747
|
var STATUS_BADGE = {
|
|
4460
4748
|
added: "\u2705 added",
|
|
@@ -4539,12 +4827,12 @@ function formatPrComment(diff) {
|
|
|
4539
4827
|
}
|
|
4540
4828
|
function loadDiffResult(filePath) {
|
|
4541
4829
|
const abs = resolve12(filePath);
|
|
4542
|
-
if (!
|
|
4830
|
+
if (!existsSync10(abs)) {
|
|
4543
4831
|
throw new Error(`DiffResult file not found: ${abs}`);
|
|
4544
4832
|
}
|
|
4545
4833
|
let raw;
|
|
4546
4834
|
try {
|
|
4547
|
-
raw =
|
|
4835
|
+
raw = readFileSync8(abs, "utf-8");
|
|
4548
4836
|
} catch (err) {
|
|
4549
4837
|
throw new Error(
|
|
4550
4838
|
`Failed to read DiffResult file: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -4866,7 +5154,7 @@ function buildStructuredReport(report) {
|
|
|
4866
5154
|
}
|
|
4867
5155
|
|
|
4868
5156
|
// src/site-commands.ts
|
|
4869
|
-
import { createReadStream, existsSync as
|
|
5157
|
+
import { createReadStream, existsSync as existsSync11, statSync } from "fs";
|
|
4870
5158
|
import { createServer } from "http";
|
|
4871
5159
|
import { extname, join as join4, resolve as resolve13 } from "path";
|
|
4872
5160
|
import { buildSite } from "@agent-scope/site";
|
|
@@ -4888,14 +5176,14 @@ function registerBuild(siteCmd) {
|
|
|
4888
5176
|
try {
|
|
4889
5177
|
const inputDir = resolve13(process.cwd(), opts.input);
|
|
4890
5178
|
const outputDir = resolve13(process.cwd(), opts.output);
|
|
4891
|
-
if (!
|
|
5179
|
+
if (!existsSync11(inputDir)) {
|
|
4892
5180
|
throw new Error(
|
|
4893
5181
|
`Input directory not found: ${inputDir}
|
|
4894
5182
|
Run \`scope manifest generate\` and \`scope render\` first.`
|
|
4895
5183
|
);
|
|
4896
5184
|
}
|
|
4897
5185
|
const manifestPath = join4(inputDir, "manifest.json");
|
|
4898
|
-
if (!
|
|
5186
|
+
if (!existsSync11(manifestPath)) {
|
|
4899
5187
|
throw new Error(
|
|
4900
5188
|
`Manifest not found at ${manifestPath}
|
|
4901
5189
|
Run \`scope manifest generate\` first.`
|
|
@@ -4932,7 +5220,7 @@ function registerServe(siteCmd) {
|
|
|
4932
5220
|
throw new Error(`Invalid port: ${opts.port}`);
|
|
4933
5221
|
}
|
|
4934
5222
|
const serveDir = resolve13(process.cwd(), opts.dir);
|
|
4935
|
-
if (!
|
|
5223
|
+
if (!existsSync11(serveDir)) {
|
|
4936
5224
|
throw new Error(
|
|
4937
5225
|
`Serve directory not found: ${serveDir}
|
|
4938
5226
|
Run \`scope site build\` first.`
|
|
@@ -4947,7 +5235,7 @@ Run \`scope site build\` first.`
|
|
|
4947
5235
|
res.end("Forbidden");
|
|
4948
5236
|
return;
|
|
4949
5237
|
}
|
|
4950
|
-
if (
|
|
5238
|
+
if (existsSync11(filePath) && statSync(filePath).isFile()) {
|
|
4951
5239
|
const ext = extname(filePath).toLowerCase();
|
|
4952
5240
|
const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
4953
5241
|
res.writeHead(200, { "Content-Type": contentType });
|
|
@@ -4955,7 +5243,7 @@ Run \`scope site build\` first.`
|
|
|
4955
5243
|
return;
|
|
4956
5244
|
}
|
|
4957
5245
|
const htmlPath = `${filePath}.html`;
|
|
4958
|
-
if (
|
|
5246
|
+
if (existsSync11(htmlPath) && statSync(htmlPath).isFile()) {
|
|
4959
5247
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
4960
5248
|
createReadStream(htmlPath).pipe(res);
|
|
4961
5249
|
return;
|
|
@@ -4997,7 +5285,7 @@ function createSiteCommand() {
|
|
|
4997
5285
|
}
|
|
4998
5286
|
|
|
4999
5287
|
// src/tokens/commands.ts
|
|
5000
|
-
import { existsSync as
|
|
5288
|
+
import { existsSync as existsSync14, readFileSync as readFileSync11 } from "fs";
|
|
5001
5289
|
import { resolve as resolve17 } from "path";
|
|
5002
5290
|
import {
|
|
5003
5291
|
parseTokenFileSync as parseTokenFileSync2,
|
|
@@ -5009,7 +5297,7 @@ import {
|
|
|
5009
5297
|
import { Command as Command9 } from "commander";
|
|
5010
5298
|
|
|
5011
5299
|
// src/tokens/compliance.ts
|
|
5012
|
-
import { existsSync as
|
|
5300
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
|
|
5013
5301
|
import { resolve as resolve14 } from "path";
|
|
5014
5302
|
import {
|
|
5015
5303
|
ComplianceEngine as ComplianceEngine4,
|
|
@@ -5018,14 +5306,14 @@ import {
|
|
|
5018
5306
|
var DEFAULT_STYLES_PATH = ".reactscope/compliance-styles.json";
|
|
5019
5307
|
function loadStylesFile(stylesPath) {
|
|
5020
5308
|
const absPath = resolve14(process.cwd(), stylesPath);
|
|
5021
|
-
if (!
|
|
5309
|
+
if (!existsSync12(absPath)) {
|
|
5022
5310
|
throw new Error(
|
|
5023
5311
|
`Compliance styles file not found at ${absPath}.
|
|
5024
5312
|
Run \`scope render all\` first to generate component styles, or use --styles to specify a path.
|
|
5025
5313
|
Expected format: { "ComponentName": { colors: {}, spacing: {}, typography: {}, borders: {}, shadows: {} } }`
|
|
5026
5314
|
);
|
|
5027
5315
|
}
|
|
5028
|
-
const raw =
|
|
5316
|
+
const raw = readFileSync9(absPath, "utf-8");
|
|
5029
5317
|
let parsed;
|
|
5030
5318
|
try {
|
|
5031
5319
|
parsed = JSON.parse(raw);
|
|
@@ -5185,7 +5473,7 @@ function registerCompliance(tokensCmd) {
|
|
|
5185
5473
|
}
|
|
5186
5474
|
|
|
5187
5475
|
// src/tokens/export.ts
|
|
5188
|
-
import { existsSync as
|
|
5476
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10, writeFileSync as writeFileSync9 } from "fs";
|
|
5189
5477
|
import { resolve as resolve15 } from "path";
|
|
5190
5478
|
import {
|
|
5191
5479
|
exportTokens,
|
|
@@ -5202,9 +5490,9 @@ function resolveTokenFilePath2(fileFlag) {
|
|
|
5202
5490
|
return resolve15(process.cwd(), fileFlag);
|
|
5203
5491
|
}
|
|
5204
5492
|
const configPath = resolve15(process.cwd(), CONFIG_FILE);
|
|
5205
|
-
if (
|
|
5493
|
+
if (existsSync13(configPath)) {
|
|
5206
5494
|
try {
|
|
5207
|
-
const raw =
|
|
5495
|
+
const raw = readFileSync10(configPath, "utf-8");
|
|
5208
5496
|
const config = JSON.parse(raw);
|
|
5209
5497
|
if (typeof config === "object" && config !== null && "tokens" in config && typeof config.tokens === "object" && config.tokens !== null && typeof config.tokens?.file === "string") {
|
|
5210
5498
|
const file = config.tokens.file;
|
|
@@ -5222,23 +5510,33 @@ function createTokensExportCommand() {
|
|
|
5222
5510
|
).action(
|
|
5223
5511
|
(opts) => {
|
|
5224
5512
|
if (!SUPPORTED_FORMATS.includes(opts.format)) {
|
|
5513
|
+
const FORMAT_ALIASES = {
|
|
5514
|
+
json: "flat-json",
|
|
5515
|
+
"json-flat": "flat-json",
|
|
5516
|
+
javascript: "ts",
|
|
5517
|
+
js: "ts",
|
|
5518
|
+
sass: "scss",
|
|
5519
|
+
tw: "tailwind"
|
|
5520
|
+
};
|
|
5521
|
+
const hint = FORMAT_ALIASES[opts.format.toLowerCase()];
|
|
5225
5522
|
process.stderr.write(
|
|
5226
5523
|
`Error: unsupported format "${opts.format}".
|
|
5227
5524
|
Supported formats: ${SUPPORTED_FORMATS.join(", ")}
|
|
5228
|
-
`
|
|
5525
|
+
` + (hint ? `Did you mean "${hint}"?
|
|
5526
|
+
` : "")
|
|
5229
5527
|
);
|
|
5230
5528
|
process.exit(1);
|
|
5231
5529
|
}
|
|
5232
5530
|
const format = opts.format;
|
|
5233
5531
|
try {
|
|
5234
5532
|
const filePath = resolveTokenFilePath2(opts.file);
|
|
5235
|
-
if (!
|
|
5533
|
+
if (!existsSync13(filePath)) {
|
|
5236
5534
|
throw new Error(
|
|
5237
5535
|
`Token file not found at ${filePath}.
|
|
5238
5536
|
Create a reactscope.tokens.json file or use --file to specify a path.`
|
|
5239
5537
|
);
|
|
5240
5538
|
}
|
|
5241
|
-
const raw =
|
|
5539
|
+
const raw = readFileSync10(filePath, "utf-8");
|
|
5242
5540
|
const { tokens, rawFile } = parseTokenFileSync(raw);
|
|
5243
5541
|
let themesMap;
|
|
5244
5542
|
if (opts.theme !== void 0) {
|
|
@@ -5639,9 +5937,9 @@ function resolveTokenFilePath(fileFlag) {
|
|
|
5639
5937
|
return resolve17(process.cwd(), fileFlag);
|
|
5640
5938
|
}
|
|
5641
5939
|
const configPath = resolve17(process.cwd(), CONFIG_FILE2);
|
|
5642
|
-
if (
|
|
5940
|
+
if (existsSync14(configPath)) {
|
|
5643
5941
|
try {
|
|
5644
|
-
const raw =
|
|
5942
|
+
const raw = readFileSync11(configPath, "utf-8");
|
|
5645
5943
|
const config = JSON.parse(raw);
|
|
5646
5944
|
if (typeof config === "object" && config !== null && "tokens" in config && typeof config.tokens === "object" && config.tokens !== null && typeof config.tokens?.file === "string") {
|
|
5647
5945
|
const file = config.tokens.file;
|
|
@@ -5653,13 +5951,13 @@ function resolveTokenFilePath(fileFlag) {
|
|
|
5653
5951
|
return resolve17(process.cwd(), DEFAULT_TOKEN_FILE2);
|
|
5654
5952
|
}
|
|
5655
5953
|
function loadTokens(absPath) {
|
|
5656
|
-
if (!
|
|
5954
|
+
if (!existsSync14(absPath)) {
|
|
5657
5955
|
throw new Error(
|
|
5658
5956
|
`Token file not found at ${absPath}.
|
|
5659
5957
|
Create a reactscope.tokens.json file or use --file to specify a path.`
|
|
5660
5958
|
);
|
|
5661
5959
|
}
|
|
5662
|
-
const raw =
|
|
5960
|
+
const raw = readFileSync11(absPath, "utf-8");
|
|
5663
5961
|
return parseTokenFileSync2(raw);
|
|
5664
5962
|
}
|
|
5665
5963
|
function getRawValue(node, segments) {
|
|
@@ -5873,13 +6171,13 @@ function registerValidate(tokensCmd) {
|
|
|
5873
6171
|
).option("--file <path>", "Path to token file (overrides config)").option("--format <fmt>", "Output format: json or text (default: auto-detect)").action((opts) => {
|
|
5874
6172
|
try {
|
|
5875
6173
|
const filePath = resolveTokenFilePath(opts.file);
|
|
5876
|
-
if (!
|
|
6174
|
+
if (!existsSync14(filePath)) {
|
|
5877
6175
|
throw new Error(
|
|
5878
6176
|
`Token file not found at ${filePath}.
|
|
5879
6177
|
Create a reactscope.tokens.json file or use --file to specify a path.`
|
|
5880
6178
|
);
|
|
5881
6179
|
}
|
|
5882
|
-
const raw =
|
|
6180
|
+
const raw = readFileSync11(filePath, "utf-8");
|
|
5883
6181
|
const useJson = opts.format === "json" || opts.format !== "text" && !isTTY2();
|
|
5884
6182
|
const errors = [];
|
|
5885
6183
|
let parsed;
|
|
@@ -6037,7 +6335,7 @@ function createProgram(options = {}) {
|
|
|
6037
6335
|
}
|
|
6038
6336
|
);
|
|
6039
6337
|
program2.command("generate").description("Generate a Playwright test from a Scope trace file").argument("<trace>", "Path to a serialized Scope trace (.json)").option("-o, --output <path>", "Output file path", "scope.spec.ts").option("-d, --description <text>", "Test description").action((tracePath, opts) => {
|
|
6040
|
-
const raw =
|
|
6338
|
+
const raw = readFileSync12(tracePath, "utf-8");
|
|
6041
6339
|
const trace = loadTrace(raw);
|
|
6042
6340
|
const source = generateTest(trace, {
|
|
6043
6341
|
description: opts.description,
|