@ikas/component-cli 1.4.0-beta.186 → 1.4.0-beta.188

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.
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuVpC,eAAO,MAAM,qBAAqB,SAAU;IAC1C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,KAAG,OAA2D,CAAC;AAEhE;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,MAAM,GACf;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAqBlE;AA2uBD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CA0B1E;AAMD,eAAO,MAAM,kBAAkB,UAkB9B,CAAC;AAOF,eAAO,MAAM,oBAAoB,aACrB,MAAM,YACN,MAAM,gBACF,OAAO,KACpB,IAUF,CAAC;AAuEF,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,CAwBf;AAOD;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,EAChB,oBAAoB,CAAC,EAAE,MAAM,EAAE,EAC/B,oBAAoB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GACjC,MAAM,GAAG,IAAI,CA4Cf;AAoDD,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,CAoBf;AAUD,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,OAAO,EACd,eAAe,EACX;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAChE,SAAS,EACb,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,CAsCf;AASD,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,CAgBf;AA+FD,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,OAAO,GACd,MAAM,GAAG,IAAI,CAwBf;AA85BD,wBAAgB,mBAAmB,IAAI,OAAO,CA+U7C"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmXpC,eAAO,MAAM,qBAAqB,SAAU;IAC1C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,KAAG,OAA2D,CAAC;AAEhE;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,MAAM,GACf;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAqBlE;AAmyBD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CA0B1E;AAMD,eAAO,MAAM,kBAAkB,UAkB9B,CAAC;AAOF,eAAO,MAAM,oBAAoB,aACrB,MAAM,YACN,MAAM,gBACF,OAAO,KACpB,IAUF,CAAC;AAuEF,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,CAwBf;AAOD;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,EAChB,oBAAoB,CAAC,EAAE,MAAM,EAAE,EAC/B,oBAAoB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GACjC,MAAM,GAAG,IAAI,CAuDf;AAoDD,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,CAoBf;AAUD,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,OAAO,EACd,eAAe,EACX;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAChE,SAAS,EACb,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,CAsCf;AASD,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,CAgBf;AA+FD,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,OAAO,GACd,MAAM,GAAG,IAAI,CAwBf;AA4iCD,wBAAgB,mBAAmB,IAAI,OAAO,CA+X7C"}
@@ -1,7 +1,7 @@
1
1
  import { Command } from "commander";
2
2
  import * as fs from "fs";
3
3
  import * as path from "path";
