@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.cjs CHANGED
@@ -118,6 +118,21 @@ import { createElement } from "react";
118
118
  // Suppress "React must be in scope" warnings from old JSX (we use automatic)
119
119
  banner: {
120
120
  js: "/* @agent-scope/cli component harness */"
121
+ },
122
+ // CSS imports (e.g. `import './styles.css'`) are handled at the page level via
123
+ // globalCSS injection. Tell esbuild to treat CSS files as empty modules so
124
+ // components that import CSS directly (e.g. App.tsx) don't error during bundling.
125
+ loader: {
126
+ ".css": "empty",
127
+ ".svg": "dataurl",
128
+ ".png": "dataurl",
129
+ ".jpg": "dataurl",
130
+ ".jpeg": "dataurl",
131
+ ".gif": "dataurl",
132
+ ".webp": "dataurl",
133
+ ".ttf": "dataurl",
134
+ ".woff": "dataurl",
135
+ ".woff2": "dataurl"
121
136
  }
122
137
  });
123
138
  if (result.errors.length > 0) {
@@ -525,6 +540,57 @@ async function getCompiledCssForClasses(cwd, classes) {
525
540
  if (deduped.length === 0) return null;
526
541
  return build3(deduped);
527
542
  }
543
+ async function compileGlobalCssFile(cssFilePath, cwd) {
544
+ const { existsSync: existsSync15, readFileSync: readFileSync13 } = await import('fs');
545
+ const { createRequire: createRequire3 } = await import('module');
546
+ if (!existsSync15(cssFilePath)) return null;
547
+ const raw = readFileSync13(cssFilePath, "utf-8");
548
+ const needsCompile = /@tailwind|@import\s+['"]tailwindcss/.test(raw);
549
+ if (!needsCompile) {
550
+ return raw;
551
+ }
552
+ try {
553
+ const require2 = createRequire3(path.resolve(cwd, "package.json"));
554
+ let postcss;
555
+ let twPlugin;
556
+ try {
557
+ postcss = require2("postcss");
558
+ twPlugin = require2("tailwindcss");
559
+ } catch {
560
+ return raw;
561
+ }
562
+ let autoprefixerPlugin;
563
+ try {
564
+ autoprefixerPlugin = require2("autoprefixer");
565
+ } catch {
566
+ autoprefixerPlugin = null;
567
+ }
568
+ const plugins = autoprefixerPlugin ? [twPlugin, autoprefixerPlugin] : [twPlugin];
569
+ const result = await postcss(plugins).process(raw, {
570
+ from: cssFilePath,
571
+ to: cssFilePath
572
+ });
573
+ return result.css;
574
+ } catch (err) {
575
+ process.stderr.write(
576
+ `[scope/render] Warning: CSS compilation failed for ${cssFilePath}: ${err instanceof Error ? err.message : String(err)}
577
+ `
578
+ );
579
+ return raw;
580
+ }
581
+ }
582
+ async function loadGlobalCss(globalCssFiles, cwd) {
583
+ if (globalCssFiles.length === 0) return null;
584
+ const parts = [];
585
+ for (const relPath of globalCssFiles) {
586
+ const absPath = path.resolve(cwd, relPath);
587
+ const css = await compileGlobalCssFile(absPath, cwd);
588
+ if (css !== null && css.trim().length > 0) {
589
+ parts.push(css);
590
+ }
591
+ }
592
+ return parts.length > 0 ? parts.join("\n") : null;
593
+ }
528
594
 
529
595
  // src/ci/commands.ts
530
596
  var CI_EXIT = {
@@ -1028,6 +1094,20 @@ function detectComponentPatterns(rootDir, typescript) {
1028
1094
  }
1029
1095
  return unique;
1030
1096
  }
1097
+ var GLOBAL_CSS_CANDIDATES = [
1098
+ "src/styles.css",
1099
+ "src/index.css",
1100
+ "src/global.css",
1101
+ "src/globals.css",
1102
+ "src/app.css",
1103
+ "src/main.css",
1104
+ "styles/globals.css",
1105
+ "styles/global.css",
1106
+ "styles/index.css"
1107
+ ];
1108
+ function detectGlobalCSSFiles(rootDir) {
1109
+ return GLOBAL_CSS_CANDIDATES.filter((rel) => fs.existsSync(path.join(rootDir, rel)));
1110
+ }
1031
1111
  var TAILWIND_STEMS = ["tailwind.config"];
1032
1112
  var CSS_EXTS = [".css", ".scss", ".sass", ".less"];
1033
1113
  var THEME_SUFFIXES = [".theme.ts", ".theme.js", ".theme.tsx"];
@@ -1095,13 +1175,15 @@ function detectProject(rootDir) {
1095
1175
  const packageManager = detectPackageManager(rootDir);
1096
1176
  const componentPatterns = detectComponentPatterns(rootDir, typescript);
1097
1177
  const tokenSources = detectTokenSources(rootDir);
1178
+ const globalCSSFiles = detectGlobalCSSFiles(rootDir);
1098
1179
  return {
1099
1180
  framework,
1100
1181
  typescript,
1101
1182
  tsconfigPath,
1102
1183
  componentPatterns,
1103
1184
  tokenSources,
1104
- packageManager
1185
+ packageManager,
1186
+ globalCSSFiles
1105
1187
  };
1106
1188
  }
1107
1189
  function buildDefaultConfig(detected, tokenFile, outputDir) {
@@ -1110,7 +1192,7 @@ function buildDefaultConfig(detected, tokenFile, outputDir) {
1110
1192
  components: {
1111
1193
  include,
1112
1194
  exclude: ["**/*.test.tsx", "**/*.stories.tsx"],
1113
- wrappers: { providers: [], globalCSS: [] }
1195
+ wrappers: { providers: [], globalCSS: detected.globalCSSFiles ?? [] }
1114
1196
  },
1115
1197
  render: {
1116
1198
  viewport: { default: { width: 1280, height: 800 } },
@@ -1168,18 +1250,118 @@ function ensureGitignoreEntry(rootDir, entry) {
1168
1250
  `);
1169
1251
  }
1170
1252
  }
1253
+ function extractTailwindTokens(tokenSources) {
1254
+ const tailwindSource = tokenSources.find((s) => s.kind === "tailwind-config");
1255
+ if (!tailwindSource) return null;
1256
+ try {
1257
+ let parseBlock2 = function(block) {
1258
+ const result = {};
1259
+ const lineRe = /['"]?(\w[\w.-]*|\d+)['"]?\s*:\s*['"]?(#[0-9a-fA-F]{3,8}|\d+(?:px|rem|em|%)|[\w-]+(?:\/[\w]+)?)['"]?/g;
1260
+ for (const m of block.matchAll(lineRe)) {
1261
+ if (m[1] !== void 0 && m[2] !== void 0) {
1262
+ result[m[1]] = m[2];
1263
+ }
1264
+ }
1265
+ return result;
1266
+ };
1267
+ var parseBlock = parseBlock2;
1268
+ const raw = fs.readFileSync(tailwindSource.path, "utf-8");
1269
+ const tokens = {};
1270
+ const colorsKeyIdx = raw.indexOf("colors:");
1271
+ if (colorsKeyIdx !== -1) {
1272
+ const colorsBraceStart = raw.indexOf("{", colorsKeyIdx);
1273
+ if (colorsBraceStart !== -1) {
1274
+ let colorDepth = 0;
1275
+ let colorsBraceEnd = -1;
1276
+ for (let ci = colorsBraceStart; ci < raw.length; ci++) {
1277
+ if (raw[ci] === "{") colorDepth++;
1278
+ else if (raw[ci] === "}") {
1279
+ colorDepth--;
1280
+ if (colorDepth === 0) {
1281
+ colorsBraceEnd = ci;
1282
+ break;
1283
+ }
1284
+ }
1285
+ }
1286
+ if (colorsBraceEnd > colorsBraceStart) {
1287
+ const colorSection = raw.slice(colorsBraceStart + 1, colorsBraceEnd);
1288
+ const scaleRe = /(\w+)\s*:\s*\{([^}]+)\}/g;
1289
+ const colorTokens = {};
1290
+ for (const sm of colorSection.matchAll(scaleRe)) {
1291
+ if (sm[1] === void 0 || sm[2] === void 0) continue;
1292
+ const scaleName = sm[1];
1293
+ const scaleValues = parseBlock2(sm[2]);
1294
+ if (Object.keys(scaleValues).length > 0) {
1295
+ const scaleTokens = {};
1296
+ for (const [step, hex] of Object.entries(scaleValues)) {
1297
+ scaleTokens[step] = { value: hex, type: "color" };
1298
+ }
1299
+ colorTokens[scaleName] = scaleTokens;
1300
+ }
1301
+ }
1302
+ if (Object.keys(colorTokens).length > 0) {
1303
+ tokens["color"] = colorTokens;
1304
+ }
1305
+ }
1306
+ }
1307
+ }
1308
+ const spacingMatch = raw.match(/spacing\s*:\s*\{([\s\S]*?)\n\s*\}/);
1309
+ if (spacingMatch?.[1] !== void 0) {
1310
+ const spacingValues = parseBlock2(spacingMatch[1]);
1311
+ if (Object.keys(spacingValues).length > 0) {
1312
+ const spacingTokens = {};
1313
+ for (const [key, val] of Object.entries(spacingValues)) {
1314
+ spacingTokens[key] = { value: val, type: "dimension" };
1315
+ }
1316
+ tokens["spacing"] = spacingTokens;
1317
+ }
1318
+ }
1319
+ const fontFamilyMatch = raw.match(/fontFamily\s*:\s*\{([\s\S]*?)\n\s*\}/);
1320
+ if (fontFamilyMatch?.[1] !== void 0) {
1321
+ const fontFamilyRe = /(\w+)\s*:\s*\[\s*['"]([^'"]+)['"]/g;
1322
+ const fontTokens = {};
1323
+ for (const fm of fontFamilyMatch[1].matchAll(fontFamilyRe)) {
1324
+ if (fm[1] !== void 0 && fm[2] !== void 0) {
1325
+ fontTokens[fm[1]] = { value: fm[2], type: "fontFamily" };
1326
+ }
1327
+ }
1328
+ if (Object.keys(fontTokens).length > 0) {
1329
+ tokens["font"] = fontTokens;
1330
+ }
1331
+ }
1332
+ const borderRadiusMatch = raw.match(/borderRadius\s*:\s*\{([\s\S]*?)\n\s*\}/);
1333
+ if (borderRadiusMatch?.[1] !== void 0) {
1334
+ const radiusValues = parseBlock2(borderRadiusMatch[1]);
1335
+ if (Object.keys(radiusValues).length > 0) {
1336
+ const radiusTokens = {};
1337
+ for (const [key, val] of Object.entries(radiusValues)) {
1338
+ radiusTokens[key] = { value: val, type: "dimension" };
1339
+ }
1340
+ tokens["radius"] = radiusTokens;
1341
+ }
1342
+ }
1343
+ return Object.keys(tokens).length > 0 ? tokens : null;
1344
+ } catch {
1345
+ return null;
1346
+ }
1347
+ }
1171
1348
  function scaffoldConfig(rootDir, config) {
1172
1349
  const path$1 = path.join(rootDir, "reactscope.config.json");
1173
1350
  fs.writeFileSync(path$1, `${JSON.stringify(config, null, 2)}
1174
1351
  `);
1175
1352
  return path$1;
1176
1353
  }
1177
- function scaffoldTokenFile(rootDir, tokenFile) {
1354
+ function scaffoldTokenFile(rootDir, tokenFile, extractedTokens) {
1178
1355
  const path$1 = path.join(rootDir, tokenFile);
1179
1356
  if (!fs.existsSync(path$1)) {
1180
1357
  const stub = {
1181
1358
  $schema: "https://raw.githubusercontent.com/FlatFilers/Scope/main/packages/tokens/schema.json",
1182
- tokens: {}
1359
+ version: "1.0.0",
1360
+ meta: {
1361
+ name: "Design Tokens",
1362
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
1363
+ },
1364
+ tokens: extractedTokens ?? {}
1183
1365
  };
1184
1366
  fs.writeFileSync(path$1, `${JSON.stringify(stub, null, 2)}
1185
1367
  `);
@@ -1257,7 +1439,13 @@ async function runInit(options) {
1257
1439
  }
1258
1440
  const cfgPath = scaffoldConfig(rootDir, config);
1259
1441
  created.push(cfgPath);
1260
- const tokPath = scaffoldTokenFile(rootDir, config.tokens.file);
1442
+ const extractedTokens = extractTailwindTokens(detected.tokenSources);
1443
+ if (extractedTokens !== null) {
1444
+ const tokenGroupCount = Object.keys(extractedTokens).length;
1445
+ process.stdout.write(` Extracted ${tokenGroupCount} token group(s) from Tailwind config
1446
+ `);
1447
+ }
1448
+ const tokPath = scaffoldTokenFile(rootDir, config.tokens.file, extractedTokens ?? void 0);
1261
1449
  created.push(tokPath);
1262
1450
  const outDirPath = scaffoldOutputDir(rootDir, config.output.dir);
1263
1451
  created.push(outDirPath);
@@ -1357,7 +1545,10 @@ Available: ${available}${hint}`
1357
1545
  });
1358
1546
  }
1359
1547
  function registerQuery(manifestCmd) {
1360
- 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(
1548
+ 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(
1549
+ "--has-prop <spec>",
1550
+ "Find components with a prop matching name or name:type (e.g. 'loading' or 'variant:union')"
1551
+ ).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(
1361
1552
  (opts) => {
1362
1553
  try {
1363
1554
  const manifest = loadManifest(opts.manifest);
@@ -1368,9 +1559,11 @@ function registerQuery(manifestCmd) {
1368
1559
  if (opts.complexity !== void 0) queryParts.push(`complexity=${opts.complexity}`);
1369
1560
  if (opts.sideEffects) queryParts.push("side-effects");
1370
1561
  if (opts.hasFetch) queryParts.push("has-fetch");
1562
+ if (opts.hasProp !== void 0) queryParts.push(`has-prop=${opts.hasProp}`);
1563
+ if (opts.composedBy !== void 0) queryParts.push(`composed-by=${opts.composedBy}`);
1371
1564
  if (queryParts.length === 0) {
1372
1565
  process.stderr.write(
1373
- "No query flags specified. Use --context, --hook, --complexity, --side-effects, or --has-fetch.\n"
1566
+ "No query flags specified. Use --context, --hook, --complexity, --side-effects, --has-fetch, --has-prop, or --composed-by.\n"
1374
1567
  );
1375
1568
  process.exit(1);
1376
1569
  }
@@ -1397,6 +1590,27 @@ function registerQuery(manifestCmd) {
1397
1590
  if (opts.hasFetch) {
1398
1591
  entries = entries.filter(([, d]) => d.sideEffects.fetches.length > 0);
1399
1592
  }
1593
+ if (opts.hasProp !== void 0) {
1594
+ const spec = opts.hasProp;
1595
+ const colonIdx = spec.indexOf(":");
1596
+ const propName = colonIdx >= 0 ? spec.slice(0, colonIdx) : spec;
1597
+ const propType = colonIdx >= 0 ? spec.slice(colonIdx + 1) : void 0;
1598
+ entries = entries.filter(([, d]) => {
1599
+ const props = d.props;
1600
+ if (!props || !(propName in props)) return false;
1601
+ if (propType !== void 0) {
1602
+ return props[propName]?.type === propType;
1603
+ }
1604
+ return true;
1605
+ });
1606
+ }
1607
+ if (opts.composedBy !== void 0) {
1608
+ const targetName = opts.composedBy;
1609
+ entries = entries.filter(([, d]) => {
1610
+ const composedBy = d.composedBy;
1611
+ return composedBy !== void 0 && composedBy.includes(targetName);
1612
+ });
1613
+ }
1400
1614
  const rows = entries.map(([name, d]) => ({
1401
1615
  name,
1402
1616
  file: d.filePath,
@@ -3081,6 +3295,17 @@ ${msg}`);
3081
3295
  }
3082
3296
 
3083
3297
  // src/render-commands.ts
3298
+ function loadGlobalCssFilesFromConfig(cwd) {
3299
+ const configPath = path.resolve(cwd, "reactscope.config.json");
3300
+ if (!fs.existsSync(configPath)) return [];
3301
+ try {
3302
+ const raw = fs.readFileSync(configPath, "utf-8");
3303
+ const cfg = JSON.parse(raw);
3304
+ return cfg.components?.wrappers?.globalCSS ?? [];
3305
+ } catch {
3306
+ return [];
3307
+ }
3308
+ }
3084
3309
  var MANIFEST_PATH6 = ".reactscope/manifest.json";
3085
3310
  var DEFAULT_OUTPUT_DIR = ".reactscope/renders";
3086
3311
  var _pool3 = null;
@@ -3101,7 +3326,7 @@ async function shutdownPool3() {
3101
3326
  _pool3 = null;
3102
3327
  }
3103
3328
  }
3104
- function buildRenderer(filePath, componentName, viewportWidth, viewportHeight, wrapperScript) {
3329
+ function buildRenderer(filePath, componentName, viewportWidth, viewportHeight, globalCssFiles = [], projectCwd = process.cwd(), wrapperScript) {
3105
3330
  const satori = new render.SatoriRenderer({
3106
3331
  defaultViewport: { width: viewportWidth, height: viewportHeight }
3107
3332
  });
@@ -3110,13 +3335,13 @@ function buildRenderer(filePath, componentName, viewportWidth, viewportHeight, w
3110
3335
  async renderCell(props, _complexityClass) {
3111
3336
  const startMs = performance.now();
3112
3337
  const pool = await getPool3(viewportWidth, viewportHeight);
3338
+ const projectCss = await loadGlobalCss(globalCssFiles, projectCwd);
3113
3339
  const htmlHarness = await buildComponentHarness(
3114
3340
  filePath,
3115
3341
  componentName,
3116
3342
  props,
3117
3343
  viewportWidth,
3118
- void 0,
3119
- // projectCss (handled separately)
3344
+ projectCss ?? void 0,
3120
3345
  wrapperScript
3121
3346
  );
3122
3347
  const slot = await pool.acquire();
@@ -3146,9 +3371,9 @@ function buildRenderer(filePath, componentName, viewportWidth, viewportHeight, w
3146
3371
  });
3147
3372
  return [...set];
3148
3373
  });
3149
- const projectCss = await getCompiledCssForClasses(rootDir, classes);
3150
- if (projectCss != null && projectCss.length > 0) {
3151
- await page.addStyleTag({ content: projectCss });
3374
+ const projectCss2 = await getCompiledCssForClasses(rootDir, classes);
3375
+ if (projectCss2 != null && projectCss2.length > 0) {
3376
+ await page.addStyleTag({ content: projectCss2 });
3152
3377
  }
3153
3378
  const renderTimeMs = performance.now() - startMs;
3154
3379
  const rootLocator = page.locator("[data-reactscope-root]");
@@ -3250,26 +3475,59 @@ function registerRenderSingle(renderCmd) {
3250
3475
  Available: ${available}`
3251
3476
  );
3252
3477
  }
3478
+ let props = {};
3479
+ if (opts.props !== void 0) {
3480
+ try {
3481
+ props = JSON.parse(opts.props);
3482
+ } catch {
3483
+ throw new Error(`Invalid props JSON: ${opts.props}`);
3484
+ }
3485
+ }
3486
+ if (descriptor.props !== void 0) {
3487
+ const propDefs = descriptor.props;
3488
+ for (const [propName, propDef] of Object.entries(propDefs)) {
3489
+ if (propName in props) continue;
3490
+ if (!propDef.required && propDef.default !== void 0) continue;
3491
+ if (propDef.type === "node" || propDef.type === "string") {
3492
+ props[propName] = propName === "children" ? componentName : propName;
3493
+ } else if (propDef.type === "union" && propDef.values && propDef.values.length > 0) {
3494
+ props[propName] = propDef.values[0];
3495
+ } else if (propDef.type === "boolean") {
3496
+ props[propName] = false;
3497
+ } else if (propDef.type === "number") {
3498
+ props[propName] = 0;
3499
+ }
3500
+ }
3501
+ }
3253
3502
  const { width, height } = parseViewport(opts.viewport);
3254
3503
  const rootDir = process.cwd();
3255
3504
  const filePath = path.resolve(rootDir, descriptor.filePath);
3256
3505
  const scopeData = await loadScopeFileForComponent(filePath);
3257
3506
  const wrapperScript = scopeData?.hasWrapper === true ? await buildWrapperScript(scopeData.filePath) : void 0;
3258
3507
  const scenarios = buildScenarioMap(opts, scopeData);
3259
- const renderer = buildRenderer(filePath, componentName, width, height, wrapperScript);
3508
+ const globalCssFiles = loadGlobalCssFilesFromConfig(rootDir);
3509
+ const renderer = buildRenderer(
3510
+ filePath,
3511
+ componentName,
3512
+ width,
3513
+ height,
3514
+ globalCssFiles,
3515
+ rootDir,
3516
+ wrapperScript
3517
+ );
3260
3518
  process.stderr.write(
3261
3519
  `Rendering ${componentName} [${descriptor.complexityClass}] at ${width}\xD7${height}\u2026
3262
3520
  `
3263
3521
  );
3264
3522
  const fmt2 = resolveSingleFormat(opts.format);
3265
3523
  let anyFailed = false;
3266
- for (const [scenarioName, props] of Object.entries(scenarios)) {
3524
+ for (const [scenarioName, props2] of Object.entries(scenarios)) {
3267
3525
  const isNamed = scenarioName !== "__default__";
3268
3526
  const label = isNamed ? `${componentName}:${scenarioName}` : componentName;
3269
3527
  const outcome = await render.safeRender(
3270
- () => renderer.renderCell(props, descriptor.complexityClass),
3528
+ () => renderer.renderCell(props2, descriptor.complexityClass),
3271
3529
  {
3272
- props,
3530
+ props: props2,
3273
3531
  sourceLocation: {
3274
3532
  file: descriptor.filePath,
3275
3533
  line: descriptor.loc.start,
@@ -3298,7 +3556,7 @@ Available: ${available}`
3298
3556
  `
3299
3557
  );
3300
3558
  } else if (fmt2 === "json") {
3301
- const json = formatRenderJson(label, props, result);
3559
+ const json = formatRenderJson(label, props2, result);
3302
3560
  process.stdout.write(`${JSON.stringify(json, null, 2)}
3303
3561
  `);
3304
3562
  } else {
@@ -3325,7 +3583,10 @@ Available: ${available}`
3325
3583
  );
3326
3584
  }
3327
3585
  function registerRenderMatrix(renderCmd) {
3328
- 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(
3586
+ renderCmd.command("matrix <component>").description("Render a component across a matrix of prop axes").option(
3587
+ "--axes <spec>",
3588
+ `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"]}'`
3589
+ ).option(
3329
3590
  "--contexts <ids>",
3330
3591
  "Composition context IDs, comma-separated (e.g. centered,rtl,sidebar)"
3331
3592
  ).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(
@@ -3344,21 +3605,47 @@ Available: ${available}`
3344
3605
  const { width, height } = { width: 375, height: 812 };
3345
3606
  const rootDir = process.cwd();
3346
3607
  const filePath = path.resolve(rootDir, descriptor.filePath);
3347
- const renderer = buildRenderer(filePath, componentName, width, height);
3608
+ const matrixCssFiles = loadGlobalCssFilesFromConfig(rootDir);
3609
+ const renderer = buildRenderer(
3610
+ filePath,
3611
+ componentName,
3612
+ width,
3613
+ height,
3614
+ matrixCssFiles,
3615
+ rootDir
3616
+ );
3348
3617
  const axes = [];
3349
3618
  if (opts.axes !== void 0) {
3350
- const axisSpecs = opts.axes.trim().split(/\s+/);
3351
- for (const spec of axisSpecs) {
3352
- const colonIdx = spec.indexOf(":");
3353
- if (colonIdx < 0) {
3354
- throw new Error(`Invalid axis spec "${spec}". Expected format: name:val1,val2,...`);
3619
+ const axesRaw = opts.axes.trim();
3620
+ if (axesRaw.startsWith("{")) {
3621
+ let parsed;
3622
+ try {
3623
+ parsed = JSON.parse(axesRaw);
3624
+ } catch {
3625
+ throw new Error(`Invalid JSON in --axes: ${axesRaw}`);
3355
3626
  }
3356
- const name = spec.slice(0, colonIdx);
3357
- const values = spec.slice(colonIdx + 1).split(",").map((v) => v.trim());
3358
- if (name.length === 0 || values.length === 0) {
3359
- throw new Error(`Invalid axis spec "${spec}"`);
3627
+ for (const [name, vals] of Object.entries(parsed)) {
3628
+ if (!Array.isArray(vals)) {
3629
+ throw new Error(`Axis "${name}" must be an array of values in JSON format`);
3630
+ }
3631
+ axes.push({ name, values: vals.map(String) });
3632
+ }
3633
+ } else {
3634
+ const axisSpecs = axesRaw.split(/\s+/);
3635
+ for (const spec of axisSpecs) {
3636
+ const colonIdx = spec.indexOf(":");
3637
+ if (colonIdx < 0) {
3638
+ throw new Error(
3639
+ `Invalid axis spec "${spec}". Expected format: name:val1,val2,...`
3640
+ );
3641
+ }
3642
+ const name = spec.slice(0, colonIdx);
3643
+ const values = spec.slice(colonIdx + 1).split(",").map((v) => v.trim());
3644
+ if (name.length === 0 || values.length === 0) {
3645
+ throw new Error(`Invalid axis spec "${spec}"`);
3646
+ }
3647
+ axes.push({ name, values });
3360
3648
  }
3361
- axes.push({ name, values });
3362
3649
  }
3363
3650
  }
3364
3651
  if (opts.contexts !== void 0) {
@@ -3477,7 +3764,8 @@ function registerRenderAll(renderCmd) {
3477
3764
  const descriptor = manifest.components[name];
3478
3765
  if (descriptor === void 0) return;
3479
3766
  const filePath = path.resolve(rootDir, descriptor.filePath);
3480
- const renderer = buildRenderer(filePath, name, 375, 812);
3767
+ const allCssFiles = loadGlobalCssFilesFromConfig(process.cwd());
3768
+ const renderer = buildRenderer(filePath, name, 375, 812, allCssFiles, process.cwd());
3481
3769
  const outcome = await render.safeRender(
3482
3770
  () => renderer.renderCell({}, descriptor.complexityClass),
3483
3771
  {
@@ -3743,12 +4031,12 @@ async function runBaseline(options = {}) {
3743
4031
  fs.mkdirSync(rendersDir, { recursive: true });
3744
4032
  let manifest$1;
3745
4033
  if (manifestPath !== void 0) {
3746
- const { readFileSync: readFileSync12 } = await import('fs');
4034
+ const { readFileSync: readFileSync13 } = await import('fs');
3747
4035
  const absPath = path.resolve(rootDir, manifestPath);
3748
4036
  if (!fs.existsSync(absPath)) {
3749
4037
  throw new Error(`Manifest not found at ${absPath}.`);
3750
4038
  }
3751
- manifest$1 = JSON.parse(readFileSync12(absPath, "utf-8"));
4039
+ manifest$1 = JSON.parse(readFileSync13(absPath, "utf-8"));
3752
4040
  process.stderr.write(`Loaded manifest from ${manifestPath}
3753
4041
  `);
3754
4042
  } else {
@@ -5109,10 +5397,20 @@ function createTokensExportCommand() {
5109
5397
  ).action(
5110
5398
  (opts) => {
5111
5399
  if (!SUPPORTED_FORMATS.includes(opts.format)) {
5400
+ const FORMAT_ALIASES = {
5401
+ json: "flat-json",
5402
+ "json-flat": "flat-json",
5403
+ javascript: "ts",
5404
+ js: "ts",
5405
+ sass: "scss",
5406
+ tw: "tailwind"
5407
+ };
5408
+ const hint = FORMAT_ALIASES[opts.format.toLowerCase()];
5112
5409
  process.stderr.write(
5113
5410
  `Error: unsupported format "${opts.format}".
5114
5411
  Supported formats: ${SUPPORTED_FORMATS.join(", ")}
5115
- `
5412
+ ` + (hint ? `Did you mean "${hint}"?
5413
+ ` : "")
5116
5414
  );
5117
5415
  process.exit(1);
5118
5416
  }