@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/index.js CHANGED
@@ -94,6 +94,21 @@ import { createElement } from "react";
94
94
  // Suppress "React must be in scope" warnings from old JSX (we use automatic)
95
95
  banner: {
96
96
  js: "/* @agent-scope/cli component harness */"
97
+ },
98
+ // CSS imports (e.g. `import './styles.css'`) are handled at the page level via
99
+ // globalCSS injection. Tell esbuild to treat CSS files as empty modules so
100
+ // components that import CSS directly (e.g. App.tsx) don't error during bundling.
101
+ loader: {
102
+ ".css": "empty",
103
+ ".svg": "dataurl",
104
+ ".png": "dataurl",
105
+ ".jpg": "dataurl",
106
+ ".jpeg": "dataurl",
107
+ ".gif": "dataurl",
108
+ ".webp": "dataurl",
109
+ ".ttf": "dataurl",
110
+ ".woff": "dataurl",
111
+ ".woff2": "dataurl"
97
112
  }
98
113
  });
99
114
  if (result.errors.length > 0) {
@@ -501,6 +516,57 @@ async function getCompiledCssForClasses(cwd, classes) {
501
516
  if (deduped.length === 0) return null;
502
517
  return build3(deduped);
503
518
  }
519
+ async function compileGlobalCssFile(cssFilePath, cwd) {
520
+ const { existsSync: existsSync15, readFileSync: readFileSync13 } = await import('fs');
521
+ const { createRequire: createRequire3 } = await import('module');
522
+ if (!existsSync15(cssFilePath)) return null;
523
+ const raw = readFileSync13(cssFilePath, "utf-8");
524
+ const needsCompile = /@tailwind|@import\s+['"]tailwindcss/.test(raw);
525
+ if (!needsCompile) {
526
+ return raw;
527
+ }
528
+ try {
529
+ const require2 = createRequire3(resolve(cwd, "package.json"));
530
+ let postcss;
531
+ let twPlugin;
532
+ try {
533
+ postcss = require2("postcss");
534
+ twPlugin = require2("tailwindcss");
535
+ } catch {
536
+ return raw;
537
+ }
538
+ let autoprefixerPlugin;
539
+ try {
540
+ autoprefixerPlugin = require2("autoprefixer");
541
+ } catch {
542
+ autoprefixerPlugin = null;
543
+ }
544
+ const plugins = autoprefixerPlugin ? [twPlugin, autoprefixerPlugin] : [twPlugin];
545
+ const result = await postcss(plugins).process(raw, {
546
+ from: cssFilePath,
547
+ to: cssFilePath
548
+ });
549
+ return result.css;
550
+ } catch (err) {
551
+ process.stderr.write(
552
+ `[scope/render] Warning: CSS compilation failed for ${cssFilePath}: ${err instanceof Error ? err.message : String(err)}
553
+ `
554
+ );
555
+ return raw;
556
+ }
557
+ }
558
+ async function loadGlobalCss(globalCssFiles, cwd) {
559
+ if (globalCssFiles.length === 0) return null;
560
+ const parts = [];
561
+ for (const relPath of globalCssFiles) {
562
+ const absPath = resolve(cwd, relPath);
563
+ const css = await compileGlobalCssFile(absPath, cwd);
564
+ if (css !== null && css.trim().length > 0) {
565
+ parts.push(css);
566
+ }
567
+ }
568
+ return parts.length > 0 ? parts.join("\n") : null;
569
+ }
504
570
 
505
571
  // src/ci/commands.ts
506
572
  var CI_EXIT = {
@@ -1004,6 +1070,20 @@ function detectComponentPatterns(rootDir, typescript) {
1004
1070
  }
1005
1071
  return unique;
1006
1072
  }
1073
+ var GLOBAL_CSS_CANDIDATES = [
1074
+ "src/styles.css",
1075
+ "src/index.css",
1076
+ "src/global.css",
1077
+ "src/globals.css",
1078
+ "src/app.css",
1079
+ "src/main.css",
1080
+ "styles/globals.css",
1081
+ "styles/global.css",
1082
+ "styles/index.css"
1083
+ ];
1084
+ function detectGlobalCSSFiles(rootDir) {
1085
+ return GLOBAL_CSS_CANDIDATES.filter((rel) => existsSync(join(rootDir, rel)));
1086
+ }
1007
1087
  var TAILWIND_STEMS = ["tailwind.config"];
1008
1088
  var CSS_EXTS = [".css", ".scss", ".sass", ".less"];
1009
1089
  var THEME_SUFFIXES = [".theme.ts", ".theme.js", ".theme.tsx"];
@@ -1071,13 +1151,15 @@ function detectProject(rootDir) {
1071
1151
  const packageManager = detectPackageManager(rootDir);
1072
1152
  const componentPatterns = detectComponentPatterns(rootDir, typescript);
1073
1153
  const tokenSources = detectTokenSources(rootDir);
1154
+ const globalCSSFiles = detectGlobalCSSFiles(rootDir);
1074
1155
  return {
1075
1156
  framework,
1076
1157
  typescript,
1077
1158
  tsconfigPath,
1078
1159
  componentPatterns,
1079
1160
  tokenSources,
1080
- packageManager
1161
+ packageManager,
1162
+ globalCSSFiles
1081
1163
  };
1082
1164
  }
1083
1165
  function buildDefaultConfig(detected, tokenFile, outputDir) {
@@ -1086,7 +1168,7 @@ function buildDefaultConfig(detected, tokenFile, outputDir) {
1086
1168
  components: {
1087
1169
  include,
1088
1170
  exclude: ["**/*.test.tsx", "**/*.stories.tsx"],
1089
- wrappers: { providers: [], globalCSS: [] }
1171
+ wrappers: { providers: [], globalCSS: detected.globalCSSFiles ?? [] }
1090
1172
  },
1091
1173
  render: {
1092
1174
  viewport: { default: { width: 1280, height: 800 } },
@@ -1144,18 +1226,118 @@ function ensureGitignoreEntry(rootDir, entry) {
1144
1226
  `);
1145
1227
  }
1146
1228
  }
1229
+ function extractTailwindTokens(tokenSources) {
1230
+ const tailwindSource = tokenSources.find((s) => s.kind === "tailwind-config");
1231
+ if (!tailwindSource) return null;
1232
+ try {
1233
+ let parseBlock2 = function(block) {
1234
+ const result = {};
1235
+ const lineRe = /['"]?(\w[\w.-]*|\d+)['"]?\s*:\s*['"]?(#[0-9a-fA-F]{3,8}|\d+(?:px|rem|em|%)|[\w-]+(?:\/[\w]+)?)['"]?/g;
1236
+ for (const m of block.matchAll(lineRe)) {
1237
+ if (m[1] !== void 0 && m[2] !== void 0) {
1238
+ result[m[1]] = m[2];
1239
+ }
1240
+ }
1241
+ return result;
1242
+ };
1243
+ var parseBlock = parseBlock2;
1244
+ const raw = readFileSync(tailwindSource.path, "utf-8");
1245
+ const tokens = {};
1246
+ const colorsKeyIdx = raw.indexOf("colors:");
1247
+ if (colorsKeyIdx !== -1) {
1248
+ const colorsBraceStart = raw.indexOf("{", colorsKeyIdx);
1249
+ if (colorsBraceStart !== -1) {
1250
+ let colorDepth = 0;
1251
+ let colorsBraceEnd = -1;
1252
+ for (let ci = colorsBraceStart; ci < raw.length; ci++) {
1253
+ if (raw[ci] === "{") colorDepth++;
1254
+ else if (raw[ci] === "}") {
1255
+ colorDepth--;
1256
+ if (colorDepth === 0) {
1257
+ colorsBraceEnd = ci;
1258
+ break;
1259
+ }
1260
+ }
1261
+ }
1262
+ if (colorsBraceEnd > colorsBraceStart) {
1263
+ const colorSection = raw.slice(colorsBraceStart + 1, colorsBraceEnd);
1264
+ const scaleRe = /(\w+)\s*:\s*\{([^}]+)\}/g;
1265
+ const colorTokens = {};
1266
+ for (const sm of colorSection.matchAll(scaleRe)) {
1267
+ if (sm[1] === void 0 || sm[2] === void 0) continue;
1268
+ const scaleName = sm[1];
1269
+ const scaleValues = parseBlock2(sm[2]);
1270
+ if (Object.keys(scaleValues).length > 0) {
1271
+ const scaleTokens = {};
1272
+ for (const [step, hex] of Object.entries(scaleValues)) {
1273
+ scaleTokens[step] = { value: hex, type: "color" };
1274
+ }
1275
+ colorTokens[scaleName] = scaleTokens;
1276
+ }
1277
+ }
1278
+ if (Object.keys(colorTokens).length > 0) {
1279
+ tokens["color"] = colorTokens;
1280
+ }
1281
+ }
1282
+ }
1283
+ }
1284
+ const spacingMatch = raw.match(/spacing\s*:\s*\{([\s\S]*?)\n\s*\}/);
1285
+ if (spacingMatch?.[1] !== void 0) {
1286
+ const spacingValues = parseBlock2(spacingMatch[1]);
1287
+ if (Object.keys(spacingValues).length > 0) {
1288
+ const spacingTokens = {};
1289
+ for (const [key, val] of Object.entries(spacingValues)) {
1290
+ spacingTokens[key] = { value: val, type: "dimension" };
1291
+ }
1292
+ tokens["spacing"] = spacingTokens;
1293
+ }
1294
+ }
1295
+ const fontFamilyMatch = raw.match(/fontFamily\s*:\s*\{([\s\S]*?)\n\s*\}/);
1296
+ if (fontFamilyMatch?.[1] !== void 0) {
1297
+ const fontFamilyRe = /(\w+)\s*:\s*\[\s*['"]([^'"]+)['"]/g;
1298
+ const fontTokens = {};
1299
+ for (const fm of fontFamilyMatch[1].matchAll(fontFamilyRe)) {
1300
+ if (fm[1] !== void 0 && fm[2] !== void 0) {
1301
+ fontTokens[fm[1]] = { value: fm[2], type: "fontFamily" };
1302
+ }
1303
+ }
1304
+ if (Object.keys(fontTokens).length > 0) {
1305
+ tokens["font"] = fontTokens;
1306
+ }
1307
+ }
1308
+ const borderRadiusMatch = raw.match(/borderRadius\s*:\s*\{([\s\S]*?)\n\s*\}/);
1309
+ if (borderRadiusMatch?.[1] !== void 0) {
1310
+ const radiusValues = parseBlock2(borderRadiusMatch[1]);
1311
+ if (Object.keys(radiusValues).length > 0) {
1312
+ const radiusTokens = {};
1313
+ for (const [key, val] of Object.entries(radiusValues)) {
1314
+ radiusTokens[key] = { value: val, type: "dimension" };
1315
+ }
1316
+ tokens["radius"] = radiusTokens;
1317
+ }
1318
+ }
1319
+ return Object.keys(tokens).length > 0 ? tokens : null;
1320
+ } catch {
1321
+ return null;
1322
+ }
1323
+ }
1147
1324
  function scaffoldConfig(rootDir, config) {
1148
1325
  const path = join(rootDir, "reactscope.config.json");
1149
1326
  writeFileSync(path, `${JSON.stringify(config, null, 2)}
1150
1327
  `);
1151
1328
  return path;
1152
1329
  }
1153
- function scaffoldTokenFile(rootDir, tokenFile) {
1330
+ function scaffoldTokenFile(rootDir, tokenFile, extractedTokens) {
1154
1331
  const path = join(rootDir, tokenFile);
1155
1332
  if (!existsSync(path)) {
1156
1333
  const stub = {
1157
1334
  $schema: "https://raw.githubusercontent.com/FlatFilers/Scope/main/packages/tokens/schema.json",
1158
- tokens: {}
1335
+ version: "1.0.0",
1336
+ meta: {
1337
+ name: "Design Tokens",
1338
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
1339
+ },
1340
+ tokens: extractedTokens ?? {}
1159
1341
  };
1160
1342
  writeFileSync(path, `${JSON.stringify(stub, null, 2)}
1161
1343
  `);
@@ -1233,7 +1415,13 @@ async function runInit(options) {
1233
1415
  }
1234
1416
  const cfgPath = scaffoldConfig(rootDir, config);
1235
1417
  created.push(cfgPath);
1236
- const tokPath = scaffoldTokenFile(rootDir, config.tokens.file);
1418
+ const extractedTokens = extractTailwindTokens(detected.tokenSources);
1419
+ if (extractedTokens !== null) {
1420
+ const tokenGroupCount = Object.keys(extractedTokens).length;
1421
+ process.stdout.write(` Extracted ${tokenGroupCount} token group(s) from Tailwind config
1422
+ `);
1423
+ }
1424
+ const tokPath = scaffoldTokenFile(rootDir, config.tokens.file, extractedTokens ?? void 0);
1237
1425
  created.push(tokPath);
1238
1426
  const outDirPath = scaffoldOutputDir(rootDir, config.output.dir);
1239
1427
  created.push(outDirPath);
@@ -1333,7 +1521,10 @@ Available: ${available}${hint}`
1333
1521
  });
1334
1522
  }
1335
1523
  function registerQuery(manifestCmd) {
1336
- 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("--format <fmt>", "Output format: json or table (default: auto-detect)").option("--manifest <path>", "Path to manifest.json", MANIFEST_PATH).action(
1524
+ 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(
1525
+ "--has-prop <spec>",
1526
+ "Find components with a prop matching name or name:type (e.g. 'loading' or 'variant:union')"
1527
+ ).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(
1337
1528
  (opts) => {
1338
1529
  try {
1339
1530
  const manifest = loadManifest(opts.manifest);
@@ -1344,9 +1535,11 @@ function registerQuery(manifestCmd) {
1344
1535
  if (opts.complexity !== void 0) queryParts.push(`complexity=${opts.complexity}`);
1345
1536
  if (opts.sideEffects) queryParts.push("side-effects");
1346
1537
  if (opts.hasFetch) queryParts.push("has-fetch");
1538
+ if (opts.hasProp !== void 0) queryParts.push(`has-prop=${opts.hasProp}`);
1539
+ if (opts.composedBy !== void 0) queryParts.push(`composed-by=${opts.composedBy}`);
1347
1540
  if (queryParts.length === 0) {
1348
1541
  process.stderr.write(
1349
- "No query flags specified. Use --context, --hook, --complexity, --side-effects, or --has-fetch.\n"
1542
+ "No query flags specified. Use --context, --hook, --complexity, --side-effects, --has-fetch, --has-prop, or --composed-by.\n"
1350
1543
  );
1351
1544
  process.exit(1);
1352
1545
  }
@@ -1373,6 +1566,27 @@ function registerQuery(manifestCmd) {
1373
1566
  if (opts.hasFetch) {
1374
1567
  entries = entries.filter(([, d]) => d.sideEffects.fetches.length > 0);
1375
1568
  }
1569
+ if (opts.hasProp !== void 0) {
1570
+ const spec = opts.hasProp;
1571
+ const colonIdx = spec.indexOf(":");
1572
+ const propName = colonIdx >= 0 ? spec.slice(0, colonIdx) : spec;
1573
+ const propType = colonIdx >= 0 ? spec.slice(colonIdx + 1) : void 0;
1574
+ entries = entries.filter(([, d]) => {
1575
+ const props = d.props;
1576
+ if (!props || !(propName in props)) return false;
1577
+ if (propType !== void 0) {
1578
+ return props[propName]?.type === propType;
1579
+ }
1580
+ return true;
1581
+ });
1582
+ }
1583
+ if (opts.composedBy !== void 0) {
1584
+ const targetName = opts.composedBy;
1585
+ entries = entries.filter(([, d]) => {
1586
+ const composedBy = d.composedBy;
1587
+ return composedBy !== void 0 && composedBy.includes(targetName);
1588
+ });
1589
+ }
1376
1590
  const rows = entries.map(([name, d]) => ({
1377
1591
  name,
1378
1592
  file: d.filePath,
@@ -3057,6 +3271,17 @@ ${msg}`);
3057
3271
  }
3058
3272
 
3059
3273
  // src/render-commands.ts
3274
+ function loadGlobalCssFilesFromConfig(cwd) {
3275
+ const configPath = resolve(cwd, "reactscope.config.json");
3276
+ if (!existsSync(configPath)) return [];
3277
+ try {
3278
+ const raw = readFileSync(configPath, "utf-8");
3279
+ const cfg = JSON.parse(raw);
3280
+ return cfg.components?.wrappers?.globalCSS ?? [];
3281
+ } catch {
3282
+ return [];
3283
+ }
3284
+ }
3060
3285
  var MANIFEST_PATH6 = ".reactscope/manifest.json";
3061
3286
  var DEFAULT_OUTPUT_DIR = ".reactscope/renders";
3062
3287
  var _pool3 = null;
@@ -3077,7 +3302,7 @@ async function shutdownPool3() {
3077
3302
  _pool3 = null;
3078
3303
  }
3079
3304
  }
3080
- function buildRenderer(filePath, componentName, viewportWidth, viewportHeight, wrapperScript) {
3305
+ function buildRenderer(filePath, componentName, viewportWidth, viewportHeight, globalCssFiles = [], projectCwd = process.cwd(), wrapperScript) {
3081
3306
  const satori = new SatoriRenderer({
3082
3307
  defaultViewport: { width: viewportWidth, height: viewportHeight }
3083
3308
  });
@@ -3086,13 +3311,13 @@ function buildRenderer(filePath, componentName, viewportWidth, viewportHeight, w
3086
3311
  async renderCell(props, _complexityClass) {
3087
3312
  const startMs = performance.now();
3088
3313
  const pool = await getPool3(viewportWidth, viewportHeight);
3314
+ const projectCss = await loadGlobalCss(globalCssFiles, projectCwd);
3089
3315
  const htmlHarness = await buildComponentHarness(
3090
3316
  filePath,
3091
3317
  componentName,
3092
3318
  props,
3093
3319
  viewportWidth,
3094
- void 0,
3095
- // projectCss (handled separately)
3320
+ projectCss ?? void 0,
3096
3321
  wrapperScript
3097
3322
  );
3098
3323
  const slot = await pool.acquire();
@@ -3122,9 +3347,9 @@ function buildRenderer(filePath, componentName, viewportWidth, viewportHeight, w
3122
3347
  });
3123
3348
  return [...set];
3124
3349
  });
3125
- const projectCss = await getCompiledCssForClasses(rootDir, classes);
3126
- if (projectCss != null && projectCss.length > 0) {
3127
- await page.addStyleTag({ content: projectCss });
3350
+ const projectCss2 = await getCompiledCssForClasses(rootDir, classes);
3351
+ if (projectCss2 != null && projectCss2.length > 0) {
3352
+ await page.addStyleTag({ content: projectCss2 });
3128
3353
  }
3129
3354
  const renderTimeMs = performance.now() - startMs;
3130
3355
  const rootLocator = page.locator("[data-reactscope-root]");
@@ -3226,26 +3451,59 @@ function registerRenderSingle(renderCmd) {
3226
3451
  Available: ${available}`
3227
3452
  );
3228
3453
  }
3454
+ let props = {};
3455
+ if (opts.props !== void 0) {
3456
+ try {
3457
+ props = JSON.parse(opts.props);
3458
+ } catch {
3459
+ throw new Error(`Invalid props JSON: ${opts.props}`);
3460
+ }
3461
+ }
3462
+ if (descriptor.props !== void 0) {
3463
+ const propDefs = descriptor.props;
3464
+ for (const [propName, propDef] of Object.entries(propDefs)) {
3465
+ if (propName in props) continue;
3466
+ if (!propDef.required && propDef.default !== void 0) continue;
3467
+ if (propDef.type === "node" || propDef.type === "string") {
3468
+ props[propName] = propName === "children" ? componentName : propName;
3469
+ } else if (propDef.type === "union" && propDef.values && propDef.values.length > 0) {
3470
+ props[propName] = propDef.values[0];
3471
+ } else if (propDef.type === "boolean") {
3472
+ props[propName] = false;
3473
+ } else if (propDef.type === "number") {
3474
+ props[propName] = 0;
3475
+ }
3476
+ }
3477
+ }
3229
3478
  const { width, height } = parseViewport(opts.viewport);
3230
3479
  const rootDir = process.cwd();
3231
3480
  const filePath = resolve(rootDir, descriptor.filePath);
3232
3481
  const scopeData = await loadScopeFileForComponent(filePath);
3233
3482
  const wrapperScript = scopeData?.hasWrapper === true ? await buildWrapperScript(scopeData.filePath) : void 0;
3234
3483
  const scenarios = buildScenarioMap(opts, scopeData);
3235
- const renderer = buildRenderer(filePath, componentName, width, height, wrapperScript);
3484
+ const globalCssFiles = loadGlobalCssFilesFromConfig(rootDir);
3485
+ const renderer = buildRenderer(
3486
+ filePath,
3487
+ componentName,
3488
+ width,
3489
+ height,
3490
+ globalCssFiles,
3491
+ rootDir,
3492
+ wrapperScript
3493
+ );
3236
3494
  process.stderr.write(
3237
3495
  `Rendering ${componentName} [${descriptor.complexityClass}] at ${width}\xD7${height}\u2026
3238
3496
  `
3239
3497
  );
3240
3498
  const fmt2 = resolveSingleFormat(opts.format);
3241
3499
  let anyFailed = false;
3242
- for (const [scenarioName, props] of Object.entries(scenarios)) {
3500
+ for (const [scenarioName, props2] of Object.entries(scenarios)) {
3243
3501
  const isNamed = scenarioName !== "__default__";
3244
3502
  const label = isNamed ? `${componentName}:${scenarioName}` : componentName;
3245
3503
  const outcome = await safeRender(
3246
- () => renderer.renderCell(props, descriptor.complexityClass),
3504
+ () => renderer.renderCell(props2, descriptor.complexityClass),
3247
3505
  {
3248
- props,
3506
+ props: props2,
3249
3507
  sourceLocation: {
3250
3508
  file: descriptor.filePath,
3251
3509
  line: descriptor.loc.start,
@@ -3274,7 +3532,7 @@ Available: ${available}`
3274
3532
  `
3275
3533
  );
3276
3534
  } else if (fmt2 === "json") {
3277
- const json = formatRenderJson(label, props, result);
3535
+ const json = formatRenderJson(label, props2, result);
3278
3536
  process.stdout.write(`${JSON.stringify(json, null, 2)}
3279
3537
  `);
3280
3538
  } else {
@@ -3301,7 +3559,10 @@ Available: ${available}`
3301
3559
  );
3302
3560
  }
3303
3561
  function registerRenderMatrix(renderCmd) {
3304
- renderCmd.command("matrix <component>").description("Render a component across a matrix of prop axes").option("--axes <spec>", "Axis definitions e.g. 'variant:primary,secondary size:sm,md,lg'").option(
3562
+ renderCmd.command("matrix <component>").description("Render a component across a matrix of prop axes").option(
3563
+ "--axes <spec>",
3564
+ `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"]}'`
3565
+ ).option(
3305
3566
  "--contexts <ids>",
3306
3567
  "Composition context IDs, comma-separated (e.g. centered,rtl,sidebar)"
3307
3568
  ).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(
@@ -3320,21 +3581,47 @@ Available: ${available}`
3320
3581
  const { width, height } = { width: 375, height: 812 };
3321
3582
  const rootDir = process.cwd();
3322
3583
  const filePath = resolve(rootDir, descriptor.filePath);
3323
- const renderer = buildRenderer(filePath, componentName, width, height);
3584
+ const matrixCssFiles = loadGlobalCssFilesFromConfig(rootDir);
3585
+ const renderer = buildRenderer(
3586
+ filePath,
3587
+ componentName,
3588
+ width,
3589
+ height,
3590
+ matrixCssFiles,
3591
+ rootDir
3592
+ );
3324
3593
  const axes = [];
3325
3594
  if (opts.axes !== void 0) {
3326
- const axisSpecs = opts.axes.trim().split(/\s+/);
3327
- for (const spec of axisSpecs) {
3328
- const colonIdx = spec.indexOf(":");
3329
- if (colonIdx < 0) {
3330
- throw new Error(`Invalid axis spec "${spec}". Expected format: name:val1,val2,...`);
3595
+ const axesRaw = opts.axes.trim();
3596
+ if (axesRaw.startsWith("{")) {
3597
+ let parsed;
3598
+ try {
3599
+ parsed = JSON.parse(axesRaw);
3600
+ } catch {
3601
+ throw new Error(`Invalid JSON in --axes: ${axesRaw}`);
3331
3602
  }
3332
- const name = spec.slice(0, colonIdx);
3333
- const values = spec.slice(colonIdx + 1).split(",").map((v) => v.trim());
3334
- if (name.length === 0 || values.length === 0) {
3335
- throw new Error(`Invalid axis spec "${spec}"`);
3603
+ for (const [name, vals] of Object.entries(parsed)) {
3604
+ if (!Array.isArray(vals)) {
3605
+ throw new Error(`Axis "${name}" must be an array of values in JSON format`);
3606
+ }
3607
+ axes.push({ name, values: vals.map(String) });
3608
+ }
3609
+ } else {
3610
+ const axisSpecs = axesRaw.split(/\s+/);
3611
+ for (const spec of axisSpecs) {
3612
+ const colonIdx = spec.indexOf(":");
3613
+ if (colonIdx < 0) {
3614
+ throw new Error(
3615
+ `Invalid axis spec "${spec}". Expected format: name:val1,val2,...`
3616
+ );
3617
+ }
3618
+ const name = spec.slice(0, colonIdx);
3619
+ const values = spec.slice(colonIdx + 1).split(",").map((v) => v.trim());
3620
+ if (name.length === 0 || values.length === 0) {
3621
+ throw new Error(`Invalid axis spec "${spec}"`);
3622
+ }
3623
+ axes.push({ name, values });
3336
3624
  }
3337
- axes.push({ name, values });
3338
3625
  }
3339
3626
  }
3340
3627
  if (opts.contexts !== void 0) {
@@ -3453,7 +3740,8 @@ function registerRenderAll(renderCmd) {
3453
3740
  const descriptor = manifest.components[name];
3454
3741
  if (descriptor === void 0) return;
3455
3742
  const filePath = resolve(rootDir, descriptor.filePath);
3456
- const renderer = buildRenderer(filePath, name, 375, 812);
3743
+ const allCssFiles = loadGlobalCssFilesFromConfig(process.cwd());
3744
+ const renderer = buildRenderer(filePath, name, 375, 812, allCssFiles, process.cwd());
3457
3745
  const outcome = await safeRender(
3458
3746
  () => renderer.renderCell({}, descriptor.complexityClass),
3459
3747
  {
@@ -3719,12 +4007,12 @@ async function runBaseline(options = {}) {
3719
4007
  mkdirSync(rendersDir, { recursive: true });
3720
4008
  let manifest;
3721
4009
  if (manifestPath !== void 0) {
3722
- const { readFileSync: readFileSync12 } = await import('fs');
4010
+ const { readFileSync: readFileSync13 } = await import('fs');
3723
4011
  const absPath = resolve(rootDir, manifestPath);
3724
4012
  if (!existsSync(absPath)) {
3725
4013
  throw new Error(`Manifest not found at ${absPath}.`);
3726
4014
  }
3727
- manifest = JSON.parse(readFileSync12(absPath, "utf-8"));
4015
+ manifest = JSON.parse(readFileSync13(absPath, "utf-8"));
3728
4016
  process.stderr.write(`Loaded manifest from ${manifestPath}
3729
4017
  `);
3730
4018
  } else {
@@ -5085,10 +5373,20 @@ function createTokensExportCommand() {
5085
5373
  ).action(
5086
5374
  (opts) => {
5087
5375
  if (!SUPPORTED_FORMATS.includes(opts.format)) {
5376
+ const FORMAT_ALIASES = {
5377
+ json: "flat-json",
5378
+ "json-flat": "flat-json",
5379
+ javascript: "ts",
5380
+ js: "ts",
5381
+ sass: "scss",
5382
+ tw: "tailwind"
5383
+ };
5384
+ const hint = FORMAT_ALIASES[opts.format.toLowerCase()];
5088
5385
  process.stderr.write(
5089
5386
  `Error: unsupported format "${opts.format}".
5090
5387
  Supported formats: ${SUPPORTED_FORMATS.join(", ")}
5091
- `
5388
+ ` + (hint ? `Did you mean "${hint}"?
5389
+ ` : "")
5092
5390
  );
5093
5391
  process.exit(1);
5094
5392
  }