wlmaker 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.mjs +856 -152
  2. package/package.json +2 -1
package/dist/cli.mjs CHANGED
@@ -3,7 +3,7 @@
3
3
  // src/cli.ts
4
4
  import { createRequire } from "module";
5
5
  import { Command } from "commander";
6
- import chalk9 from "chalk";
6
+ import chalk10 from "chalk";
7
7
 
8
8
  // src/core/create-bloc.ts
9
9
  import * as fs3 from "fs";
@@ -956,12 +956,12 @@ async function createUseCase(name, tierInput, options) {
956
956
  }
957
957
 
958
958
  // src/interactive.ts
959
- import * as fs13 from "fs";
959
+ import * as fs14 from "fs";
960
960
  import * as os from "os";
961
- import * as path12 from "path";
962
- import { execSync } from "child_process";
961
+ import * as path13 from "path";
962
+ import { execSync as execSync2 } from "child_process";
963
963
  import * as clack from "@clack/prompts";
964
- import chalk8 from "chalk";
964
+ import chalk9 from "chalk";
965
965
 
966
966
  // src/core/create-endpoint.ts
967
967
  import * as fs9 from "fs";
@@ -1050,8 +1050,8 @@ ${paramsClass}
1050
1050
  }
1051
1051
  `;
1052
1052
  }
1053
- function retrofitMethod(methodName, path13, httpMethod, params, returnType) {
1054
- const httpAnnotation = `@${httpMethod.toUpperCase()}('${path13}')`;
1053
+ function retrofitMethod(methodName, path14, httpMethod, params, returnType) {
1054
+ const httpAnnotation = `@${httpMethod.toUpperCase()}('${path14}')`;
1055
1055
  const paramList = params.map((p) => {
1056
1056
  if (p.isPath) return `@Path('${p.name}') ${p.type} ${p.name}`;
1057
1057
  if (p.isBody) return `@Body() ${p.type} ${p.name}`;
@@ -1212,15 +1212,15 @@ async function createEndpoint(options) {
1212
1212
  const methodParamsForSignature = methodParams.map((p) => ({ name: p.name, type: p.type }));
1213
1213
  const modelType = `${pascal}Model`;
1214
1214
  const entityType = `${pascal}Entity`;
1215
- const spinner2 = (msg) => console.log(chalk4.cyan(` \u2192 ${msg}`));
1216
- spinner2("Generating entity");
1215
+ const spinner3 = (msg) => console.log(chalk4.cyan(` \u2192 ${msg}`));
1216
+ spinner3("Generating entity");
1217
1217
  const entityDir = path8.join(lib, "domain", "entities", feature);
1218
1218
  fs9.mkdirSync(entityDir, { recursive: true });
1219
1219
  fs9.writeFileSync(
1220
1220
  path8.join(entityDir, `${useCaseSnake}_entity.dart`),
1221
1221
  entityBoilerplate(pascal)
1222
1222
  );
1223
- spinner2("Generating model");
1223
+ spinner3("Generating model");
1224
1224
  const modelDir = path8.join(lib, "data", "models", feature);
1225
1225
  fs9.mkdirSync(modelDir, { recursive: true });
1226
1226
  fs9.writeFileSync(
@@ -1228,7 +1228,7 @@ async function createEndpoint(options) {
1228
1228
  modelBoilerplate(useCaseSnake, pascal, options.projectName)
1229
1229
  );
1230
1230
  if (needsBody) {
1231
- spinner2("Generating request model");
1231
+ spinner3("Generating request model");
1232
1232
  const reqModelDir = path8.join(lib, "data", "models", feature);
1233
1233
  fs9.mkdirSync(reqModelDir, { recursive: true });
1234
1234
  fs9.writeFileSync(
@@ -1236,7 +1236,7 @@ async function createEndpoint(options) {
1236
1236
  requestModelBoilerplate(useCaseSnake, pascal)
1237
1237
  );
1238
1238
  }
1239
- spinner2("Injecting Retrofit method");
1239
+ spinner3("Injecting Retrofit method");
1240
1240
  const bffPath = path8.resolve(options.bffApiFile);
1241
1241
  if (fs9.existsSync(bffPath)) {
1242
1242
  const retrofitParams = buildRetrofitParams(options, pathParams, needsBody);
@@ -1251,7 +1251,7 @@ async function createEndpoint(options) {
1251
1251
  } else {
1252
1252
  console.log(chalk4.yellow(` \u26A0 BFF API file not found: ${bffPath}`));
1253
1253
  }
1254
- spinner2("Injecting datasource method");
1254
+ spinner3("Injecting datasource method");
1255
1255
  const dsPath = path8.resolve(datasourceFile);
1256
1256
  if (fs9.existsSync(dsPath)) {
1257
1257
  const dsMethod = datasourceMethod(
@@ -1263,7 +1263,7 @@ async function createEndpoint(options) {
1263
1263
  } else {
1264
1264
  console.log(chalk4.yellow(` \u26A0 Datasource file not found: ${dsPath}`));
1265
1265
  }
1266
- spinner2("Injecting repository interface method");
1266
+ spinner3("Injecting repository interface method");
1267
1267
  const repoIfacePath = path8.resolve(repositoryInterfaceFile);
1268
1268
  if (fs9.existsSync(repoIfacePath)) {
1269
1269
  const ifaceMethod = repositoryInterfaceMethod(
@@ -1275,7 +1275,7 @@ async function createEndpoint(options) {
1275
1275
  } else {
1276
1276
  console.log(chalk4.yellow(` \u26A0 Repository interface not found: ${repoIfacePath}`));
1277
1277
  }
1278
- spinner2("Injecting repository implementation method");
1278
+ spinner3("Injecting repository implementation method");
1279
1279
  const repoImplPath = path8.resolve(repositoryImplFile);
1280
1280
  if (fs9.existsSync(repoImplPath)) {
1281
1281
  const dsVarName = camelCase2(datasourceClassName);
@@ -1290,7 +1290,7 @@ async function createEndpoint(options) {
1290
1290
  } else {
1291
1291
  console.log(chalk4.yellow(` \u26A0 Repository implementation not found: ${repoImplPath}`));
1292
1292
  }
1293
- spinner2("Generating UseCase");
1293
+ spinner3("Generating UseCase");
1294
1294
  const useCaseDir = path8.join(lib, "domain", "usecases", feature);
1295
1295
  fs9.mkdirSync(useCaseDir, { recursive: true });
1296
1296
  fs9.writeFileSync(
@@ -1304,7 +1304,7 @@ async function createEndpoint(options) {
1304
1304
  options.projectName
1305
1305
  )
1306
1306
  );
1307
- spinner2("Updating barrel files");
1307
+ spinner3("Updating barrel files");
1308
1308
  const dsFileName = path8.basename(datasourceFile);
1309
1309
  injectExport(
1310
1310
  path8.join(lib, "data", "datasources", "datasources.dart"),
@@ -1332,7 +1332,7 @@ async function createEndpoint(options) {
1332
1332
  `export '${feature}/${feature}.dart';`
1333
1333
  );
1334
1334
  if (options.diTarget && options.diTarget !== "none") {
1335
- spinner2("Registering in DI modules");
1335
+ spinner3("Registering in DI modules");
1336
1336
  const appBaseDir = findAppBasePackageDir(options.projectRoot, options.diTarget);
1337
1337
  if (appBaseDir) {
1338
1338
  const domainPascal = pascalCase4(domain);
@@ -1465,24 +1465,594 @@ function injectModuleRegistration(filePath, className, registrationCode, dedupKe
1465
1465
  fs9.writeFileSync(filePath, newContent);
1466
1466
  }
1467
1467
 
1468
- // src/core/docs-serve.ts
1469
- import { spawn as spawn2 } from "child_process";
1468
+ // src/core/create-env-var.ts
1470
1469
  import * as fs10 from "fs";
1471
1470
  import * as path9 from "path";
1471
+ import { execSync } from "child_process";
1472
1472
  import chalk5 from "chalk";
1473
+ import { camelCase as camelCase3 } from "change-case";
1474
+ function screamingSnakeToCamel(name) {
1475
+ return camelCase3(name.toLowerCase());
1476
+ }
1477
+ function dartTypeForGetter(type) {
1478
+ return type;
1479
+ }
1480
+ function typedJsonValue(defaultValue, dartType) {
1481
+ if (!defaultValue || !defaultValue.trim()) {
1482
+ switch (dartType) {
1483
+ case "int":
1484
+ return 0;
1485
+ case "bool":
1486
+ return false;
1487
+ case "List<String>":
1488
+ return [];
1489
+ default:
1490
+ return "";
1491
+ }
1492
+ }
1493
+ switch (dartType) {
1494
+ case "int":
1495
+ return parseInt(defaultValue, 10);
1496
+ case "bool":
1497
+ return defaultValue === "true";
1498
+ case "List<String>":
1499
+ return defaultValue.split(",").map((s) => s.trim());
1500
+ default:
1501
+ return defaultValue;
1502
+ }
1503
+ }
1504
+ function abstractGetterCode(camelName, dartType) {
1505
+ const type = dartTypeForGetter(dartType);
1506
+ return `${type} get ${camelName};`;
1507
+ }
1508
+ function buildEnvGetterCode(camelName, constName, dartType) {
1509
+ const type = dartTypeForGetter(dartType);
1510
+ switch (dartType) {
1511
+ case "String":
1512
+ return `@override
1513
+ String get ${camelName} => const String.fromEnvironment('${constName}');`;
1514
+ case "int":
1515
+ return `@override
1516
+ int get ${camelName} => const int.fromEnvironment('${constName}');`;
1517
+ case "bool":
1518
+ return `@override
1519
+ bool get ${camelName} => const bool.fromEnvironment('${constName}');`;
1520
+ case "List<String>":
1521
+ return [
1522
+ "@override",
1523
+ `List<String> get ${camelName} {`,
1524
+ ` const rawString = String.fromEnvironment('${constName}');`,
1525
+ " if (rawString.isEmpty) return [];",
1526
+ " return rawString.split(',').map((e) => e.trim()).toList();",
1527
+ "}"
1528
+ ].join("\n");
1529
+ }
1530
+ }
1531
+ function appConfigDefaultValue(dartType) {
1532
+ switch (dartType) {
1533
+ case "String":
1534
+ return "''";
1535
+ case "int":
1536
+ return "0";
1537
+ case "bool":
1538
+ return "false";
1539
+ case "List<String>":
1540
+ return "const []";
1541
+ }
1542
+ }
1543
+ function appConfigModelJsonKey(camelName, dartType) {
1544
+ switch (dartType) {
1545
+ case "bool":
1546
+ return `@override
1547
+ @JsonKey(name: '${camelName}', fromJson: AppConfigModel.parseBool)`;
1548
+ case "int":
1549
+ return `@override
1550
+ @JsonKey(name: '${camelName}', fromJson: AppConfigModel.parseInt)`;
1551
+ case "List<String>":
1552
+ return `@override
1553
+ @JsonKey(name: '${camelName}', fromJson: AppConfigModel.parseToList)`;
1554
+ default:
1555
+ return `@override
1556
+ @JsonKey(name: '${camelName}')`;
1557
+ }
1558
+ }
1559
+ function discoverAppsWithEnv(monorepoRoot) {
1560
+ const appsDir = path9.join(monorepoRoot, "apps");
1561
+ if (!fs10.existsSync(appsDir)) return [];
1562
+ return fs10.readdirSync(appsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).filter((d) => fs10.existsSync(path9.join(appsDir, d.name, "env"))).map((d) => d.name).sort();
1563
+ }
1564
+ function injectIntoJsonFile(filePath, key, value, isTemplate) {
1565
+ if (!fs10.existsSync(filePath)) {
1566
+ console.log(chalk5.yellow(` Skipping ${filePath} (not found)`));
1567
+ return;
1568
+ }
1569
+ const content = fs10.readFileSync(filePath, "utf8");
1570
+ const json = JSON.parse(content);
1571
+ if (key in json) {
1572
+ console.log(chalk5.yellow(` Key "${key}" already exists in ${path9.basename(filePath)}`));
1573
+ return;
1574
+ }
1575
+ json[key] = isTemplate ? `\${${key}}` : value;
1576
+ fs10.writeFileSync(filePath, JSON.stringify(json, null, 2) + "\n");
1577
+ console.log(chalk5.green(` Updated ${path9.basename(filePath)}`));
1578
+ }
1579
+ function injectAbstractGetter(monorepoRoot, camelName, dartType) {
1580
+ const packagesDir = path9.join(monorepoRoot, "packages");
1581
+ if (!fs10.existsSync(packagesDir)) return;
1582
+ const envFile = findFileInPackages(packagesDir, "app_environment.dart");
1583
+ if (!envFile) {
1584
+ console.log(chalk5.yellow(" app_environment.dart not found in any package"));
1585
+ return;
1586
+ }
1587
+ const content = fs10.readFileSync(envFile, "utf8");
1588
+ const dedup = `get ${camelName}`;
1589
+ if (content.includes(dedup)) {
1590
+ console.log(chalk5.yellow(` '${camelName}' already in AppEnvironment`));
1591
+ return;
1592
+ }
1593
+ const getterRegex = /\n\s+\w+(?:<[^>]+>)?\s+get\s+\w+;/g;
1594
+ let lastGetterMatch = null;
1595
+ let match;
1596
+ while ((match = getterRegex.exec(content)) !== null) {
1597
+ lastGetterMatch = match;
1598
+ }
1599
+ if (!lastGetterMatch) {
1600
+ console.log(chalk5.yellow(" No getters found in AppEnvironment"));
1601
+ return;
1602
+ }
1603
+ const insertPos = lastGetterMatch.index + lastGetterMatch[0].length;
1604
+ const getterLine = `
1605
+ ${abstractGetterCode(camelName, dartType)}`;
1606
+ const newContent = content.slice(0, insertPos) + getterLine + content.slice(insertPos);
1607
+ fs10.writeFileSync(envFile, newContent);
1608
+ console.log(chalk5.green(` Injected abstract getter in ${formatPkgPath(monorepoRoot, envFile)}`));
1609
+ }
1610
+ function injectBuildEnvGetter(monorepoRoot, variableName, camelName, dartType) {
1611
+ const packagesDir = path9.join(monorepoRoot, "packages");
1612
+ if (!fs10.existsSync(packagesDir)) return;
1613
+ const buildEnvFile = findFileInPackages(packagesDir, "app_build_environment.dart");
1614
+ if (!buildEnvFile) {
1615
+ console.log(chalk5.yellow(" app_build_environment.dart not found in any package"));
1616
+ return;
1617
+ }
1618
+ const code = buildEnvGetterCode(camelName, variableName, dartType);
1619
+ try {
1620
+ injectMethod(buildEnvFile, "AppBuildEnvironment", code, `get ${camelName}`);
1621
+ console.log(chalk5.green(` Injected build env getter in ${formatPkgPath(monorepoRoot, buildEnvFile)}`));
1622
+ } catch (e) {
1623
+ console.log(chalk5.yellow(` Skipped: ${e.message}`));
1624
+ }
1625
+ }
1626
+ function injectIntoRemoteConfigDefaults(vendorsPath, camelName, dartType) {
1627
+ const content = fs10.readFileSync(vendorsPath, "utf8");
1628
+ if (content.includes(`'${camelName}'`) || content.includes(`"${camelName}"`)) {
1629
+ console.log(chalk5.yellow(` '${camelName}' already in ${path9.basename(vendorsPath)}`));
1630
+ return;
1631
+ }
1632
+ const defaultValuesRegex = /final\s+defaultValues\s*=\s*(?:<String,\s*dynamic>\s*)?\{/;
1633
+ const match = content.match(defaultValuesRegex);
1634
+ if (!match) {
1635
+ console.log(chalk5.yellow(` Could not find defaultValues map in ${path9.basename(vendorsPath)}`));
1636
+ return;
1637
+ }
1638
+ const mapStart = content.indexOf(match[0]) + match[0].length;
1639
+ let braceCount = 1;
1640
+ let mapEnd = -1;
1641
+ for (let i = mapStart; i < content.length; i++) {
1642
+ if (content[i] === "{") braceCount++;
1643
+ else if (content[i] === "}") {
1644
+ braceCount--;
1645
+ if (braceCount === 0) {
1646
+ mapEnd = i;
1647
+ break;
1648
+ }
1649
+ }
1650
+ }
1651
+ if (mapEnd === -1) {
1652
+ console.log(chalk5.yellow(` Could not find end of defaultValues map`));
1653
+ return;
1654
+ }
1655
+ const valueExpr = dartType === "List<String>" ? `jsonEncode(env.${camelName})` : `env.${camelName}`;
1656
+ const beforeClose = content.slice(0, mapEnd);
1657
+ const mapLines = beforeClose.split("\n");
1658
+ let lastEntryIndent = " ";
1659
+ for (let i = mapLines.length - 1; i >= 0; i--) {
1660
+ const trimmed = mapLines[i].trim();
1661
+ if (trimmed.startsWith("'") || trimmed.startsWith('"')) {
1662
+ const leadingSpaces = mapLines[i].match(/^(\s*)/)?.[1] ?? "";
1663
+ lastEntryIndent = leadingSpaces;
1664
+ break;
1665
+ }
1666
+ }
1667
+ const insertion = `${lastEntryIndent}'${camelName}': ${valueExpr},
1668
+ `;
1669
+ const newContent = content.slice(0, mapEnd) + insertion + content.slice(mapEnd);
1670
+ fs10.writeFileSync(vendorsPath, newContent);
1671
+ console.log(chalk5.green(` Injected into VendorsModule defaultValues`));
1672
+ }
1673
+ function injectAppConfigProperty(configPath, camelName, dartType) {
1674
+ const content = fs10.readFileSync(configPath, "utf8");
1675
+ if (content.includes(`final ${dartTypeForGetter(dartType)} ${camelName}`)) {
1676
+ console.log(chalk5.yellow(` '${camelName}' already in AppConfig`));
1677
+ return;
1678
+ }
1679
+ const fieldLine = ` final ${dartTypeForGetter(dartType)} ${camelName};
1680
+ `;
1681
+ let newContent = content;
1682
+ const propsMatch = newContent.match(/@override\n\s+List<Object\?>/);
1683
+ if (propsMatch) {
1684
+ const propsPos = newContent.indexOf(propsMatch[0]);
1685
+ newContent = newContent.slice(0, propsPos) + fieldLine + newContent.slice(propsPos);
1686
+ } else {
1687
+ newContent = injectBeforeClassClose(newContent, "AppConfig", ` final ${dartTypeForGetter(dartType)} ${camelName};`);
1688
+ }
1689
+ newContent = injectConstructorParam(newContent, "AppConfig", camelName, dartType);
1690
+ newContent = injectIntoPropsList(newContent, camelName);
1691
+ fs10.writeFileSync(configPath, newContent);
1692
+ console.log(chalk5.green(` Injected into AppConfig entity`));
1693
+ }
1694
+ function injectAppConfigModelProperty(modelPath, camelName, dartType) {
1695
+ const content = fs10.readFileSync(modelPath, "utf8");
1696
+ if (content.includes(`final ${dartTypeForGetter(dartType)} ${camelName}`)) {
1697
+ console.log(chalk5.yellow(` '${camelName}' already in AppConfigModel`));
1698
+ return;
1699
+ }
1700
+ const jsonKey = appConfigModelJsonKey(camelName, dartType);
1701
+ const fieldLines = jsonKey ? ` ${jsonKey}
1702
+ final ${dartTypeForGetter(dartType)} ${camelName};
1703
+ ` : ` final ${dartTypeForGetter(dartType)} ${camelName};
1704
+ `;
1705
+ let newContent = content;
1706
+ const staticPos = newContent.search(/\n\s+static\s/);
1707
+ const factoryPos = newContent.search(/\n\s+factory\s+AppConfigModel/);
1708
+ let insertAnchor = -1;
1709
+ if (staticPos !== -1 && factoryPos !== -1) {
1710
+ insertAnchor = Math.min(staticPos, factoryPos);
1711
+ } else if (staticPos !== -1) {
1712
+ insertAnchor = staticPos;
1713
+ } else if (factoryPos !== -1) {
1714
+ insertAnchor = factoryPos;
1715
+ }
1716
+ if (insertAnchor !== -1) {
1717
+ newContent = newContent.slice(0, insertAnchor + 1) + fieldLines + newContent.slice(insertAnchor + 1);
1718
+ } else {
1719
+ newContent = injectBeforeClassClose(newContent, "AppConfigModel", fieldLines);
1720
+ }
1721
+ newContent = injectConstructorParam(newContent, "AppConfigModel", camelName, dartType);
1722
+ newContent = injectIntoSuperCall(newContent, camelName);
1723
+ fs10.writeFileSync(modelPath, newContent);
1724
+ console.log(chalk5.green(` Injected into AppConfigModel`));
1725
+ }
1726
+ function injectBeforeClassClose(content, className, line) {
1727
+ const classRegex = new RegExp(`class\\s+${className}\\s*[^{]*\\{`);
1728
+ const classMatch = content.match(classRegex);
1729
+ if (!classMatch) {
1730
+ throw new Error(`Class "${className}" not found`);
1731
+ }
1732
+ const classStart = content.indexOf(classMatch[0]);
1733
+ let braceCount = 0;
1734
+ let classEnd = -1;
1735
+ let foundOpen = false;
1736
+ for (let i = classStart; i < content.length; i++) {
1737
+ if (content[i] === "{") {
1738
+ braceCount++;
1739
+ foundOpen = true;
1740
+ } else if (content[i] === "}") {
1741
+ braceCount--;
1742
+ if (foundOpen && braceCount === 0) {
1743
+ classEnd = i;
1744
+ break;
1745
+ }
1746
+ }
1747
+ }
1748
+ if (classEnd === -1) {
1749
+ throw new Error(`Could not find closing brace for class "${className}"`);
1750
+ }
1751
+ return content.slice(0, classEnd) + line + "\n" + content.slice(classEnd);
1752
+ }
1753
+ function injectConstructorParam(content, className, camelName, dartType) {
1754
+ const classRegex = new RegExp(`class\\s+${className}\\s*[^{]*\\{`);
1755
+ const classMatch = content.match(classRegex);
1756
+ if (!classMatch) return content;
1757
+ const classStart = content.indexOf(classMatch[0]);
1758
+ const constructorPattern = new RegExp(
1759
+ `(?:const\\s+)?${className}\\s*\\(`
1760
+ );
1761
+ const searchFrom = classStart;
1762
+ const constructorMatch = content.slice(searchFrom).match(constructorPattern);
1763
+ if (!constructorMatch) return content;
1764
+ const constructorAbsStart = searchFrom + constructorMatch.index;
1765
+ const parenStart = content.indexOf("(", constructorAbsStart);
1766
+ const afterParen = content.slice(parenStart + 1).trimStart();
1767
+ if (afterParen.startsWith("{")) {
1768
+ const braceOffset = content.slice(parenStart + 1).indexOf("{");
1769
+ const braceStart = parenStart + 1 + braceOffset;
1770
+ let braceCount = 1;
1771
+ let braceEnd = -1;
1772
+ for (let i = braceStart + 1; i < content.length; i++) {
1773
+ if (content[i] === "{") braceCount++;
1774
+ else if (content[i] === "}") {
1775
+ braceCount--;
1776
+ if (braceCount === 0) {
1777
+ braceEnd = i;
1778
+ break;
1779
+ }
1780
+ }
1781
+ }
1782
+ if (braceEnd === -1) return content;
1783
+ const beforeClose = content.slice(0, braceEnd);
1784
+ const paramLines = beforeClose.split("\n");
1785
+ let lastParamIndent = " ";
1786
+ for (let i = paramLines.length - 1; i >= 0; i--) {
1787
+ const trimmed = paramLines[i].trim();
1788
+ if (trimmed.startsWith("this.") || trimmed.startsWith("required")) {
1789
+ const leadingSpaces = paramLines[i].match(/^(\s*)/)?.[1] ?? "";
1790
+ lastParamIndent = leadingSpaces;
1791
+ break;
1792
+ }
1793
+ }
1794
+ const defaultVal = appConfigDefaultValue(dartType);
1795
+ const insertion = `${lastParamIndent}this.${camelName} = ${defaultVal},
1796
+ `;
1797
+ return content.slice(0, braceEnd) + insertion + content.slice(braceEnd);
1798
+ } else {
1799
+ let parenCount = 1;
1800
+ let parenEnd = -1;
1801
+ for (let i = parenStart + 1; i < content.length; i++) {
1802
+ if (content[i] === "(") parenCount++;
1803
+ else if (content[i] === ")") {
1804
+ parenCount--;
1805
+ if (parenCount === 0) {
1806
+ parenEnd = i;
1807
+ break;
1808
+ }
1809
+ }
1810
+ }
1811
+ if (parenEnd === -1) return content;
1812
+ const beforeClose = content.slice(0, parenEnd);
1813
+ const paramLines = beforeClose.split("\n");
1814
+ let lastParamIndent = " ";
1815
+ for (let i = paramLines.length - 1; i >= 0; i--) {
1816
+ const trimmed = paramLines[i].trim();
1817
+ if (trimmed.length > 0) {
1818
+ const leadingSpaces = paramLines[i].match(/^(\s*)/)?.[1] ?? "";
1819
+ lastParamIndent = leadingSpaces;
1820
+ break;
1821
+ }
1822
+ }
1823
+ const defaultVal = appConfigDefaultValue(dartType);
1824
+ const insertion = `${lastParamIndent}this.${camelName} = ${defaultVal},
1825
+ `;
1826
+ return content.slice(0, parenEnd) + insertion + content.slice(parenEnd);
1827
+ }
1828
+ }
1829
+ function injectIntoPropsList(content, camelName) {
1830
+ const propsRegex = /List<Object\?>\s+get\s+props\s*=>\s*\[/;
1831
+ const match = content.match(propsRegex);
1832
+ if (!match) return content;
1833
+ const propsStart = content.indexOf(match[0]) + match[0].length;
1834
+ let bracketCount = 1;
1835
+ let bracketEnd = -1;
1836
+ for (let i = propsStart; i < content.length; i++) {
1837
+ if (content[i] === "[") bracketCount++;
1838
+ else if (content[i] === "]") {
1839
+ bracketCount--;
1840
+ if (bracketCount === 0) {
1841
+ bracketEnd = i;
1842
+ break;
1843
+ }
1844
+ }
1845
+ }
1846
+ if (bracketEnd === -1) return content;
1847
+ const beforeClose = content.slice(0, bracketEnd);
1848
+ const propLines = beforeClose.split("\n");
1849
+ let lastPropIndent = " ";
1850
+ for (let i = propLines.length - 1; i >= 0; i--) {
1851
+ const trimmed = propLines[i].trim();
1852
+ if (trimmed.length > 0 && trimmed !== "[") {
1853
+ const leadingSpaces = propLines[i].match(/^(\s*)/)?.[1] ?? "";
1854
+ lastPropIndent = leadingSpaces;
1855
+ break;
1856
+ }
1857
+ }
1858
+ const insertion = `${lastPropIndent}${camelName},
1859
+ `;
1860
+ return content.slice(0, bracketEnd) + insertion + content.slice(bracketEnd);
1861
+ }
1862
+ function injectIntoSuperCall(content, camelName) {
1863
+ const superRegex = /super\s*\(\s*/;
1864
+ const match = content.match(superRegex);
1865
+ if (!match) return content;
1866
+ const superStart = content.indexOf(match[0]) + match[0].length;
1867
+ let parenCount = 1;
1868
+ let parenEnd = -1;
1869
+ for (let i = superStart; i < content.length; i++) {
1870
+ if (content[i] === "(") parenCount++;
1871
+ else if (content[i] === ")") {
1872
+ parenCount--;
1873
+ if (parenCount === 0) {
1874
+ parenEnd = i;
1875
+ break;
1876
+ }
1877
+ }
1878
+ }
1879
+ if (parenEnd === -1) return content;
1880
+ const beforeClose = content.slice(0, parenEnd);
1881
+ const superLines = beforeClose.split("\n");
1882
+ let lastEntryIndent = " ";
1883
+ for (let i = superLines.length - 1; i >= 0; i--) {
1884
+ const trimmed = superLines[i].trim();
1885
+ if (trimmed.length > 0 && trimmed.includes(":")) {
1886
+ const leadingSpaces = superLines[i].match(/^(\s*)/)?.[1] ?? "";
1887
+ lastEntryIndent = leadingSpaces;
1888
+ break;
1889
+ }
1890
+ }
1891
+ const insertion = `${lastEntryIndent}${camelName}: ${camelName},
1892
+ `;
1893
+ return content.slice(0, parenEnd) + insertion + content.slice(parenEnd);
1894
+ }
1895
+ function findFileRecursive(dir, fileName) {
1896
+ const entries = fs10.readdirSync(dir, { withFileTypes: true });
1897
+ for (const entry of entries) {
1898
+ const fullPath = path9.join(dir, entry.name);
1899
+ if (entry.isDirectory()) {
1900
+ const result = findFileRecursive(fullPath, fileName);
1901
+ if (result) return result;
1902
+ } else if (entry.name === fileName) {
1903
+ return fullPath;
1904
+ }
1905
+ }
1906
+ return null;
1907
+ }
1908
+ function discoverVendorsModules(monorepoRoot) {
1909
+ const packagesDir = path9.join(monorepoRoot, "packages");
1910
+ if (!fs10.existsSync(packagesDir)) return [];
1911
+ const results = [];
1912
+ const packages = fs10.readdirSync(packagesDir, { withFileTypes: true }).filter((d) => d.isDirectory());
1913
+ for (const pkg2 of packages) {
1914
+ const pkgDir = path9.join(packagesDir, pkg2.name);
1915
+ const found = findFileRecursive(pkgDir, "vendors_module.dart");
1916
+ if (found) results.push(pkg2.name);
1917
+ }
1918
+ return results.sort();
1919
+ }
1920
+ function findAppConfigFile(monorepoRoot) {
1921
+ const packagesDir = path9.join(monorepoRoot, "packages");
1922
+ if (!fs10.existsSync(packagesDir)) return null;
1923
+ return findClassInPackages(packagesDir, "AppConfig");
1924
+ }
1925
+ function findAppConfigModelFile(monorepoRoot) {
1926
+ const packagesDir = path9.join(monorepoRoot, "packages");
1927
+ if (!fs10.existsSync(packagesDir)) return null;
1928
+ return findClassInPackages(packagesDir, "AppConfigModel");
1929
+ }
1930
+ function findClassInPackages(packagesDir, className) {
1931
+ const packages = fs10.readdirSync(packagesDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => path9.join(packagesDir, d.name));
1932
+ for (const pkgDir of packages) {
1933
+ const found = findClassFileRecursive(pkgDir, className);
1934
+ if (found) return found;
1935
+ }
1936
+ return null;
1937
+ }
1938
+ function findClassFileRecursive(dir, className) {
1939
+ const classRegex = new RegExp(`\\bclass\\s+${className}\\b`);
1940
+ const entries = fs10.readdirSync(dir, { withFileTypes: true });
1941
+ for (const entry of entries) {
1942
+ const fullPath = path9.join(dir, entry.name);
1943
+ if (entry.isDirectory()) {
1944
+ const result = findClassFileRecursive(fullPath, className);
1945
+ if (result) return result;
1946
+ } else if (entry.name.endsWith(".dart") && !entry.name.endsWith(".g.dart")) {
1947
+ const content = fs10.readFileSync(fullPath, "utf8");
1948
+ if (classRegex.test(content)) {
1949
+ return fullPath;
1950
+ }
1951
+ }
1952
+ }
1953
+ return null;
1954
+ }
1955
+ function findFileInPackages(packagesDir, fileName) {
1956
+ const packages = fs10.readdirSync(packagesDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => path9.join(packagesDir, d.name));
1957
+ for (const pkgDir of packages) {
1958
+ const found = findFileRecursive(pkgDir, fileName);
1959
+ if (found) return found;
1960
+ }
1961
+ return null;
1962
+ }
1963
+ function formatPkgPath(monorepoRoot, absPath) {
1964
+ return path9.relative(monorepoRoot, absPath);
1965
+ }
1966
+ var spinner = (msg) => console.log(chalk5.cyan(` \u2192 ${msg}`));
1967
+ async function createEnvVar(options) {
1968
+ const {
1969
+ monorepoRoot,
1970
+ variableName,
1971
+ dartType,
1972
+ defaultValue,
1973
+ selectedApps,
1974
+ includeInRemoteConfig,
1975
+ includeInAppConfig
1976
+ } = options;
1977
+ const camelName = screamingSnakeToCamel(variableName);
1978
+ console.log(chalk5.bold(`
1979
+ Adding environment variable: ${chalk5.cyan(variableName)}
1980
+ `));
1981
+ spinner("PASO 1: Updating env JSON files");
1982
+ const jsonValue = typedJsonValue(defaultValue, dartType);
1983
+ const appsDir = path9.join(monorepoRoot, "apps");
1984
+ for (const app of selectedApps) {
1985
+ const appEnvDir = path9.join(appsDir, app, "env");
1986
+ const files = [
1987
+ { name: "example.env.json", isTemplate: true },
1988
+ { name: "development.env.json", isTemplate: false },
1989
+ { name: "production.env.json", isTemplate: false }
1990
+ ];
1991
+ for (const { name, isTemplate } of files) {
1992
+ injectIntoJsonFile(path9.join(appEnvDir, name), variableName, jsonValue, isTemplate);
1993
+ }
1994
+ }
1995
+ spinner("PASO 2: Injecting into AppEnvironment");
1996
+ injectAbstractGetter(monorepoRoot, camelName, dartType);
1997
+ spinner("PASO 3: Injecting into AppBuildEnvironment");
1998
+ injectBuildEnvGetter(monorepoRoot, variableName, camelName, dartType);
1999
+ if (includeInRemoteConfig) {
2000
+ spinner("PASO 4: Injecting into VendorsModule RemoteConfig");
2001
+ for (const pkgName of options.vendorsTargets) {
2002
+ const pkgDir = path9.join(monorepoRoot, "packages", pkgName);
2003
+ const vendorsPath = findFileRecursive(pkgDir, "vendors_module.dart");
2004
+ if (vendorsPath) {
2005
+ console.log(chalk5.cyan(` \u2192 ${pkgName}/vendors_module.dart`));
2006
+ injectIntoRemoteConfigDefaults(vendorsPath, camelName, dartType);
2007
+ } else {
2008
+ console.log(chalk5.yellow(` vendors_module.dart not found in ${pkgName}`));
2009
+ }
2010
+ }
2011
+ }
2012
+ if (includeInAppConfig) {
2013
+ spinner("PASO 5: Injecting into AppConfig entity");
2014
+ const configPath = findAppConfigFile(monorepoRoot);
2015
+ if (configPath) {
2016
+ injectAppConfigProperty(configPath, camelName, dartType);
2017
+ } else {
2018
+ console.log(chalk5.yellow(" app_config.dart not found, skipping AppConfig"));
2019
+ }
2020
+ spinner("PASO 6: Injecting into AppConfigModel");
2021
+ const modelPath = findAppConfigModelFile(monorepoRoot);
2022
+ if (modelPath) {
2023
+ injectAppConfigModelProperty(modelPath, camelName, dartType);
2024
+ } else {
2025
+ console.log(chalk5.yellow(" app_config_model.dart not found, skipping AppConfigModel"));
2026
+ }
2027
+ }
2028
+ console.log(chalk5.green("\nEnvironment variable added successfully!"));
2029
+ spinner("Running melos format...");
2030
+ try {
2031
+ execSync("melos format", { cwd: monorepoRoot, stdio: "pipe" });
2032
+ console.log(chalk5.green(" melos format completed"));
2033
+ } catch {
2034
+ console.log(chalk5.yellow(" melos format failed (you may need to run it manually)"));
2035
+ }
2036
+ }
2037
+
2038
+ // src/core/docs-serve.ts
2039
+ import { spawn as spawn2 } from "child_process";
2040
+ import * as fs11 from "fs";
2041
+ import * as path10 from "path";
2042
+ import chalk6 from "chalk";
1473
2043
  function detectBookDir(startDir) {
1474
2044
  const root = findMonorepoRoot(startDir) ?? startDir;
1475
- const bookDir = path9.join(root, "book");
1476
- if (fs10.existsSync(bookDir) && (fs10.existsSync(path9.join(bookDir, "docusaurus.config.js")) || fs10.existsSync(path9.join(bookDir, "docusaurus.config.ts")) || fs10.existsSync(path9.join(bookDir, "docusaurus.config.mjs")))) {
2045
+ const bookDir = path10.join(root, "book");
2046
+ if (fs11.existsSync(bookDir) && (fs11.existsSync(path10.join(bookDir, "docusaurus.config.js")) || fs11.existsSync(path10.join(bookDir, "docusaurus.config.ts")) || fs11.existsSync(path10.join(bookDir, "docusaurus.config.mjs")))) {
1477
2047
  return bookDir;
1478
2048
  }
1479
2049
  return void 0;
1480
2050
  }
1481
2051
  function serveBook(bookDir) {
1482
2052
  return new Promise((resolve4, reject) => {
1483
- const nodeModules = path9.join(bookDir, "node_modules");
1484
- if (!fs10.existsSync(nodeModules)) {
1485
- console.log(chalk5.cyan("Installing book dependencies..."));
2053
+ const nodeModules = path10.join(bookDir, "node_modules");
2054
+ if (!fs11.existsSync(nodeModules)) {
2055
+ console.log(chalk6.cyan("Installing book dependencies..."));
1486
2056
  const install = spawn2("npm", ["install"], {
1487
2057
  cwd: bookDir,
1488
2058
  stdio: "inherit"
@@ -1516,16 +2086,16 @@ function startDevServer(bookDir, done) {
1516
2086
  }
1517
2087
 
1518
2088
  // src/core/docs-commands.ts
1519
- import * as fs11 from "fs";
1520
- import * as path10 from "path";
1521
- import chalk6 from "chalk";
2089
+ import * as fs12 from "fs";
2090
+ import * as path11 from "path";
2091
+ import chalk7 from "chalk";
1522
2092
  import YAML2 from "yaml";
1523
2093
  function parseMakefile(filePath) {
1524
- if (!fs11.existsSync(filePath)) return [];
1525
- const content = fs11.readFileSync(filePath, "utf8");
2094
+ if (!fs12.existsSync(filePath)) return [];
2095
+ const content = fs12.readFileSync(filePath, "utf8");
1526
2096
  const lines = content.split("\n");
1527
2097
  const commands = [];
1528
- const source = path10.basename(filePath);
2098
+ const source = path11.basename(filePath);
1529
2099
  let pendingComments = [];
1530
2100
  for (let i = 0; i < lines.length; i++) {
1531
2101
  const line = lines[i];
@@ -1555,13 +2125,13 @@ function parseMakefile(filePath) {
1555
2125
  return commands;
1556
2126
  }
1557
2127
  function parseMelosScripts(filePath) {
1558
- if (!fs11.existsSync(filePath)) return [];
1559
- const content = fs11.readFileSync(filePath, "utf8");
2128
+ if (!fs12.existsSync(filePath)) return [];
2129
+ const content = fs12.readFileSync(filePath, "utf8");
1560
2130
  const parsed = YAML2.parse(content);
1561
2131
  const scripts = parsed?.scripts;
1562
2132
  if (!scripts || typeof scripts !== "object") return [];
1563
2133
  const commands = [];
1564
- const source = path10.basename(filePath);
2134
+ const source = path11.basename(filePath);
1565
2135
  for (const [name, value] of Object.entries(scripts)) {
1566
2136
  let description = "";
1567
2137
  let command = "";
@@ -1583,17 +2153,17 @@ function parseMelosScripts(filePath) {
1583
2153
  function discoverCommands(startDir) {
1584
2154
  const root = findMonorepoRoot(startDir) ?? startDir;
1585
2155
  const commands = [];
1586
- const rootMakefile = path10.join(root, "Makefile");
2156
+ const rootMakefile = path11.join(root, "Makefile");
1587
2157
  commands.push(...parseMakefile(rootMakefile));
1588
- const melosFile = path10.join(root, "melos.yaml");
2158
+ const melosFile = path11.join(root, "melos.yaml");
1589
2159
  commands.push(...parseMelosScripts(melosFile));
1590
- const bookMakefile = path10.join(root, "book", "Makefile");
2160
+ const bookMakefile = path11.join(root, "book", "Makefile");
1591
2161
  commands.push(...parseMakefile(bookMakefile));
1592
2162
  return commands;
1593
2163
  }
1594
2164
  function displayCommands(commands) {
1595
2165
  if (commands.length === 0) {
1596
- console.log(chalk6.yellow("No commands found."));
2166
+ console.log(chalk7.yellow("No commands found."));
1597
2167
  return;
1598
2168
  }
1599
2169
  const groups = /* @__PURE__ */ new Map();
@@ -1603,13 +2173,13 @@ function displayCommands(commands) {
1603
2173
  groups.get(key).push(cmd);
1604
2174
  }
1605
2175
  for (const [group, entries] of groups) {
1606
- console.log(chalk6.bold(chalk6.cyan(`
2176
+ console.log(chalk7.bold(chalk7.cyan(`
1607
2177
  ${group}`)));
1608
- console.log(chalk6.cyan("\u2500".repeat(group.length)));
2178
+ console.log(chalk7.cyan("\u2500".repeat(group.length)));
1609
2179
  const maxNameLen = Math.max(...entries.map((e) => e.name.length));
1610
2180
  for (const entry of entries) {
1611
- const name = chalk6.white(entry.name.padEnd(maxNameLen + 2));
1612
- const desc = entry.description ? chalk6.gray(entry.description) : chalk6.dim("(no description)");
2181
+ const name = chalk7.white(entry.name.padEnd(maxNameLen + 2));
2182
+ const desc = entry.description ? chalk7.gray(entry.description) : chalk7.dim("(no description)");
1613
2183
  console.log(` ${name} ${desc}`);
1614
2184
  }
1615
2185
  }
@@ -1617,9 +2187,9 @@ ${group}`)));
1617
2187
  }
1618
2188
 
1619
2189
  // src/core/docs-architecture.ts
1620
- import * as fs12 from "fs";
1621
- import * as path11 from "path";
1622
- import chalk7 from "chalk";
2190
+ import * as fs13 from "fs";
2191
+ import * as path12 from "path";
2192
+ import chalk8 from "chalk";
1623
2193
  import YAML3 from "yaml";
1624
2194
  var KEY_DEPS = [
1625
2195
  "freezed",
@@ -1637,18 +2207,18 @@ var KEY_DEPS = [
1637
2207
  function discoverArchitecture(startDir) {
1638
2208
  const root = findMonorepoRoot(startDir);
1639
2209
  if (!root) return null;
1640
- const melosPath = path11.join(root, "melos.yaml");
1641
- if (!fs12.existsSync(melosPath)) return null;
1642
- const melosContent = fs12.readFileSync(melosPath, "utf8");
2210
+ const melosPath = path12.join(root, "melos.yaml");
2211
+ if (!fs13.existsSync(melosPath)) return null;
2212
+ const melosContent = fs13.readFileSync(melosPath, "utf8");
1643
2213
  const melos = YAML3.parse(melosContent);
1644
- const projectName = melos?.name ?? path11.basename(root);
2214
+ const projectName = melos?.name ?? path12.basename(root);
1645
2215
  const apps = [];
1646
- const appsDir = path11.join(root, "apps");
1647
- if (fs12.existsSync(appsDir)) {
1648
- for (const entry of fs12.readdirSync(appsDir, { withFileTypes: true })) {
2216
+ const appsDir = path12.join(root, "apps");
2217
+ if (fs13.existsSync(appsDir)) {
2218
+ for (const entry of fs13.readdirSync(appsDir, { withFileTypes: true })) {
1649
2219
  if (!entry.isDirectory()) continue;
1650
- const candidate = path11.join(appsDir, entry.name);
1651
- if (fs12.existsSync(path11.join(candidate, "pubspec.yaml"))) {
2220
+ const candidate = path12.join(appsDir, entry.name);
2221
+ if (fs13.existsSync(path12.join(candidate, "pubspec.yaml"))) {
1652
2222
  apps.push({ name: entry.name, dir: candidate });
1653
2223
  }
1654
2224
  }
@@ -1656,14 +2226,14 @@ function discoverArchitecture(startDir) {
1656
2226
  const packages = [];
1657
2227
  const packageBases = ["packages", "packages/features"];
1658
2228
  for (const base of packageBases) {
1659
- const baseDir = path11.join(root, base);
1660
- if (!fs12.existsSync(baseDir)) continue;
1661
- for (const entry of fs12.readdirSync(baseDir, { withFileTypes: true })) {
2229
+ const baseDir = path12.join(root, base);
2230
+ if (!fs13.existsSync(baseDir)) continue;
2231
+ for (const entry of fs13.readdirSync(baseDir, { withFileTypes: true })) {
1662
2232
  if (!entry.isDirectory()) continue;
1663
- const candidate = path11.join(baseDir, entry.name);
1664
- const pubspecPath = path11.join(candidate, "pubspec.yaml");
1665
- if (!fs12.existsSync(pubspecPath)) continue;
1666
- const content = fs12.readFileSync(pubspecPath, "utf8");
2233
+ const candidate = path12.join(baseDir, entry.name);
2234
+ const pubspecPath = path12.join(candidate, "pubspec.yaml");
2235
+ if (!fs13.existsSync(pubspecPath)) continue;
2236
+ const content = fs13.readFileSync(pubspecPath, "utf8");
1667
2237
  const pubspec = YAML3.parse(content);
1668
2238
  const deps = {
1669
2239
  ...pubspec?.dependencies,
@@ -1698,30 +2268,30 @@ function displayArchitecture(info) {
1698
2268
  const L = "\u2514\u2500\u2500 ";
1699
2269
  const I = "\u2502 ";
1700
2270
  const S = " ";
1701
- console.log(chalk7.bold(chalk7.cyan(info.projectName)) + chalk7.gray(` (${info.root})`));
2271
+ console.log(chalk8.bold(chalk8.cyan(info.projectName)) + chalk8.gray(` (${info.root})`));
1702
2272
  console.log(`${I}`);
1703
2273
  if (info.apps.length > 0) {
1704
- console.log(chalk7.white(`${T}apps/`));
2274
+ console.log(chalk8.white(`${T}apps/`));
1705
2275
  for (let i = 0; i < info.apps.length; i++) {
1706
2276
  const app = info.apps[i];
1707
2277
  const prefix = i === info.apps.length - 1 ? `${I}${S}${L}` : `${I}${S}${T}`;
1708
- console.log(`${prefix}${chalk7.green(app.name)}`);
2278
+ console.log(`${prefix}${chalk8.green(app.name)}`);
1709
2279
  }
1710
2280
  }
1711
2281
  if (info.packages.length > 0) {
1712
2282
  const rootPkgs = info.packages.filter(
1713
- (p) => !p.dir.includes(`${path11.sep}features${path11.sep}`)
2283
+ (p) => !p.dir.includes(`${path12.sep}features${path12.sep}`)
1714
2284
  );
1715
2285
  const featurePkgs = info.packages.filter(
1716
- (p) => p.dir.includes(`${path11.sep}features${path11.sep}`)
2286
+ (p) => p.dir.includes(`${path12.sep}features${path12.sep}`)
1717
2287
  );
1718
- console.log(chalk7.white(`${T}packages/`));
2288
+ console.log(chalk8.white(`${T}packages/`));
1719
2289
  for (let i = 0; i < rootPkgs.length; i++) {
1720
2290
  const pkg2 = rootPkgs[i];
1721
2291
  const isLast = i === rootPkgs.length - 1 && featurePkgs.length === 0;
1722
2292
  const prefix = isLast ? `${I}${S}${L}` : `${I}${S}${T}`;
1723
- const deps = pkg2.keyDeps.length > 0 ? chalk7.dim(` [${pkg2.keyDeps.join(", ")}]`) : "";
1724
- console.log(`${prefix}${chalk7.yellow(pkg2.name)}${deps}`);
2293
+ const deps = pkg2.keyDeps.length > 0 ? chalk8.dim(` [${pkg2.keyDeps.join(", ")}]`) : "";
2294
+ console.log(`${prefix}${chalk8.yellow(pkg2.name)}${deps}`);
1725
2295
  }
1726
2296
  if (featurePkgs.length > 0) {
1727
2297
  console.log(`${I}${S}${T}features/`);
@@ -1729,21 +2299,21 @@ function displayArchitecture(info) {
1729
2299
  const pkg2 = featurePkgs[i];
1730
2300
  const isLast = i === featurePkgs.length - 1;
1731
2301
  const prefix = isLast ? `${I}${S}${S}${L}` : `${I}${S}${S}${T}`;
1732
- const deps = pkg2.keyDeps.length > 0 ? chalk7.dim(` [${pkg2.keyDeps.join(", ")}]`) : "";
1733
- console.log(`${prefix}${chalk7.yellow(pkg2.name)}${deps}`);
2302
+ const deps = pkg2.keyDeps.length > 0 ? chalk8.dim(` [${pkg2.keyDeps.join(", ")}]`) : "";
2303
+ console.log(`${prefix}${chalk8.yellow(pkg2.name)}${deps}`);
1734
2304
  }
1735
2305
  }
1736
2306
  }
1737
2307
  if (info.hasDesignSystem) {
1738
- const tiers = info.designSystemTiers.length > 0 ? chalk7.dim(` (${info.designSystemTiers.join(", ")})`) : "";
1739
- console.log(`${T}${chalk7.magenta("design_system")}${tiers}`);
2308
+ const tiers = info.designSystemTiers.length > 0 ? chalk8.dim(` (${info.designSystemTiers.join(", ")})`) : "";
2309
+ console.log(`${T}${chalk8.magenta("design_system")}${tiers}`);
1740
2310
  }
1741
- const bookDir = path11.join(info.root, "book");
1742
- if (fs12.existsSync(bookDir)) {
1743
- console.log(`${T}${chalk7.blue("book/")}` + chalk7.dim(" (Docusaurus)"));
2311
+ const bookDir = path12.join(info.root, "book");
2312
+ if (fs13.existsSync(bookDir)) {
2313
+ console.log(`${T}${chalk8.blue("book/")}` + chalk8.dim(" (Docusaurus)"));
1744
2314
  }
1745
- if (fs12.existsSync(path11.join(info.root, "melos.yaml"))) {
1746
- console.log(`${L}${chalk7.gray("melos.yaml")}`);
2315
+ if (fs13.existsSync(path12.join(info.root, "melos.yaml"))) {
2316
+ console.log(`${L}${chalk8.gray("melos.yaml")}`);
1747
2317
  }
1748
2318
  console.log();
1749
2319
  }
@@ -1755,7 +2325,7 @@ async function resolveProject() {
1755
2325
  s.start("Analyzing current directory...");
1756
2326
  const cwdProject = analyzeProject(process.cwd());
1757
2327
  if (cwdProject && (cwdProject.hasFreezed || cwdProject.hasBloc)) {
1758
- s.stop(`Found ${chalk8.green(cwdProject.projectName)}`);
2328
+ s.stop(`Found ${chalk9.green(cwdProject.projectName)}`);
1759
2329
  return cwdProject;
1760
2330
  }
1761
2331
  s.message("Looking for Melos monorepo...");
@@ -1768,8 +2338,8 @@ async function resolveProject() {
1768
2338
  }
1769
2339
  }
1770
2340
  s.message("Scanning for Flutter projects...");
1771
- const homeDev = path12.join(os.homedir(), "Development");
1772
- if (fs13.existsSync(homeDev)) {
2341
+ const homeDev = path13.join(os.homedir(), "Development");
2342
+ if (fs14.existsSync(homeDev)) {
1773
2343
  const projects = discoverProjects(homeDev, 2);
1774
2344
  if (projects.length > 0) {
1775
2345
  s.stop(`Found ${projects.length} Flutter project(s)`);
@@ -1777,12 +2347,12 @@ async function resolveProject() {
1777
2347
  }
1778
2348
  }
1779
2349
  s.stop("No Flutter projects found");
1780
- clack.outro(chalk8.red("Could not find any Flutter project with freezed or flutter_bloc."));
2350
+ clack.outro(chalk9.red("Could not find any Flutter project with freezed or flutter_bloc."));
1781
2351
  return null;
1782
2352
  }
1783
2353
  async function selectPackage(projects) {
1784
2354
  if (projects.length === 1) {
1785
- clack.log.info(`Using ${chalk8.green(projects[0].projectName)}`);
2355
+ clack.log.info(`Using ${chalk9.green(projects[0].projectName)}`);
1786
2356
  return projects[0];
1787
2357
  }
1788
2358
  const selected = await clack.select({
@@ -1790,7 +2360,7 @@ async function selectPackage(projects) {
1790
2360
  options: projects.map((p) => ({
1791
2361
  value: p,
1792
2362
  label: p.projectName,
1793
- hint: path12.relative(os.homedir(), p.projectRoot)
2363
+ hint: path13.relative(os.homedir(), p.projectRoot)
1794
2364
  }))
1795
2365
  });
1796
2366
  if (clack.isCancel(selected)) {
@@ -1838,13 +2408,13 @@ async function blocFlow(project) {
1838
2408
  clack.cancel("Cancelled");
1839
2409
  return;
1840
2410
  }
1841
- targetDir = path12.resolve(customPath);
2411
+ targetDir = path13.resolve(customPath);
1842
2412
  } else {
1843
- targetDir = path12.join(project.projectRoot, "lib", "features", feature);
2413
+ targetDir = path13.join(project.projectRoot, "lib", "features", feature);
1844
2414
  }
1845
- } else if (fs13.existsSync(path12.join(project.projectRoot, "lib", "bloc"))) {
1846
- targetDir = path12.join(project.projectRoot, "lib", "bloc");
1847
- clack.log.info(`Target: ${chalk8.cyan("lib/bloc/")}`);
2415
+ } else if (fs14.existsSync(path13.join(project.projectRoot, "lib", "bloc"))) {
2416
+ targetDir = path13.join(project.projectRoot, "lib", "bloc");
2417
+ clack.log.info(`Target: ${chalk9.cyan("lib/bloc/")}`);
1848
2418
  } else {
1849
2419
  clack.note(
1850
2420
  "No lib/features/ directory found. Provide a target path manually.",
@@ -1861,7 +2431,7 @@ async function blocFlow(project) {
1861
2431
  clack.cancel("Cancelled");
1862
2432
  return;
1863
2433
  }
1864
- targetDir = path12.resolve(customPath);
2434
+ targetDir = path13.resolve(customPath);
1865
2435
  }
1866
2436
  const defaultRun = project.hasBuildRunner;
1867
2437
  const runBuildRunner2 = await clack.confirm({
@@ -1880,10 +2450,10 @@ async function blocFlow(project) {
1880
2450
  buildRunner: runBuildRunner2
1881
2451
  });
1882
2452
  genSpinner.stop("BLoC generated");
1883
- clack.outro(chalk8.green("Done!"));
2453
+ clack.outro(chalk9.green("Done!"));
1884
2454
  } catch (error) {
1885
2455
  genSpinner.stop("Failed");
1886
- clack.outro(chalk8.red(`Error: ${error}`));
2456
+ clack.outro(chalk9.red(`Error: ${error}`));
1887
2457
  }
1888
2458
  }
1889
2459
  async function widgetFlow() {
@@ -1891,14 +2461,14 @@ async function widgetFlow() {
1891
2461
  const ds = detectDesignSystem(projectRoot);
1892
2462
  if (!ds) {
1893
2463
  clack.outro(
1894
- chalk8.red(
2464
+ chalk9.red(
1895
2465
  "No design system detected. Run this command from a project with wl_design_system/ directory."
1896
2466
  )
1897
2467
  );
1898
2468
  return;
1899
2469
  }
1900
2470
  clack.log.info(
1901
- `Design system: ${chalk8.cyan(path12.relative(projectRoot, ds.componentsDir))}`
2471
+ `Design system: ${chalk9.cyan(path13.relative(projectRoot, ds.componentsDir))}`
1902
2472
  );
1903
2473
  const name = await clack.text({
1904
2474
  message: "Widget name (snake_case, without wl_ prefix)",
@@ -1963,17 +2533,17 @@ async function widgetFlow() {
1963
2533
  genSpinner.stop(`Use-case skipped: ${e}`);
1964
2534
  }
1965
2535
  }
1966
- clack.outro(chalk8.green("Done!"));
2536
+ clack.outro(chalk9.green("Done!"));
1967
2537
  } catch (error) {
1968
2538
  genSpinner.stop("Failed");
1969
- clack.outro(chalk8.red(`Error: ${error}`));
2539
+ clack.outro(chalk9.red(`Error: ${error}`));
1970
2540
  }
1971
2541
  }
1972
2542
  async function useCaseFlow() {
1973
2543
  const ds = detectDesignSystem(process.cwd());
1974
2544
  if (!ds) {
1975
2545
  clack.outro(
1976
- chalk8.red(
2546
+ chalk9.red(
1977
2547
  "No design system detected. Ensure wl_design_system/ directory exists."
1978
2548
  )
1979
2549
  );
@@ -1981,7 +2551,7 @@ async function useCaseFlow() {
1981
2551
  }
1982
2552
  if (!ds.widgetbookDir) {
1983
2553
  clack.outro(
1984
- chalk8.red(
2554
+ chalk9.red(
1985
2555
  "No widgetbook package detected. Ensure apps/widgetbook/ exists."
1986
2556
  )
1987
2557
  );
@@ -2030,18 +2600,18 @@ async function useCaseFlow() {
2030
2600
  buildRunner: runBuildRunner2
2031
2601
  });
2032
2602
  genSpinner.stop("Use-case generated");
2033
- clack.outro(chalk8.green("Done!"));
2603
+ clack.outro(chalk9.green("Done!"));
2034
2604
  } catch (error) {
2035
2605
  genSpinner.stop("Failed");
2036
- clack.outro(chalk8.red(`Error: ${error}`));
2606
+ clack.outro(chalk9.red(`Error: ${error}`));
2037
2607
  }
2038
2608
  }
2039
2609
  function findBffFiles(dir) {
2040
2610
  const results = [];
2041
- if (!fs13.existsSync(dir)) return results;
2042
- const entries = fs13.readdirSync(dir, { withFileTypes: true });
2611
+ if (!fs14.existsSync(dir)) return results;
2612
+ const entries = fs14.readdirSync(dir, { withFileTypes: true });
2043
2613
  for (const entry of entries) {
2044
- const fullPath = path12.join(dir, entry.name);
2614
+ const fullPath = path13.join(dir, entry.name);
2045
2615
  if (entry.isDirectory()) {
2046
2616
  results.push(...findBffFiles(fullPath));
2047
2617
  } else if (entry.isFile() && entry.name.endsWith(".dart") && !entry.name.includes(".g.")) {
@@ -2053,8 +2623,8 @@ function findBffFiles(dir) {
2053
2623
  function inferBffFile(bffFiles, endpointPath) {
2054
2624
  const firstSegment = endpointPath.replace(/^\//, "").split("/")[0].toLowerCase();
2055
2625
  if (!firstSegment) return null;
2056
- const withDomains = bffFiles.filter((f) => /^bff_.+_api\.dart$/.test(path12.basename(f))).map((f) => {
2057
- const base = path12.basename(f, ".dart");
2626
+ const withDomains = bffFiles.filter((f) => /^bff_.+_api\.dart$/.test(path13.basename(f))).map((f) => {
2627
+ const base = path13.basename(f, ".dart");
2058
2628
  const domain = base.replace(/^bff_/, "").replace(/_api$/, "");
2059
2629
  return { file: f, domain };
2060
2630
  });
@@ -2081,19 +2651,19 @@ async function resolveEndpointProject() {
2081
2651
  s.message(`Monorepo found at ${monorepoRoot}`);
2082
2652
  const packageBases = ["packages", "packages/features"];
2083
2653
  for (const base of packageBases) {
2084
- const baseDir = path12.join(monorepoRoot, base);
2085
- if (!fs13.existsSync(baseDir)) continue;
2086
- const entries = fs13.readdirSync(baseDir, { withFileTypes: true });
2654
+ const baseDir = path13.join(monorepoRoot, base);
2655
+ if (!fs14.existsSync(baseDir)) continue;
2656
+ const entries = fs14.readdirSync(baseDir, { withFileTypes: true });
2087
2657
  for (const entry of entries) {
2088
2658
  if (!entry.isDirectory()) continue;
2089
- const candidate = path12.join(baseDir, entry.name);
2090
- if (!fs13.existsSync(path12.join(candidate, "pubspec.yaml"))) continue;
2091
- const bffPath = path12.join(candidate, "lib", "data", "api", "bff");
2092
- s.message(`Checking ${candidate} \u2192 bff exists: ${fs13.existsSync(bffPath)}`);
2093
- if (fs13.existsSync(bffPath)) {
2659
+ const candidate = path13.join(baseDir, entry.name);
2660
+ if (!fs14.existsSync(path13.join(candidate, "pubspec.yaml"))) continue;
2661
+ const bffPath = path13.join(candidate, "lib", "data", "api", "bff");
2662
+ s.message(`Checking ${candidate} \u2192 bff exists: ${fs14.existsSync(bffPath)}`);
2663
+ if (fs14.existsSync(bffPath)) {
2094
2664
  const project2 = analyzeProject(candidate);
2095
2665
  if (project2) {
2096
- s.stop(`Using ${chalk8.green(project2.projectName)}`);
2666
+ s.stop(`Using ${chalk9.green(project2.projectName)}`);
2097
2667
  return project2;
2098
2668
  }
2099
2669
  }
@@ -2103,8 +2673,8 @@ async function resolveEndpointProject() {
2103
2673
  s.message("No monorepo root found");
2104
2674
  }
2105
2675
  const cwdProject = analyzeProject(process.cwd());
2106
- if (cwdProject && fs13.existsSync(path12.join(cwdProject.projectRoot, "lib", "data", "api", "bff"))) {
2107
- s.stop(`Using ${chalk8.green(cwdProject.projectName)}`);
2676
+ if (cwdProject && fs14.existsSync(path13.join(cwdProject.projectRoot, "lib", "data", "api", "bff"))) {
2677
+ s.stop(`Using ${chalk9.green(cwdProject.projectName)}`);
2108
2678
  return cwdProject;
2109
2679
  }
2110
2680
  s.stop("No BFF package found");
@@ -2113,33 +2683,33 @@ async function resolveEndpointProject() {
2113
2683
  placeholder: "e.g. /path/to/my-package or ./packages/core",
2114
2684
  validate: (v) => {
2115
2685
  if (!v.trim()) return "Path is required";
2116
- const resolved2 = path12.resolve(v.trim());
2117
- if (!fs13.existsSync(resolved2)) return "Path does not exist";
2118
- if (!fs13.existsSync(path12.join(resolved2, "pubspec.yaml"))) return "No pubspec.yaml found at this path";
2119
- if (!fs13.existsSync(path12.join(resolved2, "lib", "data", "api", "bff"))) return "No lib/data/api/bff/ found at this path";
2686
+ const resolved2 = path13.resolve(v.trim());
2687
+ if (!fs14.existsSync(resolved2)) return "Path does not exist";
2688
+ if (!fs14.existsSync(path13.join(resolved2, "pubspec.yaml"))) return "No pubspec.yaml found at this path";
2689
+ if (!fs14.existsSync(path13.join(resolved2, "lib", "data", "api", "bff"))) return "No lib/data/api/bff/ found at this path";
2120
2690
  }
2121
2691
  });
2122
2692
  if (clack.isCancel(manualPath)) {
2123
2693
  clack.cancel("Cancelled");
2124
2694
  return null;
2125
2695
  }
2126
- const resolved = path12.resolve(manualPath);
2696
+ const resolved = path13.resolve(manualPath);
2127
2697
  const project = analyzeProject(resolved);
2128
2698
  if (!project) {
2129
- clack.outro(chalk8.red(`Could not analyze project at ${resolved}`));
2699
+ clack.outro(chalk9.red(`Could not analyze project at ${resolved}`));
2130
2700
  return null;
2131
2701
  }
2132
- clack.log.info(`Using ${chalk8.green(project.projectName)}`);
2702
+ clack.log.info(`Using ${chalk9.green(project.projectName)}`);
2133
2703
  return project;
2134
2704
  }
2135
2705
  async function endpointFlow() {
2136
2706
  const project = await resolveEndpointProject();
2137
2707
  if (!project) return;
2138
- const lib = path12.join(project.projectRoot, "lib");
2139
- const bffDir = path12.join(lib, "data", "api", "bff");
2708
+ const lib = path13.join(project.projectRoot, "lib");
2709
+ const bffDir = path13.join(lib, "data", "api", "bff");
2140
2710
  const bffFiles = findBffFiles(bffDir);
2141
2711
  if (bffFiles.length === 0) {
2142
- clack.outro(chalk8.red("No BFF API files found. Ensure lib/data/api/bff/ exists with .dart files."));
2712
+ clack.outro(chalk9.red("No BFF API files found. Ensure lib/data/api/bff/ exists with .dart files."));
2143
2713
  return;
2144
2714
  }
2145
2715
  const httpMethod = await clack.select({
@@ -2169,13 +2739,13 @@ async function endpointFlow() {
2169
2739
  }
2170
2740
  let bffApiFile = inferBffFile(bffFiles, endpointPath);
2171
2741
  if (bffApiFile) {
2172
- clack.log.info(`Auto-detected BFF file: ${chalk8.cyan(path12.basename(bffApiFile))}`);
2742
+ clack.log.info(`Auto-detected BFF file: ${chalk9.cyan(path13.basename(bffApiFile))}`);
2173
2743
  } else {
2174
2744
  const selected = await clack.select({
2175
2745
  message: "Could not auto-detect BFF file. Select one:",
2176
2746
  options: bffFiles.map((f) => ({
2177
2747
  value: f,
2178
- label: path12.relative(bffDir, f)
2748
+ label: path13.relative(bffDir, f)
2179
2749
  }))
2180
2750
  });
2181
2751
  if (clack.isCancel(selected)) {
@@ -2239,14 +2809,14 @@ async function endpointFlow() {
2239
2809
  diLazySingleton
2240
2810
  });
2241
2811
  genSpinner.stop("Endpoint generated");
2242
- clack.outro(chalk8.green("Done!"));
2812
+ clack.outro(chalk9.green("Done!"));
2243
2813
  } catch (error) {
2244
2814
  genSpinner.stop("Failed");
2245
- clack.outro(chalk8.red(`Error: ${error}`));
2815
+ clack.outro(chalk9.red(`Error: ${error}`));
2246
2816
  }
2247
2817
  }
2248
2818
  async function docsInteractiveMode() {
2249
- clack.intro(chalk8.bgCyan(chalk8.black(" wlmaker docs ")));
2819
+ clack.intro(chalk9.bgCyan(chalk9.black(" wlmaker docs ")));
2250
2820
  const action = await clack.select({
2251
2821
  message: "What do you want to do?",
2252
2822
  options: [
@@ -2289,49 +2859,172 @@ async function docsInteractiveMode() {
2289
2859
  return;
2290
2860
  }
2291
2861
  const remoteUrl = "https://dc-wl-docs.web.app/development_workflow/";
2292
- clack.log.info(`Opening remote docs: ${chalk8.cyan(remoteUrl)}`);
2293
- execSync(`open "${remoteUrl}"`, { stdio: "ignore" });
2294
- clack.outro(chalk8.green("Opened remote docs in browser"));
2862
+ if (!remoteUrl) {
2863
+ clack.outro(chalk9.red("Remote docs URL not configured. Set WL_DOCS_URL in your environment."));
2864
+ return;
2865
+ }
2866
+ clack.log.info(`Opening remote docs: ${chalk9.cyan(remoteUrl)}`);
2867
+ execSync2(`open "${remoteUrl}"`, { stdio: "ignore" });
2868
+ clack.outro(chalk9.green("Opened remote docs in browser"));
2295
2869
  return;
2296
2870
  }
2297
2871
  const bookDir = detectBookDir(process.cwd());
2298
2872
  if (!bookDir) {
2299
2873
  clack.outro(
2300
- chalk8.red("No Docusaurus book/ directory found. Run from a monorepo root.")
2874
+ chalk9.red("No Docusaurus book/ directory found. Run from a monorepo root.")
2301
2875
  );
2302
2876
  return;
2303
2877
  }
2304
- clack.log.info(`Serving docs from ${chalk8.cyan(bookDir)}`);
2878
+ clack.log.info(`Serving docs from ${chalk9.cyan(bookDir)}`);
2305
2879
  await serveBook(bookDir);
2306
2880
  break;
2307
2881
  }
2308
2882
  case "commands": {
2309
2883
  const commands = discoverCommands(process.cwd());
2310
2884
  if (commands.length === 0) {
2311
- clack.outro(chalk8.yellow("No commands found. Run from a monorepo root."));
2885
+ clack.outro(chalk9.yellow("No commands found. Run from a monorepo root."));
2312
2886
  return;
2313
2887
  }
2314
- clack.log.info(`Found ${chalk8.green(commands.length.toString())} command(s)`);
2888
+ clack.log.info(`Found ${chalk9.green(commands.length.toString())} command(s)`);
2315
2889
  displayCommands(commands);
2316
- clack.outro(chalk8.green("Done!"));
2890
+ clack.outro(chalk9.green("Done!"));
2317
2891
  break;
2318
2892
  }
2319
2893
  case "architecture": {
2320
2894
  const info = discoverArchitecture(process.cwd());
2321
2895
  if (!info) {
2322
2896
  clack.outro(
2323
- chalk8.yellow("No monorepo detected. Run from within a Melos monorepo.")
2897
+ chalk9.yellow("No monorepo detected. Run from within a Melos monorepo.")
2324
2898
  );
2325
2899
  return;
2326
2900
  }
2327
2901
  displayArchitecture(info);
2328
- clack.outro(chalk8.green("Done!"));
2902
+ clack.outro(chalk9.green("Done!"));
2329
2903
  break;
2330
2904
  }
2331
2905
  }
2332
2906
  }
2907
+ var SCREAMING_SNAKE_REGEX = /^[A-Z][A-Z0-9_]*$/;
2908
+ async function envVarFlow() {
2909
+ const monorepoRoot = findMonorepoRoot(process.cwd());
2910
+ if (!monorepoRoot) {
2911
+ clack.outro(chalk9.red("Not inside a monorepo. Run from within a Melos monorepo."));
2912
+ return;
2913
+ }
2914
+ const apps = discoverAppsWithEnv(monorepoRoot);
2915
+ if (apps.length === 0) {
2916
+ clack.outro(chalk9.red("No apps with env/ directory found in the monorepo."));
2917
+ return;
2918
+ }
2919
+ const variableName = await clack.text({
2920
+ message: "Variable name (SCREAMING_SNAKE_CASE)",
2921
+ placeholder: "MY_FEATURE_FLAG",
2922
+ validate: (v) => {
2923
+ if (!v.trim()) return "Name is required";
2924
+ if (!SCREAMING_SNAKE_REGEX.test(v)) return "Must be SCREAMING_SNAKE_CASE (uppercase, numbers, underscores)";
2925
+ }
2926
+ });
2927
+ if (clack.isCancel(variableName)) {
2928
+ clack.cancel("Cancelled");
2929
+ return;
2930
+ }
2931
+ const dartType = await clack.select({
2932
+ message: "Variable type",
2933
+ options: [
2934
+ { value: "String", label: "String" },
2935
+ { value: "int", label: "int" },
2936
+ { value: "bool", label: "bool" },
2937
+ { value: "List<String>", label: "List<String>" }
2938
+ ]
2939
+ });
2940
+ if (clack.isCancel(dartType)) {
2941
+ clack.cancel("Cancelled");
2942
+ return;
2943
+ }
2944
+ const selectedType = dartType;
2945
+ const defaultValue = await clack.text({
2946
+ message: "Default value (leave empty to skip)",
2947
+ placeholder: selectedType === "bool" ? "false" : selectedType === "int" ? "0" : "",
2948
+ validate: (v) => {
2949
+ if (!v || !v.trim()) return void 0;
2950
+ if (selectedType === "int" && !/^-?\d+$/.test(v)) return "Must be an integer";
2951
+ if (selectedType === "bool" && !["true", "false"].includes(v)) return "Must be true or false";
2952
+ }
2953
+ });
2954
+ if (clack.isCancel(defaultValue)) {
2955
+ clack.cancel("Cancelled");
2956
+ return;
2957
+ }
2958
+ const selectedApps = await clack.multiselect({
2959
+ message: "Select apps to add the variable to",
2960
+ options: [
2961
+ { value: "__all__", label: "All apps" },
2962
+ ...apps.map((app) => ({ value: app, label: app }))
2963
+ ],
2964
+ required: true
2965
+ });
2966
+ if (clack.isCancel(selectedApps)) {
2967
+ clack.cancel("Cancelled");
2968
+ return;
2969
+ }
2970
+ const appsList = selectedApps.includes("__all__") ? apps : selectedApps;
2971
+ const includeInRemoteConfig = await clack.confirm({
2972
+ message: "Add to VendorsModule RemoteConfig defaultValues?",
2973
+ initialValue: true
2974
+ });
2975
+ if (clack.isCancel(includeInRemoteConfig)) {
2976
+ clack.cancel("Cancelled");
2977
+ return;
2978
+ }
2979
+ let vendorsTargets = [];
2980
+ if (includeInRemoteConfig) {
2981
+ const vendorsModules = discoverVendorsModules(monorepoRoot);
2982
+ if (vendorsModules.length > 0) {
2983
+ const vendorsSelection = await clack.multiselect({
2984
+ message: "Which VendorsModule(s) to update?",
2985
+ options: [
2986
+ { value: "__all__", label: "All vendors" },
2987
+ ...vendorsModules.map((v) => ({ value: v, label: v }))
2988
+ ],
2989
+ required: true
2990
+ });
2991
+ if (clack.isCancel(vendorsSelection)) {
2992
+ clack.cancel("Cancelled");
2993
+ return;
2994
+ }
2995
+ vendorsTargets = vendorsSelection.includes("__all__") ? vendorsModules : vendorsSelection;
2996
+ }
2997
+ }
2998
+ const includeInAppConfig = await clack.confirm({
2999
+ message: "Add to AppConfig entity and AppConfigModel?",
3000
+ initialValue: true
3001
+ });
3002
+ if (clack.isCancel(includeInAppConfig)) {
3003
+ clack.cancel("Cancelled");
3004
+ return;
3005
+ }
3006
+ const genSpinner = clack.spinner();
3007
+ genSpinner.start("Adding environment variable...");
3008
+ try {
3009
+ await createEnvVar({
3010
+ monorepoRoot,
3011
+ variableName,
3012
+ dartType: selectedType,
3013
+ defaultValue: defaultValue || void 0,
3014
+ selectedApps: appsList,
3015
+ vendorsTargets,
3016
+ includeInRemoteConfig,
3017
+ includeInAppConfig
3018
+ });
3019
+ genSpinner.stop("Done");
3020
+ clack.outro(chalk9.green("Environment variable added!"));
3021
+ } catch (error) {
3022
+ genSpinner.stop("Failed");
3023
+ clack.outro(chalk9.red(`Error: ${error}`));
3024
+ }
3025
+ }
2333
3026
  async function interactiveMode() {
2334
- clack.intro(chalk8.bgCyan(chalk8.black(" wlmaker ")));
3027
+ clack.intro(chalk9.bgCyan(chalk9.black(" wlmaker ")));
2335
3028
  const createType = await clack.select({
2336
3029
  message: "What do you want to create?",
2337
3030
  options: [
@@ -2347,6 +3040,11 @@ async function interactiveMode() {
2347
3040
  label: "Endpoint",
2348
3041
  hint: "BFF Clean Architecture stack"
2349
3042
  },
3043
+ {
3044
+ value: "env-var",
3045
+ label: "Env Var",
3046
+ hint: "Add environment variable to monorepo stack"
3047
+ },
2350
3048
  {
2351
3049
  value: "docs",
2352
3050
  label: "Docs",
@@ -2375,6 +3073,9 @@ async function interactiveMode() {
2375
3073
  case "endpoint":
2376
3074
  await endpointFlow();
2377
3075
  break;
3076
+ case "env-var":
3077
+ await envVarFlow();
3078
+ break;
2378
3079
  case "docs":
2379
3080
  await docsInteractiveMode();
2380
3081
  break;
@@ -2397,7 +3098,7 @@ program.command("bloc").description("Create a new BLoC with Freezed sealed class
2397
3098
  try {
2398
3099
  await createBloc(name, options);
2399
3100
  } catch (error) {
2400
- console.error(chalk9.red(`Error: ${error}`));
3101
+ console.error(chalk10.red(`Error: ${error}`));
2401
3102
  process.exit(1);
2402
3103
  }
2403
3104
  }
@@ -2414,12 +3115,12 @@ program.command("widget").description("Create a new widget in the design system"
2414
3115
  projectRoot: options.dir,
2415
3116
  buildRunner: false
2416
3117
  });
2417
- console.log(chalk9.green("Widgetbook use-case created"));
3118
+ console.log(chalk10.green("Widgetbook use-case created"));
2418
3119
  } catch {
2419
- console.log(chalk9.yellow("Use-case skipped (may already exist)"));
3120
+ console.log(chalk10.yellow("Use-case skipped (may already exist)"));
2420
3121
  }
2421
3122
  } catch (error) {
2422
- console.error(chalk9.red(`Error: ${error}`));
3123
+ console.error(chalk10.red(`Error: ${error}`));
2423
3124
  process.exit(1);
2424
3125
  }
2425
3126
  }
@@ -2432,7 +3133,7 @@ program.command("usecase").description("Create a Widgetbook use-case for an exis
2432
3133
  buildRunner: options.buildRunner
2433
3134
  });
2434
3135
  } catch (error) {
2435
- console.error(chalk9.red(`Error: ${error}`));
3136
+ console.error(chalk10.red(`Error: ${error}`));
2436
3137
  process.exit(1);
2437
3138
  }
2438
3139
  }
@@ -2442,30 +3143,33 @@ program.command("endpoint").description("Generate Clean Architecture stack for a
2442
3143
  if (!project) return;
2443
3144
  await endpointFlow(project);
2444
3145
  });
3146
+ program.command("env-var").description("Add an environment variable across the Flutter monorepo").action(async () => {
3147
+ await envVarFlow();
3148
+ });
2445
3149
  var docsCmd = program.command("docs").description("Project documentation tools");
2446
3150
  docsCmd.command("serve").description("Start Docusaurus dev server").option("-d, --dir <path>", "project root directory", process.cwd()).action(async (options) => {
2447
3151
  const bookDir = detectBookDir(options.dir);
2448
3152
  if (!bookDir) {
2449
- console.error(chalk9.red("No Docusaurus book/ directory found. Run from a monorepo root."));
3153
+ console.error(chalk10.red("No Docusaurus book/ directory found. Run from a monorepo root."));
2450
3154
  process.exit(1);
2451
3155
  }
2452
- console.log(chalk9.cyan(`Serving docs from ${bookDir}`));
3156
+ console.log(chalk10.cyan(`Serving docs from ${bookDir}`));
2453
3157
  await serveBook(bookDir);
2454
3158
  });
2455
3159
  docsCmd.command("commands").description("Show Makefile & melos commands reference").option("-d, --dir <path>", "project root directory", process.cwd()).action(async (options) => {
2456
3160
  const commands = discoverCommands(options.dir);
2457
3161
  if (commands.length === 0) {
2458
- console.log(chalk9.yellow("No commands found. Run from a monorepo root."));
3162
+ console.log(chalk10.yellow("No commands found. Run from a monorepo root."));
2459
3163
  return;
2460
3164
  }
2461
- console.log(chalk9.green(`Found ${commands.length} command(s)
3165
+ console.log(chalk10.green(`Found ${commands.length} command(s)
2462
3166
  `));
2463
3167
  displayCommands(commands);
2464
3168
  });
2465
3169
  docsCmd.command("architecture").description("Display monorepo architecture tree").option("-d, --dir <path>", "project root directory", process.cwd()).action(async (options) => {
2466
3170
  const info = discoverArchitecture(options.dir);
2467
3171
  if (!info) {
2468
- console.log(chalk9.yellow("No monorepo detected. Run from within a Melos monorepo."));
3172
+ console.log(chalk10.yellow("No monorepo detected. Run from within a Melos monorepo."));
2469
3173
  return;
2470
3174
  }
2471
3175
  displayArchitecture(info);