4
- import { PROP_TYPES, toPascalCase, generateTypesFile, isBuiltInEnumTypeId, generateGlobalTypesFile, collectUsedEnumIds, generateComponentFile, generateStylesFile, generateProjectId, isValidProjectId, generateComponentId, generateUniqueId, updateBarrelExport, findPropGroup, collectPropGroupIds, movePropGroupInTree, validateFilteredComponentIds, } from "../utils/component-helpers.js";
4
+ import { PROP_TYPES, toPascalCase, generateTypesFile, isBuiltInEnumTypeId, generateGlobalTypesFile, collectUsedEnumIds, generateComponentFile, generateStylesFile, generateProjectId, isValidProjectId, generateComponentId, generateUniqueId, updateBarrelExport, findPropGroup, collectPropGroupIds, movePropGroupInTree, validateFilteredComponentIds, visibilityGateError, pruneCustomTypesNoLongerUsed, usedCustomTypeIds, } from "../utils/component-helpers.js";
5
5
  function loadConfig() {
6
6
  const configPath = path.resolve(process.cwd(), "ikas.config.json");
7
7
  if (!fs.existsSync(configPath)) {
@@ -168,6 +168,10 @@ const ALLOWED_PROP_FIELDS = new Set([
168
168
  "filteredComponentIds",
169
169
  "privateVarMap",
170
170
  "numberRangeData",
171
+ "requiredProp",
172
+ "requiredPropValue",
173
+ "requiredEnumOptionId",
174
+ "requiredEnumOptionValue",
171
175
  ]);
172
176
  // Emit the standard CLI error envelope and exit(1) when a validator returned a message; a no-op
173
177
  // when the message is null. Collapses the repeated `if (err) { console.log(...); process.exit(1) }`
@@ -178,6 +182,24 @@ function failIf(error) {
178
182
  process.exit(1);
179
183
  }
180
184
  }
185
+ /**
186
+ * A `--requiredPropValue` flag as a boolean. Commander hands every flag over as a string, and this is
187
+ * the one place that turns it back; anything other than true/false is refused rather than coerced,
188
+ * since "0" or "off" silently becoming `true` is exactly the kind of gate nobody would think to
189
+ * check.
190
+ */
191
+ function parseRequiredPropValue(raw, where) {
192
+ if (raw === undefined)
193
+ return undefined;
194
+ if (typeof raw === "boolean")
195
+ return raw;
196
+ if (raw === "true")
197
+ return true;
198
+ if (raw === "false")
199
+ return false;
200
+ failIf(`--requiredPropValue for ${where} must be "true" or "false" (got "${raw}").`);
201
+ return undefined;
202
+ }
181
203
  /**
182
204
  * Validate/sanitize a numberRangeData object for a NUMBER_RANGE prop. Accepts an object
183
205
  * or a JSON string. min/max/interval are truncated to integers; only a positive interval
@@ -434,9 +456,30 @@ async function parsePropsFlag(propsJson, config, configPath) {
434
456
  ...(propType === "NUMBER_RANGE"
435
457
  ? { numberRangeData: requireNumberRangeData(r.numberRangeData, r.name) }
436
458
  : {}),
459
+ ...(r.requiredProp !== undefined
460
+ ? { requiredProp: r.requiredProp }
461
+ : {}),
462
+ ...(r.requiredPropValue !== undefined
463
+ ? { requiredPropValue: r.requiredPropValue }
464
+ : {}),
465
+ ...(r.requiredEnumOptionId !== undefined
466
+ ? { requiredEnumOptionId: r.requiredEnumOptionId }
467
+ : {}),
468
+ ...(r.requiredEnumOptionValue !== undefined
469
+ ? { requiredEnumOptionValue: r.requiredEnumOptionValue }
470
+ : {}),
437
471
  };
438
472
  props.push(prop);
439
473
  }
474
+ // Gates are checked once the whole batch is parsed: a prop may name one declared after it, and
475
+ // cycle detection needs every prop in hand. The batch IS the component's prop list here — the
476
+ // component itself does not exist yet — so it stands in as one.
477
+ const batch = { name: "the new component", props };
478
+ for (const prop of props) {
479
+ failIf(visibilityGateError(batch, prop, `prop "${prop.name}"`, {
480
+ selfPropName: prop.name,
481
+ }));
482
+ }
440
483
  return props;
441
484
  }
442
485
  // --- Subcommands ---
@@ -690,6 +733,7 @@ async function addProp(ref, options) {
690
733
  const numError = validateNumberDefaultValue(options.defaultValue, options.name);
691
734
  failIf(numError);
692
735
  }
736
+ const parsedVisibilityValue = parseRequiredPropValue(options.requiredPropValue, `prop "${options.name}"`);
693
737
  const parsedNumberRangeData = propType === "NUMBER_RANGE"
694
738
  ? requireNumberRangeData(options.numberRangeData, options.name)
695
739
  : undefined;
@@ -714,7 +758,21 @@ async function addProp(ref, options) {
714
758
  ...(parsedNumberRangeData
715
759
  ? { numberRangeData: parsedNumberRangeData }
716
760
  : {}),
761
+ ...(options.requiredProp ? { requiredProp: options.requiredProp } : {}),
762
+ ...(parsedVisibilityValue !== undefined
763
+ ? { requiredPropValue: parsedVisibilityValue }
764
+ : {}),
765
+ ...(options.requiredProp && options.requiredEnumOptionValue !== undefined
766
+ ? {
767
+ // The editor has no separate option id; the value doubles as one.
768
+ requiredEnumOptionId: options.requiredEnumOptionValue,
769
+ requiredEnumOptionValue: options.requiredEnumOptionValue,
770
+ }
771
+ : {}),
717
772
  };
773
+ failIf(visibilityGateError(component, newProp, `prop "${newProp.name}"`, {
774
+ selfPropName: newProp.name,
775
+ }));
718
776
  component.props.push(newProp);
719
777
  saveConfig(configPath, config);
720
778
  // Regenerate types.ts and global types
@@ -727,7 +785,6 @@ async function addProp(ref, options) {
727
785
  name: newProp.name,
728
786
  displayName: newProp.displayName,
729
787
  type: newProp.type,
730
- required: newProp.required,
731
788
  ...(newProp.groupId ? { groupId: newProp.groupId } : {}),
732
789
  ...(newProp.enumTypeId ? { enumTypeId: newProp.enumTypeId } : {}),
733
790
  ...(newProp.filteredComponentIds
@@ -739,6 +796,14 @@ async function addProp(ref, options) {
739
796
  ...(newProp.numberRangeData
740
797
  ? { numberRangeData: newProp.numberRangeData }
741
798
  : {}),
799
+ ...(newProp.requiredProp
800
+ ? {
801
+ requiredProp: newProp.requiredProp,
802
+ ...(newProp.requiredEnumOptionValue !== undefined
803
+ ? { requiredEnumOptionValue: newProp.requiredEnumOptionValue }
804
+ : { requiredPropValue: newProp.requiredPropValue ?? true }),
805
+ }
806
+ : {}),
742
807
  },
743
808
  }));
744
809
  }
@@ -871,9 +936,9 @@ export function validateLinkDefaultValue(propType, value, propName) {
871
936
  const links = value.links;
872
937
  return firstError(links, (link, i) => validateSingleLink(link, propName, `.links[${i}]`));
873
938
  }
874
- const COMPONENT_SHAPE_HELP = 'A COMPONENT / COMPONENT_LIST default must be an object listing child code components: ' +
939
+ const COMPONENT_SHAPE_HELP = "A COMPONENT / COMPONENT_LIST default must be an object listing child code components: " +
875
940
  '{ "components": [ { "codeComponentId": "<id>" } ] }. Only code components are allowed; each entry ' +
876
- "needs a string \"codeComponentId\" (no componentId, no inline propValues).";
941
+ 'needs a string "codeComponentId" (no componentId, no inline propValues).';
877
942
  /**
878
943
  * Validate a COMPONENT / COMPONENT_LIST defaultValue. Reference-only: each entry must name a code
879
944
  * component via `codeComponentId`. Rejects non-CC (`componentId`) children, missing ids, more than
@@ -887,14 +952,19 @@ export function validateComponentDefaultValue(propType, value, propName, filtere
887
952
  if (typeof value === "string") {
888
953
  return `${where} is a JSON string but must be an object. ${COMPONENT_SHAPE_HELP}`;
889
954
  }
890
- if (!value || typeof value !== "object" || Array.isArray(value) || !Array.isArray(value.components)) {
955
+ if (!value ||
956
+ typeof value !== "object" ||
957
+ Array.isArray(value) ||
958
+ !Array.isArray(value.components)) {
891
959
  return `${where} must be an object with a "components" array. ${COMPONENT_SHAPE_HELP}`;
892
960
  }
893
961
  const components = value.components;
894
962
  if (propType === "COMPONENT" && components.length > 1) {
895
963
  return `${where}: a COMPONENT prop holds a single child — provide at most one entry (use COMPONENT_LIST for many).`;
896
964
  }
897
- const allow = filteredComponentIds && filteredComponentIds.length ? new Set(filteredComponentIds) : null;
965
+ const allow = filteredComponentIds && filteredComponentIds.length
966
+ ? new Set(filteredComponentIds)
967
+ : null;
898
968
  return firstError(components, (entry, i) => {
899
969
  const at = `${where}.components[${i}]`;
900
970
  if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
@@ -904,7 +974,8 @@ export function validateComponentDefaultValue(propType, value, propName, filtere
904
974
  if ("componentId" in e && !("codeComponentId" in e)) {
905
975
  return `${at} references a non-code component (componentId). Only code components are allowed as defaults. ${COMPONENT_SHAPE_HELP}`;
906
976
  }
907
- if (typeof e.codeComponentId !== "string" || e.codeComponentId.trim().length === 0) {
977
+ if (typeof e.codeComponentId !== "string" ||
978
+ e.codeComponentId.trim().length === 0) {
908
979
  return `${at} must have a non-empty string "codeComponentId". ${COMPONENT_SHAPE_HELP}`;
909
980
  }
910
981
  if (allow && !allow.has(e.codeComponentId)) {
@@ -1165,6 +1236,7 @@ async function updateProp(ref, options) {
1165
1236
  process.exit(1);
1166
1237
  }
1167
1238
  const prop = component.props[propIndex];
1239
+ const previouslyUsedTypeIds = usedCustomTypeIds(config);
1168
1240
  if (options.displayName !== undefined) {
1169
1241
  prop.displayName = options.displayName;
1170
1242
  }
@@ -1338,6 +1410,35 @@ async function updateProp(ref, options) {
1338
1410
  }
1339
1411
  }
1340
1412
  }
1413
+ if (options.requiredProp !== undefined) {
1414
+ if (options.requiredProp === "" || options.requiredProp === "none") {
1415
+ // Every field goes together — see `IProp.requiredPropId`.
1416
+ delete prop.requiredProp;
1417
+ delete prop.requiredPropValue;
1418
+ delete prop.requiredEnumOptionId;
1419
+ delete prop.requiredEnumOptionValue;
1420
+ }
1421
+ else {
1422
+ prop.requiredProp = options.requiredProp;
1423
+ }
1424
+ }
1425
+ if (options.requiredEnumOptionValue !== undefined && prop.requiredProp) {
1426
+ prop.requiredEnumOptionId = options.requiredEnumOptionValue;
1427
+ prop.requiredEnumOptionValue = options.requiredEnumOptionValue;
1428
+ delete prop.requiredPropValue;
1429
+ }
1430
+ if (options.requiredPropValue !== undefined && prop.requiredProp) {
1431
+ delete prop.requiredEnumOptionId;
1432
+ delete prop.requiredEnumOptionValue;
1433
+ prop.requiredPropValue = parseRequiredPropValue(options.requiredPropValue, `prop "${prop.name}"`);
1434
+ }
1435
+ // Re-checked on every update, not only when a visibility flag is passed: flipping `required`, or
1436
+ // renaming the prop this one hangs on, can invalidate a gate that was sound when it was written.
1437
+ failIf(visibilityGateError(component, prop, `prop "${prop.name}"`, {
1438
+ selfPropName: prop.name,
1439
+ }));
1440
+ // Re-typing or re-binding the prop may leave its old enum unreferenced.
1441
+ pruneCustomTypesNoLongerUsed(config, previouslyUsedTypeIds);
1341
1442
  saveConfig(configPath, config);
1342
1443
  // Regenerate types.ts and global types
1343
1444
  regenerateTypes(component, undefined, config);
@@ -1349,7 +1450,6 @@ async function updateProp(ref, options) {
1349
1450
  name: prop.name,
1350
1451
  displayName: prop.displayName,
1351
1452
  type: prop.type,
1352
- required: prop.required,
1353
1453
  ...(prop.groupId ? { groupId: prop.groupId } : {}),
1354
1454
  ...(prop.typeId ? { typeId: prop.typeId } : {}),
1355
1455
  ...(prop.enumTypeId ? { enumTypeId: prop.enumTypeId } : {}),
@@ -1360,6 +1460,14 @@ async function updateProp(ref, options) {
1360
1460
  ...(prop.numberRangeData
1361
1461
  ? { numberRangeData: prop.numberRangeData }
1362
1462
  : {}),
1463
+ ...(prop.requiredProp
1464
+ ? {
1465
+ requiredProp: prop.requiredProp,
1466
+ ...(prop.requiredEnumOptionValue !== undefined
1467
+ ? { requiredEnumOptionValue: prop.requiredEnumOptionValue }
1468
+ : { requiredPropValue: prop.requiredPropValue ?? true }),
1469
+ }
1470
+ : {}),
1363
1471
  },
1364
1472
  }));
1365
1473
  }
@@ -1374,7 +1482,9 @@ function removeProp(ref, options) {
1374
1482
  }));
1375
1483
  process.exit(1);
1376
1484
  }
1485
+ const previouslyUsedTypeIds = usedCustomTypeIds(config);
1377
1486
  component.props.splice(propIndex, 1);
1487
+ pruneCustomTypesNoLongerUsed(config, previouslyUsedTypeIds);
1378
1488
  saveConfig(configPath, config);
1379
1489
  // Regenerate types.ts and global types
1380
1490
  regenerateTypes(component, undefined, config);
@@ -1446,7 +1556,24 @@ function addPropGroup(ref, options) {
1446
1556
  id: options.id,
1447
1557
  name: options.name,
1448
1558
  ...(options.description ? { description: options.description } : {}),
1559
+ ...(options.requiredProp ? { requiredProp: options.requiredProp } : {}),
1560
+ ...(options.requiredProp && options.requiredEnumOptionValue !== undefined
1561
+ ? {
1562
+ requiredEnumOptionId: options.requiredEnumOptionValue,
1563
+ requiredEnumOptionValue: options.requiredEnumOptionValue,
1564
+ }
1565
+ : {}),
1566
+ ...(options.requiredProp &&
1567
+ options.requiredEnumOptionValue === undefined &&
1568
+ options.requiredPropValue !== undefined
1569
+ ? {
1570
+ requiredPropValue: parseRequiredPropValue(options.requiredPropValue, `group "${options.id}"`),
1571
+ }
1572
+ : {}),
1449
1573
  };
1574
+ failIf(visibilityGateError(component, newGroup, `group "${newGroup.id}"`, {
1575
+ group: newGroup,
1576
+ }));
1450
1577
  if (options.parent) {
1451
1578
  const found = findPropGroup(component.propGroups, options.parent);
1452
1579
  if (!found) {
@@ -1478,6 +1605,14 @@ function addPropGroup(ref, options) {
1478
1605
  id: newGroup.id,
1479
1606
  name: newGroup.name,
1480
1607
  ...(options.parent ? { parentId: options.parent } : {}),
1608
+ ...(newGroup.requiredProp
1609
+ ? {
1610
+ requiredProp: newGroup.requiredProp,
1611
+ ...(newGroup.requiredEnumOptionValue !== undefined
1612
+ ? { requiredEnumOptionValue: newGroup.requiredEnumOptionValue }
1613
+ : { requiredPropValue: newGroup.requiredPropValue ?? true }),
1614
+ }
1615
+ : {}),
1481
1616
  },
1482
1617
  }));
1483
1618
  }
@@ -1503,6 +1638,34 @@ function updatePropGroup(ref, options) {
1503
1638
  found.group.name = options.name;
1504
1639
  if (options.description !== undefined)
1505
1640
  found.group.description = options.description || undefined;
1641
+ if (options.requiredProp !== undefined) {
1642
+ if (options.requiredProp === "" || options.requiredProp === "none") {
1643
+ // Every field goes together — see `IThemePropGroup.requiredPropId`.
1644
+ delete found.group.requiredProp;
1645
+ delete found.group.requiredPropValue;
1646
+ delete found.group.requiredEnumOptionId;
1647
+ delete found.group.requiredEnumOptionValue;
1648
+ }
1649
+ else {
1650
+ found.group.requiredProp = options.requiredProp;
1651
+ }
1652
+ }
1653
+ if (options.requiredEnumOptionValue !== undefined &&
1654
+ found.group.requiredProp) {
1655
+ found.group.requiredEnumOptionId = options.requiredEnumOptionValue;
1656
+ found.group.requiredEnumOptionValue = options.requiredEnumOptionValue;
1657
+ delete found.group.requiredPropValue;
1658
+ }
1659
+ if (options.requiredPropValue !== undefined && found.group.requiredProp) {
1660
+ delete found.group.requiredEnumOptionId;
1661
+ delete found.group.requiredEnumOptionValue;
1662
+ found.group.requiredPropValue = parseRequiredPropValue(options.requiredPropValue, `group "${found.group.id}"`);
1663
+ }
1664
+ // Re-checked on every update: moving a prop into this group can invalidate a gate that was sound
1665
+ // when it was written.
1666
+ failIf(visibilityGateError(component, found.group, `group "${found.group.id}"`, {
1667
+ group: found.group,
1668
+ }));
1506
1669
  saveConfig(configPath, config);
1507
1670
  console.log(JSON.stringify({
1508
1671
  success: true,
@@ -1510,6 +1673,16 @@ function updatePropGroup(ref, options) {
1510
1673
  updatedPropGroup: {
1511
1674
  id: found.group.id,
1512
1675
  name: found.group.name,
1676
+ ...(found.group.requiredProp
1677
+ ? {
1678
+ requiredProp: found.group.requiredProp,
1679
+ ...(found.group.requiredEnumOptionValue !== undefined
1680
+ ? {
1681
+ requiredEnumOptionValue: found.group.requiredEnumOptionValue,
1682
+ }
1683
+ : { requiredPropValue: found.group.requiredPropValue ?? true }),
1684
+ }
1685
+ : {}),
1513
1686
  },
1514
1687
  }));
1515
1688
  }
@@ -1762,6 +1935,9 @@ function updateEnum(options) {
1762
1935
  }
1763
1936
  enumType.enumOptions = enumOptions;
1764
1937
  }
1938
+ // Stamp so the editor accepts this as newer than its own copy (it otherwise
1939
+ // only adds unknown ids and would revert this edit on its next sync-types).
1940
+ enumType.updatedAt = Date.now();
1765
1941
  saveConfig(configPath, config);
1766
1942
  // Regenerate global types and all component types
1767
1943
  regenerateGlobalTypes(config);
@@ -1859,6 +2035,9 @@ export function createConfigCommand() {
1859
2035
  .option("--filteredComponentIds <json>", "JSON array of component IDs to restrict selection (for COMPONENT/COMPONENT_LIST)")
1860
2036
  .option("--privateVarMap <json>", "JSON object mapping variable keys to {id, typeId} (for COMPONENT/COMPONENT_LIST)")
1861
2037
  .option("--numberRangeData <json>", 'JSON slider config for NUMBER_RANGE props, e.g. \'{"min":0,"max":100,"interval":5,"unit":"px"}\'')
2038
+ .option("--requiredProp <propName>", 'Show this only while the named BOOLEAN or ENUM prop satisfies the condition ("Koşullu Prop")')
2039
+ .option("--requiredPropValue <boolean>", "For a BOOLEAN --requiredProp: which state shows it, true (default) or false")
2040
+ .option("--requiredEnumOptionValue <value>", "For an ENUM --requiredProp: the option value that shows it")
1862
2041
  .action((options) => {
1863
2042
  addProp({ id: options.componentId, name: options.component }, options);
1864
2043
  });
@@ -1879,6 +2058,9 @@ export function createConfigCommand() {
1879
2058
  .option("--filteredComponentIds <json>", "JSON array of component IDs (use 'none' to clear)")
1880
2059
  .option("--privateVarMap <json>", "JSON object mapping variable keys to {id, typeId} (use 'none' to clear)")
1881
2060
  .option("--numberRangeData <json>", "JSON slider config for NUMBER_RANGE props (use 'none' to clear)")
2061
+ .option("--requiredProp <propName>", "BOOLEAN or ENUM prop that gates this prop in the editor panel (use 'none' to clear)")
2062
+ .option("--requiredPropValue <boolean>", "For a BOOLEAN --requiredProp: which state shows it, true (default) or false")
2063
+ .option("--requiredEnumOptionValue <value>", "For an ENUM --requiredProp: the option value that shows it")
1882
2064
  .action((options) => {
1883
2065
  updateProp({ id: options.componentId, name: options.component }, options);
1884
2066
  });
@@ -1920,6 +2102,9 @@ export function createConfigCommand() {
1920
2102
  .requiredOption("--name <name>", "Display name in editor")
1921
2103
  .option("--description <description>", "Group description")
1922
2104
  .option("--parent <parentId>", "Parent group ID (for nesting, max 1 level)")
2105
+ .option("--requiredProp <propName>", 'Show this only while the named BOOLEAN or ENUM prop satisfies the condition ("Koşullu Prop")')
2106
+ .option("--requiredPropValue <boolean>", "For a BOOLEAN --requiredProp: which state shows it, true (default) or false")
2107
+ .option("--requiredEnumOptionValue <value>", "For an ENUM --requiredProp: the option value that shows it")
1923
2108
  .action((options) => {
1924
2109
  addPropGroup({ id: options.componentId, name: options.component }, options);
1925
2110
  });
@@ -1931,6 +2116,9 @@ export function createConfigCommand() {
1931
2116
  .requiredOption("--id <id>", "Group ID to update")
1932
2117
  .option("--name <name>", "New display name")
1933
2118
  .option("--description <description>", "New description")
2119
+ .option("--requiredProp <propName>", "BOOLEAN or ENUM prop that gates this group in the editor panel (use 'none' to clear)")
2120
+ .option("--requiredPropValue <boolean>", "For a BOOLEAN --requiredProp: which state shows it, true (default) or false")
2121
+ .option("--requiredEnumOptionValue <value>", "For an ENUM --requiredProp: the option value that shows it")
1934
2122
  .action((options) => {
1935
2123
  updatePropGroup({ id: options.componentId, name: options.component }, options);
1936
2124
  });