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.
- package/dist/cli.mjs +856 -152
- 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,12 +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
|
|
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
|
|
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,
|
|
1054
|
-
const httpAnnotation = `@${httpMethod.toUpperCase()}('${
|
|
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
|
|
1216
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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/
|
|
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 =
|
|
1476
|
-
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")))) {
|
|
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 =
|
|
1484
|
-
if (!
|
|
1485
|
-
console.log(
|
|
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
|
|
1520
|
-
import * as
|
|
1521
|
-
import
|
|
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 (!
|
|
1525
|
-
const content =
|
|
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 =
|
|
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 (!
|
|
1559
|
-
const content =
|
|
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 =
|
|
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 =
|
|
2156
|
+
const rootMakefile = path11.join(root, "Makefile");
|
|
1587
2157
|
commands.push(...parseMakefile(rootMakefile));
|
|
1588
|
-
const melosFile =
|
|
2158
|
+
const melosFile = path11.join(root, "melos.yaml");
|
|
1589
2159
|
commands.push(...parseMelosScripts(melosFile));
|
|
1590
|
-
const bookMakefile =
|
|
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(
|
|
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(
|
|
2176
|
+
console.log(chalk7.bold(chalk7.cyan(`
|
|
1607
2177
|
${group}`)));
|
|
1608
|
-
console.log(
|
|
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 =
|
|
1612
|
-
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)");
|
|
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
|
|
1621
|
-
import * as
|
|
1622
|
-
import
|
|
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 =
|
|
1641
|
-
if (!
|
|
1642
|
-
const melosContent =
|
|
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 ??
|
|
2214
|
+
const projectName = melos?.name ?? path12.basename(root);
|
|
1645
2215
|
const apps = [];
|
|
1646
|
-
const appsDir =
|
|
1647
|
-
if (
|
|
1648
|
-
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 })) {
|
|
1649
2219
|
if (!entry.isDirectory()) continue;
|
|
1650
|
-
const candidate =
|
|
1651
|
-
if (
|
|
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 =
|
|
1660
|
-
if (!
|
|
1661
|
-
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 })) {
|
|
1662
2232
|
if (!entry.isDirectory()) continue;
|
|
1663
|
-
const candidate =
|
|
1664
|
-
const pubspecPath =
|
|
1665
|
-
if (!
|
|
1666
|
-
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");
|
|
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(
|
|
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(
|
|
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}${
|
|
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(`${
|
|
2283
|
+
(p) => !p.dir.includes(`${path12.sep}features${path12.sep}`)
|
|
1714
2284
|
);
|
|
1715
2285
|
const featurePkgs = info.packages.filter(
|
|
1716
|
-
(p) => p.dir.includes(`${
|
|
2286
|
+
(p) => p.dir.includes(`${path12.sep}features${path12.sep}`)
|
|
1717
2287
|
);
|
|
1718
|
-
console.log(
|
|
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 ?
|
|
1724
|
-
console.log(`${prefix}${
|
|
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 ?
|
|
1733
|
-
console.log(`${prefix}${
|
|
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 ?
|
|
1739
|
-
console.log(`${T}${
|
|
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 =
|
|
1742
|
-
if (
|
|
1743
|
-
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)"));
|
|
1744
2314
|
}
|
|
1745
|
-
if (
|
|
1746
|
-
console.log(`${L}${
|
|
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 ${
|
|
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 =
|
|
1772
|
-
if (
|
|
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(
|
|
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 ${
|
|
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:
|
|
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 =
|
|
2411
|
+
targetDir = path13.resolve(customPath);
|
|
1842
2412
|
} else {
|
|
1843
|
-
targetDir =
|
|
2413
|
+
targetDir = path13.join(project.projectRoot, "lib", "features", feature);
|
|
1844
2414
|
}
|
|
1845
|
-
} else if (
|
|
1846
|
-
targetDir =
|
|
1847
|
-
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/")}`);
|
|
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 =
|
|
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(
|
|
2453
|
+
clack.outro(chalk9.green("Done!"));
|
|
1884
2454
|
} catch (error) {
|
|
1885
2455
|
genSpinner.stop("Failed");
|
|
1886
|
-
clack.outro(
|
|
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
|
-
|
|
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: ${
|
|
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(
|
|
2536
|
+
clack.outro(chalk9.green("Done!"));
|
|
1967
2537
|
} catch (error) {
|
|
1968
2538
|
genSpinner.stop("Failed");
|
|
1969
|
-
clack.outro(
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
2603
|
+
clack.outro(chalk9.green("Done!"));
|
|
2034
2604
|
} catch (error) {
|
|
2035
2605
|
genSpinner.stop("Failed");
|
|
2036
|
-
clack.outro(
|
|
2606
|
+
clack.outro(chalk9.red(`Error: ${error}`));
|
|
2037
2607
|
}
|
|
2038
2608
|
}
|
|
2039
2609
|
function findBffFiles(dir) {
|
|
2040
2610
|
const results = [];
|
|
2041
|
-
if (!
|
|
2042
|
-
const entries =
|
|
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 =
|
|
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(
|
|
2057
|
-
const base =
|
|
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 =
|
|
2085
|
-
if (!
|
|
2086
|
-
const entries =
|
|
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 =
|
|
2090
|
-
if (!
|
|
2091
|
-
const bffPath =
|
|
2092
|
-
s.message(`Checking ${candidate} \u2192 bff exists: ${
|
|
2093
|
-
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)) {
|
|
2094
2664
|
const project2 = analyzeProject(candidate);
|
|
2095
2665
|
if (project2) {
|
|
2096
|
-
s.stop(`Using ${
|
|
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 &&
|
|
2107
|
-
s.stop(`Using ${
|
|
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 =
|
|
2117
|
-
if (!
|
|
2118
|
-
if (!
|
|
2119
|
-
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";
|
|
2120
2690
|
}
|
|
2121
2691
|
});
|
|
2122
2692
|
if (clack.isCancel(manualPath)) {
|
|
2123
2693
|
clack.cancel("Cancelled");
|
|
2124
2694
|
return null;
|
|
2125
2695
|
}
|
|
2126
|
-
const resolved =
|
|
2696
|
+
const resolved = path13.resolve(manualPath);
|
|
2127
2697
|
const project = analyzeProject(resolved);
|
|
2128
2698
|
if (!project) {
|
|
2129
|
-
clack.outro(
|
|
2699
|
+
clack.outro(chalk9.red(`Could not analyze project at ${resolved}`));
|
|
2130
2700
|
return null;
|
|
2131
2701
|
}
|
|
2132
|
-
clack.log.info(`Using ${
|
|
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 =
|
|
2139
|
-
const bffDir =
|
|
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(
|
|
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: ${
|
|
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:
|
|
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(
|
|
2812
|
+
clack.outro(chalk9.green("Done!"));
|
|
2243
2813
|
} catch (error) {
|
|
2244
2814
|
genSpinner.stop("Failed");
|
|
2245
|
-
clack.outro(
|
|
2815
|
+
clack.outro(chalk9.red(`Error: ${error}`));
|
|
2246
2816
|
}
|
|
2247
2817
|
}
|
|
2248
2818
|
async function docsInteractiveMode() {
|
|
2249
|
-
clack.intro(
|
|
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
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
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
|
-
|
|
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 ${
|
|
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(
|
|
2885
|
+
clack.outro(chalk9.yellow("No commands found. Run from a monorepo root."));
|
|
2312
2886
|
return;
|
|
2313
2887
|
}
|
|
2314
|
-
clack.log.info(`Found ${
|
|
2888
|
+
clack.log.info(`Found ${chalk9.green(commands.length.toString())} command(s)`);
|
|
2315
2889
|
displayCommands(commands);
|
|
2316
|
-
clack.outro(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
3118
|
+
console.log(chalk10.green("Widgetbook use-case created"));
|
|
2418
3119
|
} catch {
|
|
2419
|
-
console.log(
|
|
3120
|
+
console.log(chalk10.yellow("Use-case skipped (may already exist)"));
|
|
2420
3121
|
}
|
|
2421
3122
|
} catch (error) {
|
|
2422
|
-
console.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(
|
|
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(
|
|
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(
|
|
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(
|
|
3162
|
+
console.log(chalk10.yellow("No commands found. Run from a monorepo root."));
|
|
2459
3163
|
return;
|
|
2460
3164
|
}
|
|
2461
|
-
console.log(
|
|
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(
|
|
3172
|
+
console.log(chalk10.yellow("No monorepo detected. Run from within a Melos monorepo."));
|
|
2469
3173
|
return;
|
|
2470
3174
|
}
|
|
2471
3175
|
displayArchitecture(info);
|