wlmaker 1.3.0 → 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.
- package/dist/cli.mjs +886 -148
- 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
|
|
6
|
+
import chalk10 from "chalk";
|
|
7
7
|
|
|
8
8
|
// src/core/create-bloc.ts
|
|
9
9
|
import * as fs3 from "fs";
|
|
@@ -956,11 +956,12 @@ async function createUseCase(name, tierInput, options) {
|
|
|
956
956
|
}
|
|
957
957
|
|
|
958
958
|
// src/interactive.ts
|
|
959
|
-
import * as
|
|
959
|
+
import * as fs14 from "fs";
|
|
960
960
|
import * as os from "os";
|
|
961
|
-
import * as
|
|
961
|
+
import * as path13 from "path";
|
|
962
|
+
import { execSync as execSync2 } from "child_process";
|
|
962
963
|
import * as clack from "@clack/prompts";
|
|
963
|
-
import
|
|
964
|
+
import chalk9 from "chalk";
|
|
964
965
|
|
|
965
966
|
// src/core/create-endpoint.ts
|
|
966
967
|
import * as fs9 from "fs";
|
|
@@ -1049,8 +1050,8 @@ ${paramsClass}
|
|
|
1049
1050
|
}
|
|
1050
1051
|
`;
|
|
1051
1052
|
}
|
|
1052
|
-
function retrofitMethod(methodName,
|
|
1053
|
-
const httpAnnotation = `@${httpMethod.toUpperCase()}('${
|
|
1053
|
+
function retrofitMethod(methodName, path14, httpMethod, params, returnType) {
|
|
1054
|
+
const httpAnnotation = `@${httpMethod.toUpperCase()}('${path14}')`;
|
|
1054
1055
|
const paramList = params.map((p) => {
|
|
1055
1056
|
if (p.isPath) return `@Path('${p.name}') ${p.type} ${p.name}`;
|
|
1056
1057
|
if (p.isBody) return `@Body() ${p.type} ${p.name}`;
|
|
@@ -1211,15 +1212,15 @@ async function createEndpoint(options) {
|
|
|
1211
1212
|
const methodParamsForSignature = methodParams.map((p) => ({ name: p.name, type: p.type }));
|
|
1212
1213
|
const modelType = `${pascal}Model`;
|
|
1213
1214
|
const entityType = `${pascal}Entity`;
|
|
1214
|
-
const
|
|
1215
|
-
|
|
1215
|
+
const spinner3 = (msg) => console.log(chalk4.cyan(` \u2192 ${msg}`));
|
|
1216
|
+
spinner3("Generating entity");
|
|
1216
1217
|
const entityDir = path8.join(lib, "domain", "entities", feature);
|
|
1217
1218
|
fs9.mkdirSync(entityDir, { recursive: true });
|
|
1218
1219
|
fs9.writeFileSync(
|
|
1219
1220
|
path8.join(entityDir, `${useCaseSnake}_entity.dart`),
|
|
1220
1221
|
entityBoilerplate(pascal)
|
|
1221
1222
|
);
|
|
1222
|
-
|
|
1223
|
+
spinner3("Generating model");
|
|
1223
1224
|
const modelDir = path8.join(lib, "data", "models", feature);
|
|
1224
1225
|
fs9.mkdirSync(modelDir, { recursive: true });
|
|
1225
1226
|
fs9.writeFileSync(
|
|
@@ -1227,7 +1228,7 @@ async function createEndpoint(options) {
|
|
|
1227
1228
|
modelBoilerplate(useCaseSnake, pascal, options.projectName)
|
|
1228
1229
|
);
|
|
1229
1230
|
if (needsBody) {
|
|
1230
|
-
|
|
1231
|
+
spinner3("Generating request model");
|
|
1231
1232
|
const reqModelDir = path8.join(lib, "data", "models", feature);
|
|
1232
1233
|
fs9.mkdirSync(reqModelDir, { recursive: true });
|
|
1233
1234
|
fs9.writeFileSync(
|
|
@@ -1235,7 +1236,7 @@ async function createEndpoint(options) {
|
|
|
1235
1236
|
requestModelBoilerplate(useCaseSnake, pascal)
|
|
1236
1237
|
);
|
|
1237
1238
|
}
|
|
1238
|
-
|
|
1239
|
+
spinner3("Injecting Retrofit method");
|
|
1239
1240
|
const bffPath = path8.resolve(options.bffApiFile);
|
|
1240
1241
|
if (fs9.existsSync(bffPath)) {
|
|
1241
1242
|
const retrofitParams = buildRetrofitParams(options, pathParams, needsBody);
|
|
@@ -1250,7 +1251,7 @@ async function createEndpoint(options) {
|
|
|
1250
1251
|
} else {
|
|
1251
1252
|
console.log(chalk4.yellow(` \u26A0 BFF API file not found: ${bffPath}`));
|
|
1252
1253
|
}
|
|
1253
|
-
|
|
1254
|
+
spinner3("Injecting datasource method");
|
|
1254
1255
|
const dsPath = path8.resolve(datasourceFile);
|
|
1255
1256
|
if (fs9.existsSync(dsPath)) {
|
|
1256
1257
|
const dsMethod = datasourceMethod(
|
|
@@ -1262,7 +1263,7 @@ async function createEndpoint(options) {
|
|
|
1262
1263
|
} else {
|
|
1263
1264
|
console.log(chalk4.yellow(` \u26A0 Datasource file not found: ${dsPath}`));
|
|
1264
1265
|
}
|
|
1265
|
-
|
|
1266
|
+
spinner3("Injecting repository interface method");
|
|
1266
1267
|
const repoIfacePath = path8.resolve(repositoryInterfaceFile);
|
|
1267
1268
|
if (fs9.existsSync(repoIfacePath)) {
|
|
1268
1269
|
const ifaceMethod = repositoryInterfaceMethod(
|
|
@@ -1274,7 +1275,7 @@ async function createEndpoint(options) {
|
|
|
1274
1275
|
} else {
|
|
1275
1276
|
console.log(chalk4.yellow(` \u26A0 Repository interface not found: ${repoIfacePath}`));
|
|
1276
1277
|
}
|
|
1277
|
-
|
|
1278
|
+
spinner3("Injecting repository implementation method");
|
|
1278
1279
|
const repoImplPath = path8.resolve(repositoryImplFile);
|
|
1279
1280
|
if (fs9.existsSync(repoImplPath)) {
|
|
1280
1281
|
const dsVarName = camelCase2(datasourceClassName);
|
|
@@ -1289,7 +1290,7 @@ async function createEndpoint(options) {
|
|
|
1289
1290
|
} else {
|
|
1290
1291
|
console.log(chalk4.yellow(` \u26A0 Repository implementation not found: ${repoImplPath}`));
|
|
1291
1292
|
}
|
|
1292
|
-
|
|
1293
|
+
spinner3("Generating UseCase");
|
|
1293
1294
|
const useCaseDir = path8.join(lib, "domain", "usecases", feature);
|
|
1294
1295
|
fs9.mkdirSync(useCaseDir, { recursive: true });
|
|
1295
1296
|
fs9.writeFileSync(
|
|
@@ -1303,7 +1304,7 @@ async function createEndpoint(options) {
|
|
|
1303
1304
|
options.projectName
|
|
1304
1305
|
)
|
|
1305
1306
|
);
|
|
1306
|
-
|
|
1307
|
+
spinner3("Updating barrel files");
|
|
1307
1308
|
const dsFileName = path8.basename(datasourceFile);
|
|
1308
1309
|
injectExport(
|
|
1309
1310
|
path8.join(lib, "data", "datasources", "datasources.dart"),
|
|
@@ -1331,7 +1332,7 @@ async function createEndpoint(options) {
|
|
|
1331
1332
|
`export '${feature}/${feature}.dart';`
|
|
1332
1333
|
);
|
|
1333
1334
|
if (options.diTarget && options.diTarget !== "none") {
|
|
1334
|
-
|
|
1335
|
+
spinner3("Registering in DI modules");
|
|
1335
1336
|
const appBaseDir = findAppBasePackageDir(options.projectRoot, options.diTarget);
|
|
1336
1337
|
if (appBaseDir) {
|
|
1337
1338
|
const domainPascal = pascalCase4(domain);
|
|
@@ -1464,24 +1465,594 @@ function injectModuleRegistration(filePath, className, registrationCode, dedupKe
|
|
|
1464
1465
|
fs9.writeFileSync(filePath, newContent);
|
|
1465
1466
|
}
|
|
1466
1467
|
|
|
1467
|
-
// src/core/
|
|
1468
|
-
import { spawn as spawn2 } from "child_process";
|
|
1468
|
+
// src/core/create-env-var.ts
|
|
1469
1469
|
import * as fs10 from "fs";
|
|
1470
1470
|
import * as path9 from "path";
|
|
1471
|
+
import { execSync } from "child_process";
|
|
1471
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";
|
|
1472
2043
|
function detectBookDir(startDir) {
|
|
1473
2044
|
const root = findMonorepoRoot(startDir) ?? startDir;
|
|
1474
|
-
const bookDir =
|
|
1475
|
-
if (
|
|
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")))) {
|
|
1476
2047
|
return bookDir;
|
|
1477
2048
|
}
|
|
1478
2049
|
return void 0;
|
|
1479
2050
|
}
|
|
1480
2051
|
function serveBook(bookDir) {
|
|
1481
2052
|
return new Promise((resolve4, reject) => {
|
|
1482
|
-
const nodeModules =
|
|
1483
|
-
if (!
|
|
1484
|
-
console.log(
|
|
2053
|
+
const nodeModules = path10.join(bookDir, "node_modules");
|
|
2054
|
+
if (!fs11.existsSync(nodeModules)) {
|
|
2055
|
+
console.log(chalk6.cyan("Installing book dependencies..."));
|
|
1485
2056
|
const install = spawn2("npm", ["install"], {
|
|
1486
2057
|
cwd: bookDir,
|
|
1487
2058
|
stdio: "inherit"
|
|
@@ -1515,16 +2086,16 @@ function startDevServer(bookDir, done) {
|
|
|
1515
2086
|
}
|
|
1516
2087
|
|
|
1517
2088
|
// src/core/docs-commands.ts
|
|
1518
|
-
import * as
|
|
1519
|
-
import * as
|
|
1520
|
-
import
|
|
2089
|
+
import * as fs12 from "fs";
|
|
2090
|
+
import * as path11 from "path";
|
|
2091
|
+
import chalk7 from "chalk";
|
|
1521
2092
|
import YAML2 from "yaml";
|
|
1522
2093
|
function parseMakefile(filePath) {
|
|
1523
|
-
if (!
|
|
1524
|
-
const content =
|
|
2094
|
+
if (!fs12.existsSync(filePath)) return [];
|
|
2095
|
+
const content = fs12.readFileSync(filePath, "utf8");
|
|
1525
2096
|
const lines = content.split("\n");
|
|
1526
2097
|
const commands = [];
|
|
1527
|
-
const source =
|
|
2098
|
+
const source = path11.basename(filePath);
|
|
1528
2099
|
let pendingComments = [];
|
|
1529
2100
|
for (let i = 0; i < lines.length; i++) {
|
|
1530
2101
|
const line = lines[i];
|
|
@@ -1554,13 +2125,13 @@ function parseMakefile(filePath) {
|
|
|
1554
2125
|
return commands;
|
|
1555
2126
|
}
|
|
1556
2127
|
function parseMelosScripts(filePath) {
|
|
1557
|
-
if (!
|
|
1558
|
-
const content =
|
|
2128
|
+
if (!fs12.existsSync(filePath)) return [];
|
|
2129
|
+
const content = fs12.readFileSync(filePath, "utf8");
|
|
1559
2130
|
const parsed = YAML2.parse(content);
|
|
1560
2131
|
const scripts = parsed?.scripts;
|
|
1561
2132
|
if (!scripts || typeof scripts !== "object") return [];
|
|
1562
2133
|
const commands = [];
|
|
1563
|
-
const source =
|
|
2134
|
+
const source = path11.basename(filePath);
|
|
1564
2135
|
for (const [name, value] of Object.entries(scripts)) {
|
|
1565
2136
|
let description = "";
|
|
1566
2137
|
let command = "";
|
|
@@ -1582,17 +2153,17 @@ function parseMelosScripts(filePath) {
|
|
|
1582
2153
|
function discoverCommands(startDir) {
|
|
1583
2154
|
const root = findMonorepoRoot(startDir) ?? startDir;
|
|
1584
2155
|
const commands = [];
|
|
1585
|
-
const rootMakefile =
|
|
2156
|
+
const rootMakefile = path11.join(root, "Makefile");
|
|
1586
2157
|
commands.push(...parseMakefile(rootMakefile));
|
|
1587
|
-
const melosFile =
|
|
2158
|
+
const melosFile = path11.join(root, "melos.yaml");
|
|
1588
2159
|
commands.push(...parseMelosScripts(melosFile));
|
|
1589
|
-
const bookMakefile =
|
|
2160
|
+
const bookMakefile = path11.join(root, "book", "Makefile");
|
|
1590
2161
|
commands.push(...parseMakefile(bookMakefile));
|
|
1591
2162
|
return commands;
|
|
1592
2163
|
}
|
|
1593
2164
|
function displayCommands(commands) {
|
|
1594
2165
|
if (commands.length === 0) {
|
|
1595
|
-
console.log(
|
|
2166
|
+
console.log(chalk7.yellow("No commands found."));
|
|
1596
2167
|
return;
|
|
1597
2168
|
}
|
|
1598
2169
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -1602,13 +2173,13 @@ function displayCommands(commands) {
|
|
|
1602
2173
|
groups.get(key).push(cmd);
|
|
1603
2174
|
}
|
|
1604
2175
|
for (const [group, entries] of groups) {
|
|
1605
|
-
console.log(
|
|
2176
|
+
console.log(chalk7.bold(chalk7.cyan(`
|
|
1606
2177
|
${group}`)));
|
|
1607
|
-
console.log(
|
|
2178
|
+
console.log(chalk7.cyan("\u2500".repeat(group.length)));
|
|
1608
2179
|
const maxNameLen = Math.max(...entries.map((e) => e.name.length));
|
|
1609
2180
|
for (const entry of entries) {
|
|
1610
|
-
const name =
|
|
1611
|
-
const desc = entry.description ?
|
|
2181
|
+
const name = chalk7.white(entry.name.padEnd(maxNameLen + 2));
|
|
2182
|
+
const desc = entry.description ? chalk7.gray(entry.description) : chalk7.dim("(no description)");
|
|
1612
2183
|
console.log(` ${name} ${desc}`);
|
|
1613
2184
|
}
|
|
1614
2185
|
}
|
|
@@ -1616,9 +2187,9 @@ ${group}`)));
|
|
|
1616
2187
|
}
|
|
1617
2188
|
|
|
1618
2189
|
// src/core/docs-architecture.ts
|
|
1619
|
-
import * as
|
|
1620
|
-
import * as
|
|
1621
|
-
import
|
|
2190
|
+
import * as fs13 from "fs";
|
|
2191
|
+
import * as path12 from "path";
|
|
2192
|
+
import chalk8 from "chalk";
|
|
1622
2193
|
import YAML3 from "yaml";
|
|
1623
2194
|
var KEY_DEPS = [
|
|
1624
2195
|
"freezed",
|
|
@@ -1636,18 +2207,18 @@ var KEY_DEPS = [
|
|
|
1636
2207
|
function discoverArchitecture(startDir) {
|
|
1637
2208
|
const root = findMonorepoRoot(startDir);
|
|
1638
2209
|
if (!root) return null;
|
|
1639
|
-
const melosPath =
|
|
1640
|
-
if (!
|
|
1641
|
-
const melosContent =
|
|
2210
|
+
const melosPath = path12.join(root, "melos.yaml");
|
|
2211
|
+
if (!fs13.existsSync(melosPath)) return null;
|
|
2212
|
+
const melosContent = fs13.readFileSync(melosPath, "utf8");
|
|
1642
2213
|
const melos = YAML3.parse(melosContent);
|
|
1643
|
-
const projectName = melos?.name ??
|
|
2214
|
+
const projectName = melos?.name ?? path12.basename(root);
|
|
1644
2215
|
const apps = [];
|
|
1645
|
-
const appsDir =
|
|
1646
|
-
if (
|
|
1647
|
-
for (const entry of
|
|
2216
|
+
const appsDir = path12.join(root, "apps");
|
|
2217
|
+
if (fs13.existsSync(appsDir)) {
|
|
2218
|
+
for (const entry of fs13.readdirSync(appsDir, { withFileTypes: true })) {
|
|
1648
2219
|
if (!entry.isDirectory()) continue;
|
|
1649
|
-
const candidate =
|
|
1650
|
-
if (
|
|
2220
|
+
const candidate = path12.join(appsDir, entry.name);
|
|
2221
|
+
if (fs13.existsSync(path12.join(candidate, "pubspec.yaml"))) {
|
|
1651
2222
|
apps.push({ name: entry.name, dir: candidate });
|
|
1652
2223
|
}
|
|
1653
2224
|
}
|
|
@@ -1655,14 +2226,14 @@ function discoverArchitecture(startDir) {
|
|
|
1655
2226
|
const packages = [];
|
|
1656
2227
|
const packageBases = ["packages", "packages/features"];
|
|
1657
2228
|
for (const base of packageBases) {
|
|
1658
|
-
const baseDir =
|
|
1659
|
-
if (!
|
|
1660
|
-
for (const entry of
|
|
2229
|
+
const baseDir = path12.join(root, base);
|
|
2230
|
+
if (!fs13.existsSync(baseDir)) continue;
|
|
2231
|
+
for (const entry of fs13.readdirSync(baseDir, { withFileTypes: true })) {
|
|
1661
2232
|
if (!entry.isDirectory()) continue;
|
|
1662
|
-
const candidate =
|
|
1663
|
-
const pubspecPath =
|
|
1664
|
-
if (!
|
|
1665
|
-
const content =
|
|
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");
|
|
1666
2237
|
const pubspec = YAML3.parse(content);
|
|
1667
2238
|
const deps = {
|
|
1668
2239
|
...pubspec?.dependencies,
|
|
@@ -1697,30 +2268,30 @@ function displayArchitecture(info) {
|
|
|
1697
2268
|
const L = "\u2514\u2500\u2500 ";
|
|
1698
2269
|
const I = "\u2502 ";
|
|
1699
2270
|
const S = " ";
|
|
1700
|
-
console.log(
|
|
2271
|
+
console.log(chalk8.bold(chalk8.cyan(info.projectName)) + chalk8.gray(` (${info.root})`));
|
|
1701
2272
|
console.log(`${I}`);
|
|
1702
2273
|
if (info.apps.length > 0) {
|
|
1703
|
-
console.log(
|
|
2274
|
+
console.log(chalk8.white(`${T}apps/`));
|
|
1704
2275
|
for (let i = 0; i < info.apps.length; i++) {
|
|
1705
2276
|
const app = info.apps[i];
|
|
1706
2277
|
const prefix = i === info.apps.length - 1 ? `${I}${S}${L}` : `${I}${S}${T}`;
|
|
1707
|
-
console.log(`${prefix}${
|
|
2278
|
+
console.log(`${prefix}${chalk8.green(app.name)}`);
|
|
1708
2279
|
}
|
|
1709
2280
|
}
|
|
1710
2281
|
if (info.packages.length > 0) {
|
|
1711
2282
|
const rootPkgs = info.packages.filter(
|
|
1712
|
-
(p) => !p.dir.includes(`${
|
|
2283
|
+
(p) => !p.dir.includes(`${path12.sep}features${path12.sep}`)
|
|
1713
2284
|
);
|
|
1714
2285
|
const featurePkgs = info.packages.filter(
|
|
1715
|
-
(p) => p.dir.includes(`${
|
|
2286
|
+
(p) => p.dir.includes(`${path12.sep}features${path12.sep}`)
|
|
1716
2287
|
);
|
|
1717
|
-
console.log(
|
|
2288
|
+
console.log(chalk8.white(`${T}packages/`));
|
|
1718
2289
|
for (let i = 0; i < rootPkgs.length; i++) {
|
|
1719
2290
|
const pkg2 = rootPkgs[i];
|
|
1720
2291
|
const isLast = i === rootPkgs.length - 1 && featurePkgs.length === 0;
|
|
1721
2292
|
const prefix = isLast ? `${I}${S}${L}` : `${I}${S}${T}`;
|
|
1722
|
-
const deps = pkg2.keyDeps.length > 0 ?
|
|
1723
|
-
console.log(`${prefix}${
|
|
2293
|
+
const deps = pkg2.keyDeps.length > 0 ? chalk8.dim(` [${pkg2.keyDeps.join(", ")}]`) : "";
|
|
2294
|
+
console.log(`${prefix}${chalk8.yellow(pkg2.name)}${deps}`);
|
|
1724
2295
|
}
|
|
1725
2296
|
if (featurePkgs.length > 0) {
|
|
1726
2297
|
console.log(`${I}${S}${T}features/`);
|
|
@@ -1728,21 +2299,21 @@ function displayArchitecture(info) {
|
|
|
1728
2299
|
const pkg2 = featurePkgs[i];
|
|
1729
2300
|
const isLast = i === featurePkgs.length - 1;
|
|
1730
2301
|
const prefix = isLast ? `${I}${S}${S}${L}` : `${I}${S}${S}${T}`;
|
|
1731
|
-
const deps = pkg2.keyDeps.length > 0 ?
|
|
1732
|
-
console.log(`${prefix}${
|
|
2302
|
+
const deps = pkg2.keyDeps.length > 0 ? chalk8.dim(` [${pkg2.keyDeps.join(", ")}]`) : "";
|
|
2303
|
+
console.log(`${prefix}${chalk8.yellow(pkg2.name)}${deps}`);
|
|
1733
2304
|
}
|
|
1734
2305
|
}
|
|
1735
2306
|
}
|
|
1736
2307
|
if (info.hasDesignSystem) {
|
|
1737
|
-
const tiers = info.designSystemTiers.length > 0 ?
|
|
1738
|
-
console.log(`${T}${
|
|
2308
|
+
const tiers = info.designSystemTiers.length > 0 ? chalk8.dim(` (${info.designSystemTiers.join(", ")})`) : "";
|
|
2309
|
+
console.log(`${T}${chalk8.magenta("design_system")}${tiers}`);
|
|
1739
2310
|
}
|
|
1740
|
-
const bookDir =
|
|
1741
|
-
if (
|
|
1742
|
-
console.log(`${T}${
|
|
2311
|
+
const bookDir = path12.join(info.root, "book");
|
|
2312
|
+
if (fs13.existsSync(bookDir)) {
|
|
2313
|
+
console.log(`${T}${chalk8.blue("book/")}` + chalk8.dim(" (Docusaurus)"));
|
|
1743
2314
|
}
|
|
1744
|
-
if (
|
|
1745
|
-
console.log(`${L}${
|
|
2315
|
+
if (fs13.existsSync(path12.join(info.root, "melos.yaml"))) {
|
|
2316
|
+
console.log(`${L}${chalk8.gray("melos.yaml")}`);
|
|
1746
2317
|
}
|
|
1747
2318
|
console.log();
|
|
1748
2319
|
}
|
|
@@ -1754,7 +2325,7 @@ async function resolveProject() {
|
|
|
1754
2325
|
s.start("Analyzing current directory...");
|
|
1755
2326
|
const cwdProject = analyzeProject(process.cwd());
|
|
1756
2327
|
if (cwdProject && (cwdProject.hasFreezed || cwdProject.hasBloc)) {
|
|
1757
|
-
s.stop(`Found ${
|
|
2328
|
+
s.stop(`Found ${chalk9.green(cwdProject.projectName)}`);
|
|
1758
2329
|
return cwdProject;
|
|
1759
2330
|
}
|
|
1760
2331
|
s.message("Looking for Melos monorepo...");
|
|
@@ -1767,8 +2338,8 @@ async function resolveProject() {
|
|
|
1767
2338
|
}
|
|
1768
2339
|
}
|
|
1769
2340
|
s.message("Scanning for Flutter projects...");
|
|
1770
|
-
const homeDev =
|
|
1771
|
-
if (
|
|
2341
|
+
const homeDev = path13.join(os.homedir(), "Development");
|
|
2342
|
+
if (fs14.existsSync(homeDev)) {
|
|
1772
2343
|
const projects = discoverProjects(homeDev, 2);
|
|
1773
2344
|
if (projects.length > 0) {
|
|
1774
2345
|
s.stop(`Found ${projects.length} Flutter project(s)`);
|
|
@@ -1776,12 +2347,12 @@ async function resolveProject() {
|
|
|
1776
2347
|
}
|
|
1777
2348
|
}
|
|
1778
2349
|
s.stop("No Flutter projects found");
|
|
1779
|
-
clack.outro(
|
|
2350
|
+
clack.outro(chalk9.red("Could not find any Flutter project with freezed or flutter_bloc."));
|
|
1780
2351
|
return null;
|
|
1781
2352
|
}
|
|
1782
2353
|
async function selectPackage(projects) {
|
|
1783
2354
|
if (projects.length === 1) {
|
|
1784
|
-
clack.log.info(`Using ${
|
|
2355
|
+
clack.log.info(`Using ${chalk9.green(projects[0].projectName)}`);
|
|
1785
2356
|
return projects[0];
|
|
1786
2357
|
}
|
|
1787
2358
|
const selected = await clack.select({
|
|
@@ -1789,7 +2360,7 @@ async function selectPackage(projects) {
|
|
|
1789
2360
|
options: projects.map((p) => ({
|
|
1790
2361
|
value: p,
|
|
1791
2362
|
label: p.projectName,
|
|
1792
|
-
hint:
|
|
2363
|
+
hint: path13.relative(os.homedir(), p.projectRoot)
|
|
1793
2364
|
}))
|
|
1794
2365
|
});
|
|
1795
2366
|
if (clack.isCancel(selected)) {
|
|
@@ -1837,13 +2408,13 @@ async function blocFlow(project) {
|
|
|
1837
2408
|
clack.cancel("Cancelled");
|
|
1838
2409
|
return;
|
|
1839
2410
|
}
|
|
1840
|
-
targetDir =
|
|
2411
|
+
targetDir = path13.resolve(customPath);
|
|
1841
2412
|
} else {
|
|
1842
|
-
targetDir =
|
|
2413
|
+
targetDir = path13.join(project.projectRoot, "lib", "features", feature);
|
|
1843
2414
|
}
|
|
1844
|
-
} else if (
|
|
1845
|
-
targetDir =
|
|
1846
|
-
clack.log.info(`Target: ${
|
|
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/")}`);
|
|
1847
2418
|
} else {
|
|
1848
2419
|
clack.note(
|
|
1849
2420
|
"No lib/features/ directory found. Provide a target path manually.",
|
|
@@ -1860,7 +2431,7 @@ async function blocFlow(project) {
|
|
|
1860
2431
|
clack.cancel("Cancelled");
|
|
1861
2432
|
return;
|
|
1862
2433
|
}
|
|
1863
|
-
targetDir =
|
|
2434
|
+
targetDir = path13.resolve(customPath);
|
|
1864
2435
|
}
|
|
1865
2436
|
const defaultRun = project.hasBuildRunner;
|
|
1866
2437
|
const runBuildRunner2 = await clack.confirm({
|
|
@@ -1879,10 +2450,10 @@ async function blocFlow(project) {
|
|
|
1879
2450
|
buildRunner: runBuildRunner2
|
|
1880
2451
|
});
|
|
1881
2452
|
genSpinner.stop("BLoC generated");
|
|
1882
|
-
clack.outro(
|
|
2453
|
+
clack.outro(chalk9.green("Done!"));
|
|
1883
2454
|
} catch (error) {
|
|
1884
2455
|
genSpinner.stop("Failed");
|
|
1885
|
-
clack.outro(
|
|
2456
|
+
clack.outro(chalk9.red(`Error: ${error}`));
|
|
1886
2457
|
}
|
|
1887
2458
|
}
|
|
1888
2459
|
async function widgetFlow() {
|
|
@@ -1890,14 +2461,14 @@ async function widgetFlow() {
|
|
|
1890
2461
|
const ds = detectDesignSystem(projectRoot);
|
|
1891
2462
|
if (!ds) {
|
|
1892
2463
|
clack.outro(
|
|
1893
|
-
|
|
2464
|
+
chalk9.red(
|
|
1894
2465
|
"No design system detected. Run this command from a project with wl_design_system/ directory."
|
|
1895
2466
|
)
|
|
1896
2467
|
);
|
|
1897
2468
|
return;
|
|
1898
2469
|
}
|
|
1899
2470
|
clack.log.info(
|
|
1900
|
-
`Design system: ${
|
|
2471
|
+
`Design system: ${chalk9.cyan(path13.relative(projectRoot, ds.componentsDir))}`
|
|
1901
2472
|
);
|
|
1902
2473
|
const name = await clack.text({
|
|
1903
2474
|
message: "Widget name (snake_case, without wl_ prefix)",
|
|
@@ -1962,17 +2533,17 @@ async function widgetFlow() {
|
|
|
1962
2533
|
genSpinner.stop(`Use-case skipped: ${e}`);
|
|
1963
2534
|
}
|
|
1964
2535
|
}
|
|
1965
|
-
clack.outro(
|
|
2536
|
+
clack.outro(chalk9.green("Done!"));
|
|
1966
2537
|
} catch (error) {
|
|
1967
2538
|
genSpinner.stop("Failed");
|
|
1968
|
-
clack.outro(
|
|
2539
|
+
clack.outro(chalk9.red(`Error: ${error}`));
|
|
1969
2540
|
}
|
|
1970
2541
|
}
|
|
1971
2542
|
async function useCaseFlow() {
|
|
1972
2543
|
const ds = detectDesignSystem(process.cwd());
|
|
1973
2544
|
if (!ds) {
|
|
1974
2545
|
clack.outro(
|
|
1975
|
-
|
|
2546
|
+
chalk9.red(
|
|
1976
2547
|
"No design system detected. Ensure wl_design_system/ directory exists."
|
|
1977
2548
|
)
|
|
1978
2549
|
);
|
|
@@ -1980,7 +2551,7 @@ async function useCaseFlow() {
|
|
|
1980
2551
|
}
|
|
1981
2552
|
if (!ds.widgetbookDir) {
|
|
1982
2553
|
clack.outro(
|
|
1983
|
-
|
|
2554
|
+
chalk9.red(
|
|
1984
2555
|
"No widgetbook package detected. Ensure apps/widgetbook/ exists."
|
|
1985
2556
|
)
|
|
1986
2557
|
);
|
|
@@ -2029,18 +2600,18 @@ async function useCaseFlow() {
|
|
|
2029
2600
|
buildRunner: runBuildRunner2
|
|
2030
2601
|
});
|
|
2031
2602
|
genSpinner.stop("Use-case generated");
|
|
2032
|
-
clack.outro(
|
|
2603
|
+
clack.outro(chalk9.green("Done!"));
|
|
2033
2604
|
} catch (error) {
|
|
2034
2605
|
genSpinner.stop("Failed");
|
|
2035
|
-
clack.outro(
|
|
2606
|
+
clack.outro(chalk9.red(`Error: ${error}`));
|
|
2036
2607
|
}
|
|
2037
2608
|
}
|
|
2038
2609
|
function findBffFiles(dir) {
|
|
2039
2610
|
const results = [];
|
|
2040
|
-
if (!
|
|
2041
|
-
const entries =
|
|
2611
|
+
if (!fs14.existsSync(dir)) return results;
|
|
2612
|
+
const entries = fs14.readdirSync(dir, { withFileTypes: true });
|
|
2042
2613
|
for (const entry of entries) {
|
|
2043
|
-
const fullPath =
|
|
2614
|
+
const fullPath = path13.join(dir, entry.name);
|
|
2044
2615
|
if (entry.isDirectory()) {
|
|
2045
2616
|
results.push(...findBffFiles(fullPath));
|
|
2046
2617
|
} else if (entry.isFile() && entry.name.endsWith(".dart") && !entry.name.includes(".g.")) {
|
|
@@ -2052,8 +2623,8 @@ function findBffFiles(dir) {
|
|
|
2052
2623
|
function inferBffFile(bffFiles, endpointPath) {
|
|
2053
2624
|
const firstSegment = endpointPath.replace(/^\//, "").split("/")[0].toLowerCase();
|
|
2054
2625
|
if (!firstSegment) return null;
|
|
2055
|
-
const withDomains = bffFiles.filter((f) => /^bff_.+_api\.dart$/.test(
|
|
2056
|
-
const base =
|
|
2626
|
+
const withDomains = bffFiles.filter((f) => /^bff_.+_api\.dart$/.test(path13.basename(f))).map((f) => {
|
|
2627
|
+
const base = path13.basename(f, ".dart");
|
|
2057
2628
|
const domain = base.replace(/^bff_/, "").replace(/_api$/, "");
|
|
2058
2629
|
return { file: f, domain };
|
|
2059
2630
|
});
|
|
@@ -2080,19 +2651,19 @@ async function resolveEndpointProject() {
|
|
|
2080
2651
|
s.message(`Monorepo found at ${monorepoRoot}`);
|
|
2081
2652
|
const packageBases = ["packages", "packages/features"];
|
|
2082
2653
|
for (const base of packageBases) {
|
|
2083
|
-
const baseDir =
|
|
2084
|
-
if (!
|
|
2085
|
-
const entries =
|
|
2654
|
+
const baseDir = path13.join(monorepoRoot, base);
|
|
2655
|
+
if (!fs14.existsSync(baseDir)) continue;
|
|
2656
|
+
const entries = fs14.readdirSync(baseDir, { withFileTypes: true });
|
|
2086
2657
|
for (const entry of entries) {
|
|
2087
2658
|
if (!entry.isDirectory()) continue;
|
|
2088
|
-
const candidate =
|
|
2089
|
-
if (!
|
|
2090
|
-
const bffPath =
|
|
2091
|
-
s.message(`Checking ${candidate} \u2192 bff exists: ${
|
|
2092
|
-
if (
|
|
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)) {
|
|
2093
2664
|
const project2 = analyzeProject(candidate);
|
|
2094
2665
|
if (project2) {
|
|
2095
|
-
s.stop(`Using ${
|
|
2666
|
+
s.stop(`Using ${chalk9.green(project2.projectName)}`);
|
|
2096
2667
|
return project2;
|
|
2097
2668
|
}
|
|
2098
2669
|
}
|
|
@@ -2102,8 +2673,8 @@ async function resolveEndpointProject() {
|
|
|
2102
2673
|
s.message("No monorepo root found");
|
|
2103
2674
|
}
|
|
2104
2675
|
const cwdProject = analyzeProject(process.cwd());
|
|
2105
|
-
if (cwdProject &&
|
|
2106
|
-
s.stop(`Using ${
|
|
2676
|
+
if (cwdProject && fs14.existsSync(path13.join(cwdProject.projectRoot, "lib", "data", "api", "bff"))) {
|
|
2677
|
+
s.stop(`Using ${chalk9.green(cwdProject.projectName)}`);
|
|
2107
2678
|
return cwdProject;
|
|
2108
2679
|
}
|
|
2109
2680
|
s.stop("No BFF package found");
|
|
@@ -2112,33 +2683,33 @@ async function resolveEndpointProject() {
|
|
|
2112
2683
|
placeholder: "e.g. /path/to/my-package or ./packages/core",
|
|
2113
2684
|
validate: (v) => {
|
|
2114
2685
|
if (!v.trim()) return "Path is required";
|
|
2115
|
-
const resolved2 =
|
|
2116
|
-
if (!
|
|
2117
|
-
if (!
|
|
2118
|
-
if (!
|
|
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";
|
|
2119
2690
|
}
|
|
2120
2691
|
});
|
|
2121
2692
|
if (clack.isCancel(manualPath)) {
|
|
2122
2693
|
clack.cancel("Cancelled");
|
|
2123
2694
|
return null;
|
|
2124
2695
|
}
|
|
2125
|
-
const resolved =
|
|
2696
|
+
const resolved = path13.resolve(manualPath);
|
|
2126
2697
|
const project = analyzeProject(resolved);
|
|
2127
2698
|
if (!project) {
|
|
2128
|
-
clack.outro(
|
|
2699
|
+
clack.outro(chalk9.red(`Could not analyze project at ${resolved}`));
|
|
2129
2700
|
return null;
|
|
2130
2701
|
}
|
|
2131
|
-
clack.log.info(`Using ${
|
|
2702
|
+
clack.log.info(`Using ${chalk9.green(project.projectName)}`);
|
|
2132
2703
|
return project;
|
|
2133
2704
|
}
|
|
2134
2705
|
async function endpointFlow() {
|
|
2135
2706
|
const project = await resolveEndpointProject();
|
|
2136
2707
|
if (!project) return;
|
|
2137
|
-
const lib =
|
|
2138
|
-
const bffDir =
|
|
2708
|
+
const lib = path13.join(project.projectRoot, "lib");
|
|
2709
|
+
const bffDir = path13.join(lib, "data", "api", "bff");
|
|
2139
2710
|
const bffFiles = findBffFiles(bffDir);
|
|
2140
2711
|
if (bffFiles.length === 0) {
|
|
2141
|
-
clack.outro(
|
|
2712
|
+
clack.outro(chalk9.red("No BFF API files found. Ensure lib/data/api/bff/ exists with .dart files."));
|
|
2142
2713
|
return;
|
|
2143
2714
|
}
|
|
2144
2715
|
const httpMethod = await clack.select({
|
|
@@ -2168,13 +2739,13 @@ async function endpointFlow() {
|
|
|
2168
2739
|
}
|
|
2169
2740
|
let bffApiFile = inferBffFile(bffFiles, endpointPath);
|
|
2170
2741
|
if (bffApiFile) {
|
|
2171
|
-
clack.log.info(`Auto-detected BFF file: ${
|
|
2742
|
+
clack.log.info(`Auto-detected BFF file: ${chalk9.cyan(path13.basename(bffApiFile))}`);
|
|
2172
2743
|
} else {
|
|
2173
2744
|
const selected = await clack.select({
|
|
2174
2745
|
message: "Could not auto-detect BFF file. Select one:",
|
|
2175
2746
|
options: bffFiles.map((f) => ({
|
|
2176
2747
|
value: f,
|
|
2177
|
-
label:
|
|
2748
|
+
label: path13.relative(bffDir, f)
|
|
2178
2749
|
}))
|
|
2179
2750
|
});
|
|
2180
2751
|
if (clack.isCancel(selected)) {
|
|
@@ -2238,14 +2809,14 @@ async function endpointFlow() {
|
|
|
2238
2809
|
diLazySingleton
|
|
2239
2810
|
});
|
|
2240
2811
|
genSpinner.stop("Endpoint generated");
|
|
2241
|
-
clack.outro(
|
|
2812
|
+
clack.outro(chalk9.green("Done!"));
|
|
2242
2813
|
} catch (error) {
|
|
2243
2814
|
genSpinner.stop("Failed");
|
|
2244
|
-
clack.outro(
|
|
2815
|
+
clack.outro(chalk9.red(`Error: ${error}`));
|
|
2245
2816
|
}
|
|
2246
2817
|
}
|
|
2247
2818
|
async function docsInteractiveMode() {
|
|
2248
|
-
clack.intro(
|
|
2819
|
+
clack.intro(chalk9.bgCyan(chalk9.black(" wlmaker docs ")));
|
|
2249
2820
|
const action = await clack.select({
|
|
2250
2821
|
message: "What do you want to do?",
|
|
2251
2822
|
options: [
|
|
@@ -2260,44 +2831,200 @@ async function docsInteractiveMode() {
|
|
|
2260
2831
|
}
|
|
2261
2832
|
switch (action) {
|
|
2262
2833
|
case "serve": {
|
|
2834
|
+
const serveMode = await clack.select({
|
|
2835
|
+
message: "How do you want to view the docs?",
|
|
2836
|
+
options: [
|
|
2837
|
+
{ value: "local", label: "Local", hint: "Start Docusaurus dev server locally" },
|
|
2838
|
+
{ value: "remote", label: "Remote", hint: "Open the deployed docs in your browser" }
|
|
2839
|
+
]
|
|
2840
|
+
});
|
|
2841
|
+
if (clack.isCancel(serveMode)) {
|
|
2842
|
+
clack.cancel("Cancelled");
|
|
2843
|
+
return;
|
|
2844
|
+
}
|
|
2845
|
+
if (serveMode === "remote") {
|
|
2846
|
+
const challenge = await clack.text({
|
|
2847
|
+
message: "Pregunta de seguridad: Qu\xE9 dice mechi cuando tiene una duda en el dise\xF1o?",
|
|
2848
|
+
placeholder: "...",
|
|
2849
|
+
validate: (input) => {
|
|
2850
|
+
const normalized = input.trim().toLowerCase().replace(/[áä]/g, "a").replace(/[éë]/g, "e").replace(/[íï]/g, "i").replace(/[óö]/g, "o").replace(/[úü]/g, "u");
|
|
2851
|
+
const expected = "deja le pregunto a cesita";
|
|
2852
|
+
if (normalized !== expected) {
|
|
2853
|
+
return "Respuesta incorrecta. Vuelve cuando sepas el lore.";
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
});
|
|
2857
|
+
if (clack.isCancel(challenge)) {
|
|
2858
|
+
clack.cancel("Cancelled");
|
|
2859
|
+
return;
|
|
2860
|
+
}
|
|
2861
|
+
const remoteUrl = "https://dc-wl-docs.web.app/development_workflow/";
|
|
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"));
|
|
2869
|
+
return;
|
|
2870
|
+
}
|
|
2263
2871
|
const bookDir = detectBookDir(process.cwd());
|
|
2264
2872
|
if (!bookDir) {
|
|
2265
2873
|
clack.outro(
|
|
2266
|
-
|
|
2874
|
+
chalk9.red("No Docusaurus book/ directory found. Run from a monorepo root.")
|
|
2267
2875
|
);
|
|
2268
2876
|
return;
|
|
2269
2877
|
}
|
|
2270
|
-
clack.log.info(`Serving docs from ${
|
|
2878
|
+
clack.log.info(`Serving docs from ${chalk9.cyan(bookDir)}`);
|
|
2271
2879
|
await serveBook(bookDir);
|
|
2272
2880
|
break;
|
|
2273
2881
|
}
|
|
2274
2882
|
case "commands": {
|
|
2275
2883
|
const commands = discoverCommands(process.cwd());
|
|
2276
2884
|
if (commands.length === 0) {
|
|
2277
|
-
clack.outro(
|
|
2885
|
+
clack.outro(chalk9.yellow("No commands found. Run from a monorepo root."));
|
|
2278
2886
|
return;
|
|
2279
2887
|
}
|
|
2280
|
-
clack.log.info(`Found ${
|
|
2888
|
+
clack.log.info(`Found ${chalk9.green(commands.length.toString())} command(s)`);
|
|
2281
2889
|
displayCommands(commands);
|
|
2282
|
-
clack.outro(
|
|
2890
|
+
clack.outro(chalk9.green("Done!"));
|
|
2283
2891
|
break;
|
|
2284
2892
|
}
|
|
2285
2893
|
case "architecture": {
|
|
2286
2894
|
const info = discoverArchitecture(process.cwd());
|
|
2287
2895
|
if (!info) {
|
|
2288
2896
|
clack.outro(
|
|
2289
|
-
|
|
2897
|
+
chalk9.yellow("No monorepo detected. Run from within a Melos monorepo.")
|
|
2290
2898
|
);
|
|
2291
2899
|
return;
|
|
2292
2900
|
}
|
|
2293
2901
|
displayArchitecture(info);
|
|
2294
|
-
clack.outro(
|
|
2902
|
+
clack.outro(chalk9.green("Done!"));
|
|
2295
2903
|
break;
|
|
2296
2904
|
}
|
|
2297
2905
|
}
|
|
2298
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
|
+
}
|
|
2299
3026
|
async function interactiveMode() {
|
|
2300
|
-
clack.intro(
|
|
3027
|
+
clack.intro(chalk9.bgCyan(chalk9.black(" wlmaker ")));
|
|
2301
3028
|
const createType = await clack.select({
|
|
2302
3029
|
message: "What do you want to create?",
|
|
2303
3030
|
options: [
|
|
@@ -2313,6 +3040,11 @@ async function interactiveMode() {
|
|
|
2313
3040
|
label: "Endpoint",
|
|
2314
3041
|
hint: "BFF Clean Architecture stack"
|
|
2315
3042
|
},
|
|
3043
|
+
{
|
|
3044
|
+
value: "env-var",
|
|
3045
|
+
label: "Env Var",
|
|
3046
|
+
hint: "Add environment variable to monorepo stack"
|
|
3047
|
+
},
|
|
2316
3048
|
{
|
|
2317
3049
|
value: "docs",
|
|
2318
3050
|
label: "Docs",
|
|
@@ -2341,6 +3073,9 @@ async function interactiveMode() {
|
|
|
2341
3073
|
case "endpoint":
|
|
2342
3074
|
await endpointFlow();
|
|
2343
3075
|
break;
|
|
3076
|
+
case "env-var":
|
|
3077
|
+
await envVarFlow();
|
|
3078
|
+
break;
|
|
2344
3079
|
case "docs":
|
|
2345
3080
|
await docsInteractiveMode();
|
|
2346
3081
|
break;
|
|
@@ -2363,7 +3098,7 @@ program.command("bloc").description("Create a new BLoC with Freezed sealed class
|
|
|
2363
3098
|
try {
|
|
2364
3099
|
await createBloc(name, options);
|
|
2365
3100
|
} catch (error) {
|
|
2366
|
-
console.error(
|
|
3101
|
+
console.error(chalk10.red(`Error: ${error}`));
|
|
2367
3102
|
process.exit(1);
|
|
2368
3103
|
}
|
|
2369
3104
|
}
|
|
@@ -2380,12 +3115,12 @@ program.command("widget").description("Create a new widget in the design system"
|
|
|
2380
3115
|
projectRoot: options.dir,
|
|
2381
3116
|
buildRunner: false
|
|
2382
3117
|
});
|
|
2383
|
-
console.log(
|
|
3118
|
+
console.log(chalk10.green("Widgetbook use-case created"));
|
|
2384
3119
|
} catch {
|
|
2385
|
-
console.log(
|
|
3120
|
+
console.log(chalk10.yellow("Use-case skipped (may already exist)"));
|
|
2386
3121
|
}
|
|
2387
3122
|
} catch (error) {
|
|
2388
|
-
console.error(
|
|
3123
|
+
console.error(chalk10.red(`Error: ${error}`));
|
|
2389
3124
|
process.exit(1);
|
|
2390
3125
|
}
|
|
2391
3126
|
}
|
|
@@ -2398,7 +3133,7 @@ program.command("usecase").description("Create a Widgetbook use-case for an exis
|
|
|
2398
3133
|
buildRunner: options.buildRunner
|
|
2399
3134
|
});
|
|
2400
3135
|
} catch (error) {
|
|
2401
|
-
console.error(
|
|
3136
|
+
console.error(chalk10.red(`Error: ${error}`));
|
|
2402
3137
|
process.exit(1);
|
|
2403
3138
|
}
|
|
2404
3139
|
}
|
|
@@ -2408,30 +3143,33 @@ program.command("endpoint").description("Generate Clean Architecture stack for a
|
|
|
2408
3143
|
if (!project) return;
|
|
2409
3144
|
await endpointFlow(project);
|
|
2410
3145
|
});
|
|
3146
|
+
program.command("env-var").description("Add an environment variable across the Flutter monorepo").action(async () => {
|
|
3147
|
+
await envVarFlow();
|
|
3148
|
+
});
|
|
2411
3149
|
var docsCmd = program.command("docs").description("Project documentation tools");
|
|
2412
3150
|
docsCmd.command("serve").description("Start Docusaurus dev server").option("-d, --dir <path>", "project root directory", process.cwd()).action(async (options) => {
|
|
2413
3151
|
const bookDir = detectBookDir(options.dir);
|
|
2414
3152
|
if (!bookDir) {
|
|
2415
|
-
console.error(
|
|
3153
|
+
console.error(chalk10.red("No Docusaurus book/ directory found. Run from a monorepo root."));
|
|
2416
3154
|
process.exit(1);
|
|
2417
3155
|
}
|
|
2418
|
-
console.log(
|
|
3156
|
+
console.log(chalk10.cyan(`Serving docs from ${bookDir}`));
|
|
2419
3157
|
await serveBook(bookDir);
|
|
2420
3158
|
});
|
|
2421
3159
|
docsCmd.command("commands").description("Show Makefile & melos commands reference").option("-d, --dir <path>", "project root directory", process.cwd()).action(async (options) => {
|
|
2422
3160
|
const commands = discoverCommands(options.dir);
|
|
2423
3161
|
if (commands.length === 0) {
|
|
2424
|
-
console.log(
|
|
3162
|
+
console.log(chalk10.yellow("No commands found. Run from a monorepo root."));
|
|
2425
3163
|
return;
|
|
2426
3164
|
}
|
|
2427
|
-
console.log(
|
|
3165
|
+
console.log(chalk10.green(`Found ${commands.length} command(s)
|
|
2428
3166
|
`));
|
|
2429
3167
|
displayCommands(commands);
|
|
2430
3168
|
});
|
|
2431
3169
|
docsCmd.command("architecture").description("Display monorepo architecture tree").option("-d, --dir <path>", "project root directory", process.cwd()).action(async (options) => {
|
|
2432
3170
|
const info = discoverArchitecture(options.dir);
|
|
2433
3171
|
if (!info) {
|
|
2434
|
-
console.log(
|
|
3172
|
+
console.log(chalk10.yellow("No monorepo detected. Run from within a Melos monorepo."));
|
|
2435
3173
|
return;
|
|
2436
3174
|
}
|
|
2437
3175
|
displayArchitecture(info);
|