wlmaker 1.4.0 → 1.5.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 +1082 -333
- package/package.json +1 -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 chalk12 from "chalk";
|
|
7
7
|
|
|
8
8
|
// src/core/create-bloc.ts
|
|
9
9
|
import * as fs3 from "fs";
|
|
@@ -106,17 +106,17 @@ function hasBuildRunner(projectRoot) {
|
|
|
106
106
|
return /build_runner/.test(pubspec);
|
|
107
107
|
}
|
|
108
108
|
function runBuildRunner(projectRoot) {
|
|
109
|
-
return new Promise((
|
|
109
|
+
return new Promise((resolve5) => {
|
|
110
110
|
const child = spawn(
|
|
111
111
|
"dart",
|
|
112
112
|
["run", "build_runner", "build", "--delete-conflicting-outputs"],
|
|
113
113
|
{ cwd: projectRoot, stdio: "inherit" }
|
|
114
114
|
);
|
|
115
115
|
child.on("close", (code) => {
|
|
116
|
-
|
|
116
|
+
resolve5();
|
|
117
117
|
});
|
|
118
118
|
child.on("error", () => {
|
|
119
|
-
|
|
119
|
+
resolve5();
|
|
120
120
|
});
|
|
121
121
|
});
|
|
122
122
|
}
|
|
@@ -955,19 +955,154 @@ async function createUseCase(name, tierInput, options) {
|
|
|
955
955
|
}
|
|
956
956
|
}
|
|
957
957
|
|
|
958
|
+
// src/core/create-page.ts
|
|
959
|
+
import * as fs8 from "fs";
|
|
960
|
+
import * as path8 from "path";
|
|
961
|
+
import chalk4 from "chalk";
|
|
962
|
+
|
|
963
|
+
// src/core/page-templates.ts
|
|
964
|
+
import { pascalCase as pascalCase4 } from "change-case";
|
|
965
|
+
function pageTemplate(name, packageName) {
|
|
966
|
+
const pascal = pascalCase4(name);
|
|
967
|
+
const routePath = "/" + name;
|
|
968
|
+
const imports = [
|
|
969
|
+
`package:flutter/material.dart`,
|
|
970
|
+
`package:go_router/go_router.dart`,
|
|
971
|
+
`package:${packageName}/pages/views/${name}_view.dart`
|
|
972
|
+
].sort();
|
|
973
|
+
return `${imports.map((i) => `import '${i}';`).join("\n")}
|
|
974
|
+
|
|
975
|
+
class ${pascal}Page extends GoRoute {
|
|
976
|
+
${pascal}Page({super.name, super.routes})
|
|
977
|
+
: super(
|
|
978
|
+
path: fullPath,
|
|
979
|
+
pageBuilder: (context, state) =>
|
|
980
|
+
const MaterialPage(child: ${pascal}View()),
|
|
981
|
+
);
|
|
982
|
+
|
|
983
|
+
static const fullPath = '${routePath}';
|
|
984
|
+
|
|
985
|
+
static void open(BuildContext context) => context.go(fullPath);
|
|
986
|
+
}
|
|
987
|
+
`;
|
|
988
|
+
}
|
|
989
|
+
function viewTemplate(name) {
|
|
990
|
+
const pascal = pascalCase4(name);
|
|
991
|
+
return `import 'package:flutter/material.dart';
|
|
992
|
+
|
|
993
|
+
class ${pascal}View extends StatelessWidget {
|
|
994
|
+
const ${pascal}View({super.key});
|
|
995
|
+
|
|
996
|
+
@override
|
|
997
|
+
Widget build(BuildContext context) {
|
|
998
|
+
return Scaffold(
|
|
999
|
+
appBar: AppBar(title: const Text('${pascal}')),
|
|
1000
|
+
body: const Center(child: Text('${pascal}')),
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
`;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
// src/core/create-page.ts
|
|
1008
|
+
var SNAKE_CASE_REGEX4 = /^[a-z][a-z0-9_]*$/;
|
|
1009
|
+
async function createPage(name, options) {
|
|
1010
|
+
const pagesPath = path8.resolve(options.pagesPath);
|
|
1011
|
+
if (!name || name.trim().length === 0) {
|
|
1012
|
+
throw new Error("Page name is required.");
|
|
1013
|
+
}
|
|
1014
|
+
if (!SNAKE_CASE_REGEX4.test(name)) {
|
|
1015
|
+
throw new Error("Page name must be snake_case (lowercase letters, digits, underscores).");
|
|
1016
|
+
}
|
|
1017
|
+
if (!fs8.existsSync(pagesPath)) {
|
|
1018
|
+
throw new Error(`Path does not exist: ${pagesPath}`);
|
|
1019
|
+
}
|
|
1020
|
+
const packageName = extractPackageName(pagesPath);
|
|
1021
|
+
const pageFile = path8.join(pagesPath, `${name}_page.dart`);
|
|
1022
|
+
const viewsDir = path8.join(pagesPath, "views");
|
|
1023
|
+
const viewFile = path8.join(viewsDir, `${name}_view.dart`);
|
|
1024
|
+
if (fs8.existsSync(pageFile)) {
|
|
1025
|
+
throw new Error(`Page "${name}" already exists at ${pageFile}`);
|
|
1026
|
+
}
|
|
1027
|
+
if (fs8.existsSync(viewFile)) {
|
|
1028
|
+
throw new Error(`View "${name}" already exists at ${viewFile}`);
|
|
1029
|
+
}
|
|
1030
|
+
fs8.mkdirSync(viewsDir, { recursive: true });
|
|
1031
|
+
if (!packageName) {
|
|
1032
|
+
throw new Error(
|
|
1033
|
+
`Could not determine package name from path: ${pagesPath}
|
|
1034
|
+
Expected path pattern: .../packages/<name>/lib/pages`
|
|
1035
|
+
);
|
|
1036
|
+
}
|
|
1037
|
+
fs8.writeFileSync(pageFile, pageTemplate(name, packageName));
|
|
1038
|
+
console.log(chalk4.green(` \u2713 Page file created: ${pageFile}`));
|
|
1039
|
+
fs8.writeFileSync(viewFile, viewTemplate(name));
|
|
1040
|
+
console.log(chalk4.green(` \u2713 View file created: ${viewFile}`));
|
|
1041
|
+
updateViewsBarrel(viewsDir, name);
|
|
1042
|
+
updatePagesBarrel(pagesPath, name);
|
|
1043
|
+
}
|
|
1044
|
+
function updateViewsBarrel(viewsDir, name) {
|
|
1045
|
+
const barrelPath = path8.join(viewsDir, "views.dart");
|
|
1046
|
+
const exportLine = `export '${name}_view.dart';`;
|
|
1047
|
+
let lines = [];
|
|
1048
|
+
if (fs8.existsSync(barrelPath)) {
|
|
1049
|
+
const content = fs8.readFileSync(barrelPath, "utf8");
|
|
1050
|
+
lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
1051
|
+
}
|
|
1052
|
+
if (lines.includes(exportLine)) {
|
|
1053
|
+
console.log(chalk4.blue(" \xB7 Export already in views.dart"));
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
lines.push(exportLine);
|
|
1057
|
+
lines.sort();
|
|
1058
|
+
fs8.writeFileSync(barrelPath, lines.join("\n") + "\n");
|
|
1059
|
+
console.log(chalk4.green(" \u2713 views.dart updated"));
|
|
1060
|
+
}
|
|
1061
|
+
function updatePagesBarrel(pagesPath, name) {
|
|
1062
|
+
const barrelPath = path8.join(pagesPath, "pages.dart");
|
|
1063
|
+
const pageExport = `export '${name}_page.dart';`;
|
|
1064
|
+
const viewsExport = `export 'views/views.dart';`;
|
|
1065
|
+
let lines = [];
|
|
1066
|
+
if (fs8.existsSync(barrelPath)) {
|
|
1067
|
+
const content = fs8.readFileSync(barrelPath, "utf8");
|
|
1068
|
+
lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
1069
|
+
}
|
|
1070
|
+
let modified = false;
|
|
1071
|
+
if (!lines.includes(pageExport)) {
|
|
1072
|
+
lines.push(pageExport);
|
|
1073
|
+
modified = true;
|
|
1074
|
+
}
|
|
1075
|
+
if (!lines.includes(viewsExport)) {
|
|
1076
|
+
lines.push(viewsExport);
|
|
1077
|
+
modified = true;
|
|
1078
|
+
}
|
|
1079
|
+
if (modified) {
|
|
1080
|
+
lines.sort();
|
|
1081
|
+
fs8.writeFileSync(barrelPath, lines.join("\n") + "\n");
|
|
1082
|
+
console.log(chalk4.green(" \u2713 pages.dart updated"));
|
|
1083
|
+
} else {
|
|
1084
|
+
console.log(chalk4.blue(" \xB7 pages.dart already up to date"));
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
function extractPackageName(pagesPath) {
|
|
1088
|
+
const normalized = pagesPath.replace(/\\/g, "/");
|
|
1089
|
+
const match = normalized.match(/\/([^/]+)\/lib\/pages\/?$/);
|
|
1090
|
+
return match ? match[1] : null;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
958
1093
|
// src/interactive.ts
|
|
959
|
-
import * as
|
|
1094
|
+
import * as fs16 from "fs";
|
|
960
1095
|
import * as os from "os";
|
|
961
|
-
import * as
|
|
962
|
-
import { execSync as
|
|
1096
|
+
import * as path15 from "path";
|
|
1097
|
+
import { execSync as execSync3 } from "child_process";
|
|
963
1098
|
import * as clack from "@clack/prompts";
|
|
964
|
-
import
|
|
1099
|
+
import chalk11 from "chalk";
|
|
965
1100
|
|
|
966
1101
|
// src/core/create-endpoint.ts
|
|
967
|
-
import * as
|
|
968
|
-
import * as
|
|
969
|
-
import
|
|
970
|
-
import { pascalCase as
|
|
1102
|
+
import * as fs10 from "fs";
|
|
1103
|
+
import * as path9 from "path";
|
|
1104
|
+
import chalk5 from "chalk";
|
|
1105
|
+
import { pascalCase as pascalCase5, camelCase as camelCase2 } from "change-case";
|
|
971
1106
|
|
|
972
1107
|
// src/core/endpoint-templates.ts
|
|
973
1108
|
import { camelCase } from "change-case";
|
|
@@ -1050,8 +1185,8 @@ ${paramsClass}
|
|
|
1050
1185
|
}
|
|
1051
1186
|
`;
|
|
1052
1187
|
}
|
|
1053
|
-
function retrofitMethod(methodName,
|
|
1054
|
-
const httpAnnotation = `@${httpMethod.toUpperCase()}('${
|
|
1188
|
+
function retrofitMethod(methodName, path16, httpMethod, params, returnType) {
|
|
1189
|
+
const httpAnnotation = `@${httpMethod.toUpperCase()}('${path16}')`;
|
|
1055
1190
|
const paramList = params.map((p) => {
|
|
1056
1191
|
if (p.isPath) return `@Path('${p.name}') ${p.type} ${p.name}`;
|
|
1057
1192
|
if (p.isBody) return `@Body() ${p.type} ${p.name}`;
|
|
@@ -1117,9 +1252,9 @@ ${annotation}${useCaseClass} ${useCaseCamel}(${repoInterface} repository) =>
|
|
|
1117
1252
|
}
|
|
1118
1253
|
|
|
1119
1254
|
// src/core/dart-injector.ts
|
|
1120
|
-
import * as
|
|
1255
|
+
import * as fs9 from "fs";
|
|
1121
1256
|
function injectMethod(filePath, className, methodCode, dedupKey) {
|
|
1122
|
-
const content =
|
|
1257
|
+
const content = fs9.readFileSync(filePath, "utf8");
|
|
1123
1258
|
const dedup = dedupKey ?? findSignature(methodCode);
|
|
1124
1259
|
if (dedup && content.includes(dedup)) {
|
|
1125
1260
|
return;
|
|
@@ -1150,10 +1285,10 @@ function injectMethod(filePath, className, methodCode, dedupKey) {
|
|
|
1150
1285
|
}
|
|
1151
1286
|
const indentedMethod = methodCode.split("\n").map((line) => line.trim() ? ` ${line}` : "").join("\n");
|
|
1152
1287
|
const newContent = content.slice(0, classEnd) + "\n" + indentedMethod + "\n" + content.slice(classEnd);
|
|
1153
|
-
|
|
1288
|
+
fs9.writeFileSync(filePath, newContent);
|
|
1154
1289
|
}
|
|
1155
1290
|
function injectImport(filePath, importLine) {
|
|
1156
|
-
const content =
|
|
1291
|
+
const content = fs9.readFileSync(filePath, "utf8");
|
|
1157
1292
|
if (content.includes(importLine.trim())) {
|
|
1158
1293
|
return;
|
|
1159
1294
|
}
|
|
@@ -1163,15 +1298,15 @@ function injectImport(filePath, importLine) {
|
|
|
1163
1298
|
const lastImport = imports[imports.length - 1];
|
|
1164
1299
|
const insertPos = lastImport.index + lastImport[0].length;
|
|
1165
1300
|
const newContent = content.slice(0, insertPos) + "\n" + importLine + content.slice(insertPos);
|
|
1166
|
-
|
|
1301
|
+
fs9.writeFileSync(filePath, newContent);
|
|
1167
1302
|
} else {
|
|
1168
|
-
|
|
1303
|
+
fs9.writeFileSync(filePath, importLine + "\n\n" + content);
|
|
1169
1304
|
}
|
|
1170
1305
|
}
|
|
1171
1306
|
function injectExport(filePath, exportLine) {
|
|
1172
1307
|
let content = "";
|
|
1173
|
-
if (
|
|
1174
|
-
content =
|
|
1308
|
+
if (fs9.existsSync(filePath)) {
|
|
1309
|
+
content = fs9.readFileSync(filePath, "utf8");
|
|
1175
1310
|
}
|
|
1176
1311
|
if (content.includes(exportLine)) {
|
|
1177
1312
|
return;
|
|
@@ -1179,7 +1314,7 @@ function injectExport(filePath, exportLine) {
|
|
|
1179
1314
|
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
1180
1315
|
lines.push(exportLine);
|
|
1181
1316
|
lines.sort();
|
|
1182
|
-
|
|
1317
|
+
fs9.writeFileSync(filePath, lines.join("\n") + "\n");
|
|
1183
1318
|
}
|
|
1184
1319
|
function findSignature(methodCode) {
|
|
1185
1320
|
const lines = methodCode.trim().split("\n");
|
|
@@ -1194,51 +1329,51 @@ function findSignature(methodCode) {
|
|
|
1194
1329
|
|
|
1195
1330
|
// src/core/create-endpoint.ts
|
|
1196
1331
|
async function createEndpoint(options) {
|
|
1197
|
-
const lib =
|
|
1198
|
-
const pascal =
|
|
1199
|
-
const useCasePascal =
|
|
1332
|
+
const lib = path9.join(options.projectRoot, "lib");
|
|
1333
|
+
const pascal = pascalCase5(options.useCaseName);
|
|
1334
|
+
const useCasePascal = pascalCase5(options.useCaseName);
|
|
1200
1335
|
const useCaseSnake = options.useCaseName;
|
|
1201
1336
|
const needsBody = ["POST", "PUT", "PATCH"].includes(options.httpMethod);
|
|
1202
1337
|
const domain = extractDomain(options.bffApiFile);
|
|
1203
|
-
const datasourceFile =
|
|
1204
|
-
const datasourceClassName = `${
|
|
1205
|
-
const repositoryInterfaceFile =
|
|
1206
|
-
const repositoryInterfaceName = `${
|
|
1207
|
-
const repositoryImplFile =
|
|
1208
|
-
const repositoryImplClassName = `${
|
|
1338
|
+
const datasourceFile = path9.join(lib, "data", "datasources", `${domain}_rest_datasource.dart`);
|
|
1339
|
+
const datasourceClassName = `${pascalCase5(domain)}RestDataSource`;
|
|
1340
|
+
const repositoryInterfaceFile = path9.join(lib, "domain", "repositories", `${domain}_repository.dart`);
|
|
1341
|
+
const repositoryInterfaceName = `${pascalCase5(domain)}Repository`;
|
|
1342
|
+
const repositoryImplFile = path9.join(lib, "data", "repositories", `${domain}_repository_data.dart`);
|
|
1343
|
+
const repositoryImplClassName = `${pascalCase5(domain)}RepositoryData`;
|
|
1209
1344
|
const feature = domain;
|
|
1210
1345
|
const pathParams = extractPathParams(options.endpointPath);
|
|
1211
1346
|
const methodParams = buildMethodParams(options, pathParams, needsBody);
|
|
1212
1347
|
const methodParamsForSignature = methodParams.map((p) => ({ name: p.name, type: p.type }));
|
|
1213
1348
|
const modelType = `${pascal}Model`;
|
|
1214
1349
|
const entityType = `${pascal}Entity`;
|
|
1215
|
-
const spinner3 = (msg) => console.log(
|
|
1350
|
+
const spinner3 = (msg) => console.log(chalk5.cyan(` \u2192 ${msg}`));
|
|
1216
1351
|
spinner3("Generating entity");
|
|
1217
|
-
const entityDir =
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1352
|
+
const entityDir = path9.join(lib, "domain", "entities", feature);
|
|
1353
|
+
fs10.mkdirSync(entityDir, { recursive: true });
|
|
1354
|
+
fs10.writeFileSync(
|
|
1355
|
+
path9.join(entityDir, `${useCaseSnake}_entity.dart`),
|
|
1221
1356
|
entityBoilerplate(pascal)
|
|
1222
1357
|
);
|
|
1223
1358
|
spinner3("Generating model");
|
|
1224
|
-
const modelDir =
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1359
|
+
const modelDir = path9.join(lib, "data", "models", feature);
|
|
1360
|
+
fs10.mkdirSync(modelDir, { recursive: true });
|
|
1361
|
+
fs10.writeFileSync(
|
|
1362
|
+
path9.join(modelDir, `${useCaseSnake}_model.dart`),
|
|
1228
1363
|
modelBoilerplate(useCaseSnake, pascal, options.projectName)
|
|
1229
1364
|
);
|
|
1230
1365
|
if (needsBody) {
|
|
1231
1366
|
spinner3("Generating request model");
|
|
1232
|
-
const reqModelDir =
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1367
|
+
const reqModelDir = path9.join(lib, "data", "models", feature);
|
|
1368
|
+
fs10.mkdirSync(reqModelDir, { recursive: true });
|
|
1369
|
+
fs10.writeFileSync(
|
|
1370
|
+
path9.join(reqModelDir, `${useCaseSnake}_request_model.dart`),
|
|
1236
1371
|
requestModelBoilerplate(useCaseSnake, pascal)
|
|
1237
1372
|
);
|
|
1238
1373
|
}
|
|
1239
1374
|
spinner3("Injecting Retrofit method");
|
|
1240
|
-
const bffPath =
|
|
1241
|
-
if (
|
|
1375
|
+
const bffPath = path9.resolve(options.bffApiFile);
|
|
1376
|
+
if (fs10.existsSync(bffPath)) {
|
|
1242
1377
|
const retrofitParams = buildRetrofitParams(options, pathParams, needsBody);
|
|
1243
1378
|
const method = retrofitMethod(
|
|
1244
1379
|
camelCase2(options.useCaseName),
|
|
@@ -1249,11 +1384,11 @@ async function createEndpoint(options) {
|
|
|
1249
1384
|
);
|
|
1250
1385
|
injectMethod(bffPath, extractClassName(bffPath), method);
|
|
1251
1386
|
} else {
|
|
1252
|
-
console.log(
|
|
1387
|
+
console.log(chalk5.yellow(` \u26A0 BFF API file not found: ${bffPath}`));
|
|
1253
1388
|
}
|
|
1254
1389
|
spinner3("Injecting datasource method");
|
|
1255
|
-
const dsPath =
|
|
1256
|
-
if (
|
|
1390
|
+
const dsPath = path9.resolve(datasourceFile);
|
|
1391
|
+
if (fs10.existsSync(dsPath)) {
|
|
1257
1392
|
const dsMethod = datasourceMethod(
|
|
1258
1393
|
camelCase2(options.useCaseName),
|
|
1259
1394
|
modelType,
|
|
@@ -1261,11 +1396,11 @@ async function createEndpoint(options) {
|
|
|
1261
1396
|
);
|
|
1262
1397
|
injectMethod(dsPath, datasourceClassName, dsMethod);
|
|
1263
1398
|
} else {
|
|
1264
|
-
console.log(
|
|
1399
|
+
console.log(chalk5.yellow(` \u26A0 Datasource file not found: ${dsPath}`));
|
|
1265
1400
|
}
|
|
1266
1401
|
spinner3("Injecting repository interface method");
|
|
1267
|
-
const repoIfacePath =
|
|
1268
|
-
if (
|
|
1402
|
+
const repoIfacePath = path9.resolve(repositoryInterfaceFile);
|
|
1403
|
+
if (fs10.existsSync(repoIfacePath)) {
|
|
1269
1404
|
const ifaceMethod = repositoryInterfaceMethod(
|
|
1270
1405
|
camelCase2(options.useCaseName),
|
|
1271
1406
|
entityType,
|
|
@@ -1273,11 +1408,11 @@ async function createEndpoint(options) {
|
|
|
1273
1408
|
);
|
|
1274
1409
|
injectMethod(repoIfacePath, repositoryInterfaceName, ifaceMethod);
|
|
1275
1410
|
} else {
|
|
1276
|
-
console.log(
|
|
1411
|
+
console.log(chalk5.yellow(` \u26A0 Repository interface not found: ${repoIfacePath}`));
|
|
1277
1412
|
}
|
|
1278
1413
|
spinner3("Injecting repository implementation method");
|
|
1279
|
-
const repoImplPath =
|
|
1280
|
-
if (
|
|
1414
|
+
const repoImplPath = path9.resolve(repositoryImplFile);
|
|
1415
|
+
if (fs10.existsSync(repoImplPath)) {
|
|
1281
1416
|
const dsVarName = camelCase2(datasourceClassName);
|
|
1282
1417
|
const implMethod = repositoryImplMethod(
|
|
1283
1418
|
camelCase2(options.useCaseName),
|
|
@@ -1288,13 +1423,13 @@ async function createEndpoint(options) {
|
|
|
1288
1423
|
);
|
|
1289
1424
|
injectMethod(repoImplPath, repositoryImplClassName, implMethod);
|
|
1290
1425
|
} else {
|
|
1291
|
-
console.log(
|
|
1426
|
+
console.log(chalk5.yellow(` \u26A0 Repository implementation not found: ${repoImplPath}`));
|
|
1292
1427
|
}
|
|
1293
1428
|
spinner3("Generating UseCase");
|
|
1294
|
-
const useCaseDir =
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1429
|
+
const useCaseDir = path9.join(lib, "domain", "usecases", feature);
|
|
1430
|
+
fs10.mkdirSync(useCaseDir, { recursive: true });
|
|
1431
|
+
fs10.writeFileSync(
|
|
1432
|
+
path9.join(useCaseDir, `${useCaseSnake}_usecase.dart`),
|
|
1298
1433
|
useCaseTemplate2(
|
|
1299
1434
|
useCasePascal,
|
|
1300
1435
|
camelCase2(options.useCaseName),
|
|
@@ -1305,39 +1440,39 @@ async function createEndpoint(options) {
|
|
|
1305
1440
|
)
|
|
1306
1441
|
);
|
|
1307
1442
|
spinner3("Updating barrel files");
|
|
1308
|
-
const dsFileName =
|
|
1443
|
+
const dsFileName = path9.basename(datasourceFile);
|
|
1309
1444
|
injectExport(
|
|
1310
|
-
|
|
1445
|
+
path9.join(lib, "data", "datasources", "datasources.dart"),
|
|
1311
1446
|
`export '${dsFileName}';`
|
|
1312
1447
|
);
|
|
1313
|
-
const entityBarrel =
|
|
1448
|
+
const entityBarrel = path9.join(lib, "domain", "entities", feature, `${feature}.dart`);
|
|
1314
1449
|
injectExport(entityBarrel, `export '${useCaseSnake}_entity.dart';`);
|
|
1315
1450
|
injectExport(
|
|
1316
|
-
|
|
1451
|
+
path9.join(lib, "domain", "entities", "entities.dart"),
|
|
1317
1452
|
`export '${feature}/${feature}.dart';`
|
|
1318
1453
|
);
|
|
1319
|
-
const modelBarrel =
|
|
1454
|
+
const modelBarrel = path9.join(lib, "data", "models", feature, `${feature}.dart`);
|
|
1320
1455
|
injectExport(modelBarrel, `export '${useCaseSnake}_model.dart';`);
|
|
1321
1456
|
if (needsBody) {
|
|
1322
1457
|
injectExport(modelBarrel, `export '${useCaseSnake}_request_model.dart';`);
|
|
1323
1458
|
}
|
|
1324
1459
|
injectExport(
|
|
1325
|
-
|
|
1460
|
+
path9.join(lib, "data", "models", "models.dart"),
|
|
1326
1461
|
`export '${feature}/${feature}.dart';`
|
|
1327
1462
|
);
|
|
1328
|
-
const useCaseBarrel =
|
|
1463
|
+
const useCaseBarrel = path9.join(useCaseDir, `${feature}.dart`);
|
|
1329
1464
|
injectExport(useCaseBarrel, `export '${useCaseSnake}_usecase.dart';`);
|
|
1330
1465
|
injectExport(
|
|
1331
|
-
|
|
1466
|
+
path9.join(lib, "domain", "usecases", "usecases.dart"),
|
|
1332
1467
|
`export '${feature}/${feature}.dart';`
|
|
1333
1468
|
);
|
|
1334
1469
|
if (options.diTarget && options.diTarget !== "none") {
|
|
1335
1470
|
spinner3("Registering in DI modules");
|
|
1336
1471
|
const appBaseDir = findAppBasePackageDir(options.projectRoot, options.diTarget);
|
|
1337
1472
|
if (appBaseDir) {
|
|
1338
|
-
const domainPascal =
|
|
1473
|
+
const domainPascal = pascalCase5(domain);
|
|
1339
1474
|
const lazy = options.diLazySingleton !== false;
|
|
1340
|
-
const dsModuleFile =
|
|
1475
|
+
const dsModuleFile = path9.join(appBaseDir, "datasources_module.dart");
|
|
1341
1476
|
const dsRegistration = datasourceModuleRegistration(domainPascal, lazy);
|
|
1342
1477
|
injectModuleRegistration(
|
|
1343
1478
|
dsModuleFile,
|
|
@@ -1345,7 +1480,7 @@ async function createEndpoint(options) {
|
|
|
1345
1480
|
dsRegistration,
|
|
1346
1481
|
`${domainPascal}RestDataSource`
|
|
1347
1482
|
);
|
|
1348
|
-
const repoModuleFile =
|
|
1483
|
+
const repoModuleFile = path9.join(appBaseDir, "repositories_module.dart");
|
|
1349
1484
|
const repoRegistration = repositoryModuleRegistration(domainPascal, lazy);
|
|
1350
1485
|
injectModuleRegistration(
|
|
1351
1486
|
repoModuleFile,
|
|
@@ -1353,7 +1488,7 @@ async function createEndpoint(options) {
|
|
|
1353
1488
|
repoRegistration,
|
|
1354
1489
|
`${domainPascal}RepositoryData`
|
|
1355
1490
|
);
|
|
1356
|
-
const ucModuleFile =
|
|
1491
|
+
const ucModuleFile = path9.join(appBaseDir, "usecases_module.dart");
|
|
1357
1492
|
const ucRegistration = useCaseModuleRegistration(useCasePascal, domainPascal, lazy);
|
|
1358
1493
|
injectModuleRegistration(
|
|
1359
1494
|
ucModuleFile,
|
|
@@ -1366,14 +1501,14 @@ async function createEndpoint(options) {
|
|
|
1366
1501
|
injectImport(repoModuleFile, coreImport);
|
|
1367
1502
|
injectImport(ucModuleFile, coreImport);
|
|
1368
1503
|
} else {
|
|
1369
|
-
console.log(
|
|
1504
|
+
console.log(chalk5.yellow(` \u26A0 Package "${options.diTarget}" not found in monorepo`));
|
|
1370
1505
|
}
|
|
1371
1506
|
}
|
|
1372
|
-
console.log(
|
|
1507
|
+
console.log(chalk5.green(`
|
|
1373
1508
|
\u2713 Endpoint "${options.useCaseName}" generated successfully.`));
|
|
1374
1509
|
}
|
|
1375
1510
|
function extractDomain(bffApiFile) {
|
|
1376
|
-
const baseName =
|
|
1511
|
+
const baseName = path9.basename(bffApiFile);
|
|
1377
1512
|
const match = baseName.match(/^bff_(.+)_api\.dart$/);
|
|
1378
1513
|
if (match) return match[1];
|
|
1379
1514
|
return baseName.replace(/\.dart$/, "");
|
|
@@ -1390,7 +1525,7 @@ function extractPathParams(endpointPath) {
|
|
|
1390
1525
|
function buildMethodParams(options, pathParams, hasRequestBody) {
|
|
1391
1526
|
const params = [...pathParams];
|
|
1392
1527
|
if (hasRequestBody) {
|
|
1393
|
-
const reqPascal =
|
|
1528
|
+
const reqPascal = pascalCase5(options.useCaseName);
|
|
1394
1529
|
params.push({ name: "body", type: `${reqPascal}RequestModel` });
|
|
1395
1530
|
}
|
|
1396
1531
|
return params;
|
|
@@ -1401,43 +1536,43 @@ function buildRetrofitParams(options, pathParams, hasRequestBody) {
|
|
|
1401
1536
|
params.push({ ...pp, isPath: true });
|
|
1402
1537
|
}
|
|
1403
1538
|
if (hasRequestBody) {
|
|
1404
|
-
const reqPascal =
|
|
1539
|
+
const reqPascal = pascalCase5(options.useCaseName);
|
|
1405
1540
|
params.push({ name: "body", type: `${reqPascal}RequestModel`, isBody: true });
|
|
1406
1541
|
}
|
|
1407
1542
|
return params;
|
|
1408
1543
|
}
|
|
1409
1544
|
function extractClassName(filePath) {
|
|
1410
|
-
const content =
|
|
1545
|
+
const content = fs10.readFileSync(filePath, "utf8");
|
|
1411
1546
|
const match = content.match(/class\s+(\w+)\s+/);
|
|
1412
1547
|
if (!match) throw new Error(`No class found in ${filePath}`);
|
|
1413
1548
|
return match[1];
|
|
1414
1549
|
}
|
|
1415
1550
|
function findAppBasePackageDir(projectRoot, packageName) {
|
|
1416
|
-
let dir =
|
|
1551
|
+
let dir = path9.resolve(projectRoot);
|
|
1417
1552
|
for (let i = 0; i < 10; i++) {
|
|
1418
|
-
const candidate =
|
|
1419
|
-
if (
|
|
1553
|
+
const candidate = path9.join(dir, "packages", packageName, "lib", "dependencies");
|
|
1554
|
+
if (fs10.existsSync(candidate)) {
|
|
1420
1555
|
return candidate;
|
|
1421
1556
|
}
|
|
1422
|
-
const parent =
|
|
1557
|
+
const parent = path9.dirname(dir);
|
|
1423
1558
|
if (parent === dir) break;
|
|
1424
1559
|
dir = parent;
|
|
1425
1560
|
}
|
|
1426
1561
|
return null;
|
|
1427
1562
|
}
|
|
1428
1563
|
function injectModuleRegistration(filePath, className, registrationCode, dedupKey) {
|
|
1429
|
-
if (!
|
|
1430
|
-
console.log(
|
|
1564
|
+
if (!fs10.existsSync(filePath)) {
|
|
1565
|
+
console.log(chalk5.yellow(` \u26A0 Module file not found: ${filePath}`));
|
|
1431
1566
|
return;
|
|
1432
1567
|
}
|
|
1433
|
-
const content =
|
|
1568
|
+
const content = fs10.readFileSync(filePath, "utf8");
|
|
1434
1569
|
if (content.includes(dedupKey)) {
|
|
1435
1570
|
return;
|
|
1436
1571
|
}
|
|
1437
1572
|
const classRegex = new RegExp(`class\\s+${className}\\s*[^{]*\\{`);
|
|
1438
1573
|
const classMatch = content.match(classRegex);
|
|
1439
1574
|
if (!classMatch) {
|
|
1440
|
-
console.log(
|
|
1575
|
+
console.log(chalk5.yellow(` \u26A0 Class "${className}" not found in ${filePath}`));
|
|
1441
1576
|
return;
|
|
1442
1577
|
}
|
|
1443
1578
|
const classStart = content.indexOf(classMatch[0]);
|
|
@@ -1457,19 +1592,19 @@ function injectModuleRegistration(filePath, className, registrationCode, dedupKe
|
|
|
1457
1592
|
}
|
|
1458
1593
|
}
|
|
1459
1594
|
if (classEnd === -1) {
|
|
1460
|
-
console.log(
|
|
1595
|
+
console.log(chalk5.yellow(` \u26A0 Could not find closing brace for "${className}" in ${filePath}`));
|
|
1461
1596
|
return;
|
|
1462
1597
|
}
|
|
1463
1598
|
const indentedCode = registrationCode.split("\n").map((line) => line.trim() ? ` ${line}` : "").join("\n");
|
|
1464
1599
|
const newContent = content.slice(0, classEnd) + "\n" + indentedCode + "\n" + content.slice(classEnd);
|
|
1465
|
-
|
|
1600
|
+
fs10.writeFileSync(filePath, newContent);
|
|
1466
1601
|
}
|
|
1467
1602
|
|
|
1468
1603
|
// src/core/create-env-var.ts
|
|
1469
|
-
import * as
|
|
1470
|
-
import * as
|
|
1604
|
+
import * as fs11 from "fs";
|
|
1605
|
+
import * as path10 from "path";
|
|
1471
1606
|
import { execSync } from "child_process";
|
|
1472
|
-
import
|
|
1607
|
+
import chalk6 from "chalk";
|
|
1473
1608
|
import { camelCase as camelCase3 } from "change-case";
|
|
1474
1609
|
function screamingSnakeToCamel(name) {
|
|
1475
1610
|
return camelCase3(name.toLowerCase());
|
|
@@ -1557,38 +1692,39 @@ function appConfigModelJsonKey(camelName, dartType) {
|
|
|
1557
1692
|
}
|
|
1558
1693
|
}
|
|
1559
1694
|
function discoverAppsWithEnv(monorepoRoot) {
|
|
1560
|
-
const appsDir =
|
|
1561
|
-
if (!
|
|
1562
|
-
return
|
|
1695
|
+
const appsDir = path10.join(monorepoRoot, "apps");
|
|
1696
|
+
if (!fs11.existsSync(appsDir)) return [];
|
|
1697
|
+
return fs11.readdirSync(appsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).filter((d) => fs11.existsSync(path10.join(appsDir, d.name, "env"))).map((d) => d.name).sort();
|
|
1563
1698
|
}
|
|
1564
1699
|
function injectIntoJsonFile(filePath, key, value, isTemplate) {
|
|
1565
|
-
if (!
|
|
1566
|
-
console.log(
|
|
1567
|
-
return;
|
|
1700
|
+
if (!fs11.existsSync(filePath)) {
|
|
1701
|
+
console.log(chalk6.yellow(` Skipping ${filePath} (not found)`));
|
|
1702
|
+
return null;
|
|
1568
1703
|
}
|
|
1569
|
-
const content =
|
|
1704
|
+
const content = fs11.readFileSync(filePath, "utf8");
|
|
1570
1705
|
const json = JSON.parse(content);
|
|
1571
1706
|
if (key in json) {
|
|
1572
|
-
console.log(
|
|
1573
|
-
return;
|
|
1707
|
+
console.log(chalk6.yellow(` Key "${key}" already exists in ${path10.basename(filePath)}`));
|
|
1708
|
+
return null;
|
|
1574
1709
|
}
|
|
1575
1710
|
json[key] = isTemplate ? `\${${key}}` : value;
|
|
1576
|
-
|
|
1577
|
-
console.log(
|
|
1711
|
+
fs11.writeFileSync(filePath, JSON.stringify(json, null, 2) + "\n");
|
|
1712
|
+
console.log(chalk6.green(` Updated ${path10.basename(filePath)}`));
|
|
1713
|
+
return filePath;
|
|
1578
1714
|
}
|
|
1579
1715
|
function injectAbstractGetter(monorepoRoot, camelName, dartType) {
|
|
1580
|
-
const packagesDir =
|
|
1581
|
-
if (!
|
|
1716
|
+
const packagesDir = path10.join(monorepoRoot, "packages");
|
|
1717
|
+
if (!fs11.existsSync(packagesDir)) return null;
|
|
1582
1718
|
const envFile = findFileInPackages(packagesDir, "app_environment.dart");
|
|
1583
1719
|
if (!envFile) {
|
|
1584
|
-
console.log(
|
|
1585
|
-
return;
|
|
1720
|
+
console.log(chalk6.yellow(" app_environment.dart not found in any package"));
|
|
1721
|
+
return null;
|
|
1586
1722
|
}
|
|
1587
|
-
const content =
|
|
1723
|
+
const content = fs11.readFileSync(envFile, "utf8");
|
|
1588
1724
|
const dedup = `get ${camelName}`;
|
|
1589
1725
|
if (content.includes(dedup)) {
|
|
1590
|
-
console.log(
|
|
1591
|
-
return;
|
|
1726
|
+
console.log(chalk6.yellow(` '${camelName}' already in AppEnvironment`));
|
|
1727
|
+
return null;
|
|
1592
1728
|
}
|
|
1593
1729
|
const getterRegex = /\n\s+\w+(?:<[^>]+>)?\s+get\s+\w+;/g;
|
|
1594
1730
|
let lastGetterMatch = null;
|
|
@@ -1597,43 +1733,46 @@ function injectAbstractGetter(monorepoRoot, camelName, dartType) {
|
|
|
1597
1733
|
lastGetterMatch = match;
|
|
1598
1734
|
}
|
|
1599
1735
|
if (!lastGetterMatch) {
|
|
1600
|
-
console.log(
|
|
1601
|
-
return;
|
|
1736
|
+
console.log(chalk6.yellow(" No getters found in AppEnvironment"));
|
|
1737
|
+
return null;
|
|
1602
1738
|
}
|
|
1603
1739
|
const insertPos = lastGetterMatch.index + lastGetterMatch[0].length;
|
|
1604
1740
|
const getterLine = `
|
|
1605
1741
|
${abstractGetterCode(camelName, dartType)}`;
|
|
1606
1742
|
const newContent = content.slice(0, insertPos) + getterLine + content.slice(insertPos);
|
|
1607
|
-
|
|
1608
|
-
console.log(
|
|
1743
|
+
fs11.writeFileSync(envFile, newContent);
|
|
1744
|
+
console.log(chalk6.green(` Injected abstract getter in ${formatPkgPath(monorepoRoot, envFile)}`));
|
|
1745
|
+
return envFile;
|
|
1609
1746
|
}
|
|
1610
1747
|
function injectBuildEnvGetter(monorepoRoot, variableName, camelName, dartType) {
|
|
1611
|
-
const packagesDir =
|
|
1612
|
-
if (!
|
|
1748
|
+
const packagesDir = path10.join(monorepoRoot, "packages");
|
|
1749
|
+
if (!fs11.existsSync(packagesDir)) return null;
|
|
1613
1750
|
const buildEnvFile = findFileInPackages(packagesDir, "app_build_environment.dart");
|
|
1614
1751
|
if (!buildEnvFile) {
|
|
1615
|
-
console.log(
|
|
1616
|
-
return;
|
|
1752
|
+
console.log(chalk6.yellow(" app_build_environment.dart not found in any package"));
|
|
1753
|
+
return null;
|
|
1617
1754
|
}
|
|
1618
1755
|
const code = buildEnvGetterCode(camelName, variableName, dartType);
|
|
1619
1756
|
try {
|
|
1620
1757
|
injectMethod(buildEnvFile, "AppBuildEnvironment", code, `get ${camelName}`);
|
|
1621
|
-
console.log(
|
|
1758
|
+
console.log(chalk6.green(` Injected build env getter in ${formatPkgPath(monorepoRoot, buildEnvFile)}`));
|
|
1759
|
+
return buildEnvFile;
|
|
1622
1760
|
} catch (e) {
|
|
1623
|
-
console.log(
|
|
1761
|
+
console.log(chalk6.yellow(` Skipped: ${e.message}`));
|
|
1762
|
+
return null;
|
|
1624
1763
|
}
|
|
1625
1764
|
}
|
|
1626
1765
|
function injectIntoRemoteConfigDefaults(vendorsPath, camelName, dartType) {
|
|
1627
|
-
const content =
|
|
1766
|
+
const content = fs11.readFileSync(vendorsPath, "utf8");
|
|
1628
1767
|
if (content.includes(`'${camelName}'`) || content.includes(`"${camelName}"`)) {
|
|
1629
|
-
console.log(
|
|
1630
|
-
return;
|
|
1768
|
+
console.log(chalk6.yellow(` '${camelName}' already in ${path10.basename(vendorsPath)}`));
|
|
1769
|
+
return null;
|
|
1631
1770
|
}
|
|
1632
1771
|
const defaultValuesRegex = /final\s+defaultValues\s*=\s*(?:<String,\s*dynamic>\s*)?\{/;
|
|
1633
1772
|
const match = content.match(defaultValuesRegex);
|
|
1634
1773
|
if (!match) {
|
|
1635
|
-
console.log(
|
|
1636
|
-
return;
|
|
1774
|
+
console.log(chalk6.yellow(` Could not find defaultValues map in ${path10.basename(vendorsPath)}`));
|
|
1775
|
+
return null;
|
|
1637
1776
|
}
|
|
1638
1777
|
const mapStart = content.indexOf(match[0]) + match[0].length;
|
|
1639
1778
|
let braceCount = 1;
|
|
@@ -1649,8 +1788,8 @@ function injectIntoRemoteConfigDefaults(vendorsPath, camelName, dartType) {
|
|
|
1649
1788
|
}
|
|
1650
1789
|
}
|
|
1651
1790
|
if (mapEnd === -1) {
|
|
1652
|
-
console.log(
|
|
1653
|
-
return;
|
|
1791
|
+
console.log(chalk6.yellow(` Could not find end of defaultValues map`));
|
|
1792
|
+
return null;
|
|
1654
1793
|
}
|
|
1655
1794
|
const valueExpr = dartType === "List<String>" ? `jsonEncode(env.${camelName})` : `env.${camelName}`;
|
|
1656
1795
|
const beforeClose = content.slice(0, mapEnd);
|
|
@@ -1667,48 +1806,49 @@ function injectIntoRemoteConfigDefaults(vendorsPath, camelName, dartType) {
|
|
|
1667
1806
|
const insertion = `${lastEntryIndent}'${camelName}': ${valueExpr},
|
|
1668
1807
|
`;
|
|
1669
1808
|
const newContent = content.slice(0, mapEnd) + insertion + content.slice(mapEnd);
|
|
1670
|
-
|
|
1671
|
-
console.log(
|
|
1809
|
+
fs11.writeFileSync(vendorsPath, newContent);
|
|
1810
|
+
console.log(chalk6.green(` Injected into VendorsModule defaultValues`));
|
|
1811
|
+
return vendorsPath;
|
|
1672
1812
|
}
|
|
1673
1813
|
function injectAppConfigProperty(configPath, camelName, dartType) {
|
|
1674
|
-
const content =
|
|
1814
|
+
const content = fs11.readFileSync(configPath, "utf8");
|
|
1675
1815
|
if (content.includes(`final ${dartTypeForGetter(dartType)} ${camelName}`)) {
|
|
1676
|
-
console.log(
|
|
1677
|
-
return;
|
|
1816
|
+
console.log(chalk6.yellow(` '${camelName}' already in AppConfig`));
|
|
1817
|
+
return null;
|
|
1678
1818
|
}
|
|
1679
1819
|
const fieldLine = ` final ${dartTypeForGetter(dartType)} ${camelName};
|
|
1680
1820
|
`;
|
|
1681
1821
|
let newContent = content;
|
|
1682
|
-
const
|
|
1683
|
-
if (
|
|
1684
|
-
|
|
1685
|
-
newContent = newContent.slice(0, propsPos) + fieldLine + newContent.slice(propsPos);
|
|
1822
|
+
const propsSeparator = newContent.search(/\n\n(\s*)@override\n\s+List<Object\?>/);
|
|
1823
|
+
if (propsSeparator !== -1) {
|
|
1824
|
+
newContent = newContent.slice(0, propsSeparator + 1) + fieldLine + newContent.slice(propsSeparator + 1);
|
|
1686
1825
|
} else {
|
|
1687
1826
|
newContent = injectBeforeClassClose(newContent, "AppConfig", ` final ${dartTypeForGetter(dartType)} ${camelName};`);
|
|
1688
1827
|
}
|
|
1689
1828
|
newContent = injectConstructorParam(newContent, "AppConfig", camelName, dartType);
|
|
1690
1829
|
newContent = injectIntoPropsList(newContent, camelName);
|
|
1691
|
-
|
|
1692
|
-
console.log(
|
|
1830
|
+
fs11.writeFileSync(configPath, newContent);
|
|
1831
|
+
console.log(chalk6.green(` Injected into AppConfig entity`));
|
|
1832
|
+
return configPath;
|
|
1693
1833
|
}
|
|
1694
1834
|
function injectAppConfigModelProperty(modelPath, camelName, dartType) {
|
|
1695
|
-
const content =
|
|
1835
|
+
const content = fs11.readFileSync(modelPath, "utf8");
|
|
1696
1836
|
if (content.includes(`final ${dartTypeForGetter(dartType)} ${camelName}`)) {
|
|
1697
|
-
console.log(
|
|
1698
|
-
return;
|
|
1837
|
+
console.log(chalk6.yellow(` '${camelName}' already in AppConfigModel`));
|
|
1838
|
+
return null;
|
|
1699
1839
|
}
|
|
1700
1840
|
const jsonKey = appConfigModelJsonKey(camelName, dartType);
|
|
1701
|
-
const fieldLines = jsonKey ? `
|
|
1841
|
+
const fieldLines = jsonKey ? `
|
|
1842
|
+
${jsonKey}
|
|
1843
|
+
final ${dartTypeForGetter(dartType)} ${camelName};
|
|
1844
|
+
` : `
|
|
1702
1845
|
final ${dartTypeForGetter(dartType)} ${camelName};
|
|
1703
|
-
` : ` final ${dartTypeForGetter(dartType)} ${camelName};
|
|
1704
1846
|
`;
|
|
1705
1847
|
let newContent = content;
|
|
1706
1848
|
const staticPos = newContent.search(/\n\s+static\s/);
|
|
1707
1849
|
const factoryPos = newContent.search(/\n\s+factory\s+AppConfigModel/);
|
|
1708
1850
|
let insertAnchor = -1;
|
|
1709
|
-
if (staticPos !== -1
|
|
1710
|
-
insertAnchor = Math.min(staticPos, factoryPos);
|
|
1711
|
-
} else if (staticPos !== -1) {
|
|
1851
|
+
if (staticPos !== -1) {
|
|
1712
1852
|
insertAnchor = staticPos;
|
|
1713
1853
|
} else if (factoryPos !== -1) {
|
|
1714
1854
|
insertAnchor = factoryPos;
|
|
@@ -1720,8 +1860,9 @@ function injectAppConfigModelProperty(modelPath, camelName, dartType) {
|
|
|
1720
1860
|
}
|
|
1721
1861
|
newContent = injectConstructorParam(newContent, "AppConfigModel", camelName, dartType);
|
|
1722
1862
|
newContent = injectIntoSuperCall(newContent, camelName);
|
|
1723
|
-
|
|
1724
|
-
console.log(
|
|
1863
|
+
fs11.writeFileSync(modelPath, newContent);
|
|
1864
|
+
console.log(chalk6.green(` Injected into AppConfigModel`));
|
|
1865
|
+
return modelPath;
|
|
1725
1866
|
}
|
|
1726
1867
|
function injectBeforeClassClose(content, className, line) {
|
|
1727
1868
|
const classRegex = new RegExp(`class\\s+${className}\\s*[^{]*\\{`);
|
|
@@ -1893,9 +2034,9 @@ function injectIntoSuperCall(content, camelName) {
|
|
|
1893
2034
|
return content.slice(0, parenEnd) + insertion + content.slice(parenEnd);
|
|
1894
2035
|
}
|
|
1895
2036
|
function findFileRecursive(dir, fileName) {
|
|
1896
|
-
const entries =
|
|
2037
|
+
const entries = fs11.readdirSync(dir, { withFileTypes: true });
|
|
1897
2038
|
for (const entry of entries) {
|
|
1898
|
-
const fullPath =
|
|
2039
|
+
const fullPath = path10.join(dir, entry.name);
|
|
1899
2040
|
if (entry.isDirectory()) {
|
|
1900
2041
|
const result = findFileRecursive(fullPath, fileName);
|
|
1901
2042
|
if (result) return result;
|
|
@@ -1906,29 +2047,29 @@ function findFileRecursive(dir, fileName) {
|
|
|
1906
2047
|
return null;
|
|
1907
2048
|
}
|
|
1908
2049
|
function discoverVendorsModules(monorepoRoot) {
|
|
1909
|
-
const packagesDir =
|
|
1910
|
-
if (!
|
|
2050
|
+
const packagesDir = path10.join(monorepoRoot, "packages");
|
|
2051
|
+
if (!fs11.existsSync(packagesDir)) return [];
|
|
1911
2052
|
const results = [];
|
|
1912
|
-
const packages =
|
|
2053
|
+
const packages = fs11.readdirSync(packagesDir, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
1913
2054
|
for (const pkg2 of packages) {
|
|
1914
|
-
const pkgDir =
|
|
2055
|
+
const pkgDir = path10.join(packagesDir, pkg2.name);
|
|
1915
2056
|
const found = findFileRecursive(pkgDir, "vendors_module.dart");
|
|
1916
2057
|
if (found) results.push(pkg2.name);
|
|
1917
2058
|
}
|
|
1918
2059
|
return results.sort();
|
|
1919
2060
|
}
|
|
1920
2061
|
function findAppConfigFile(monorepoRoot) {
|
|
1921
|
-
const packagesDir =
|
|
1922
|
-
if (!
|
|
2062
|
+
const packagesDir = path10.join(monorepoRoot, "packages");
|
|
2063
|
+
if (!fs11.existsSync(packagesDir)) return null;
|
|
1923
2064
|
return findClassInPackages(packagesDir, "AppConfig");
|
|
1924
2065
|
}
|
|
1925
2066
|
function findAppConfigModelFile(monorepoRoot) {
|
|
1926
|
-
const packagesDir =
|
|
1927
|
-
if (!
|
|
2067
|
+
const packagesDir = path10.join(monorepoRoot, "packages");
|
|
2068
|
+
if (!fs11.existsSync(packagesDir)) return null;
|
|
1928
2069
|
return findClassInPackages(packagesDir, "AppConfigModel");
|
|
1929
2070
|
}
|
|
1930
2071
|
function findClassInPackages(packagesDir, className) {
|
|
1931
|
-
const packages =
|
|
2072
|
+
const packages = fs11.readdirSync(packagesDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => path10.join(packagesDir, d.name));
|
|
1932
2073
|
for (const pkgDir of packages) {
|
|
1933
2074
|
const found = findClassFileRecursive(pkgDir, className);
|
|
1934
2075
|
if (found) return found;
|
|
@@ -1937,14 +2078,14 @@ function findClassInPackages(packagesDir, className) {
|
|
|
1937
2078
|
}
|
|
1938
2079
|
function findClassFileRecursive(dir, className) {
|
|
1939
2080
|
const classRegex = new RegExp(`\\bclass\\s+${className}\\b`);
|
|
1940
|
-
const entries =
|
|
2081
|
+
const entries = fs11.readdirSync(dir, { withFileTypes: true });
|
|
1941
2082
|
for (const entry of entries) {
|
|
1942
|
-
const fullPath =
|
|
2083
|
+
const fullPath = path10.join(dir, entry.name);
|
|
1943
2084
|
if (entry.isDirectory()) {
|
|
1944
2085
|
const result = findClassFileRecursive(fullPath, className);
|
|
1945
2086
|
if (result) return result;
|
|
1946
2087
|
} else if (entry.name.endsWith(".dart") && !entry.name.endsWith(".g.dart")) {
|
|
1947
|
-
const content =
|
|
2088
|
+
const content = fs11.readFileSync(fullPath, "utf8");
|
|
1948
2089
|
if (classRegex.test(content)) {
|
|
1949
2090
|
return fullPath;
|
|
1950
2091
|
}
|
|
@@ -1953,7 +2094,7 @@ function findClassFileRecursive(dir, className) {
|
|
|
1953
2094
|
return null;
|
|
1954
2095
|
}
|
|
1955
2096
|
function findFileInPackages(packagesDir, fileName) {
|
|
1956
|
-
const packages =
|
|
2097
|
+
const packages = fs11.readdirSync(packagesDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => path10.join(packagesDir, d.name));
|
|
1957
2098
|
for (const pkgDir of packages) {
|
|
1958
2099
|
const found = findFileRecursive(pkgDir, fileName);
|
|
1959
2100
|
if (found) return found;
|
|
@@ -1961,9 +2102,9 @@ function findFileInPackages(packagesDir, fileName) {
|
|
|
1961
2102
|
return null;
|
|
1962
2103
|
}
|
|
1963
2104
|
function formatPkgPath(monorepoRoot, absPath) {
|
|
1964
|
-
return
|
|
2105
|
+
return path10.relative(monorepoRoot, absPath);
|
|
1965
2106
|
}
|
|
1966
|
-
var spinner = (msg) => console.log(
|
|
2107
|
+
var spinner = (msg) => console.log(chalk6.cyan(` \u2192 ${msg}`));
|
|
1967
2108
|
async function createEnvVar(options) {
|
|
1968
2109
|
const {
|
|
1969
2110
|
monorepoRoot,
|
|
@@ -1975,37 +2116,42 @@ async function createEnvVar(options) {
|
|
|
1975
2116
|
includeInAppConfig
|
|
1976
2117
|
} = options;
|
|
1977
2118
|
const camelName = screamingSnakeToCamel(variableName);
|
|
1978
|
-
|
|
1979
|
-
|
|
2119
|
+
const modifiedFiles = [];
|
|
2120
|
+
console.log(chalk6.bold(`
|
|
2121
|
+
Adding environment variable: ${chalk6.cyan(variableName)}
|
|
1980
2122
|
`));
|
|
1981
2123
|
spinner("PASO 1: Updating env JSON files");
|
|
1982
2124
|
const jsonValue = typedJsonValue(defaultValue, dartType);
|
|
1983
|
-
const appsDir =
|
|
2125
|
+
const appsDir = path10.join(monorepoRoot, "apps");
|
|
1984
2126
|
for (const app of selectedApps) {
|
|
1985
|
-
const appEnvDir =
|
|
2127
|
+
const appEnvDir = path10.join(appsDir, app, "env");
|
|
1986
2128
|
const files = [
|
|
1987
2129
|
{ name: "example.env.json", isTemplate: true },
|
|
1988
2130
|
{ name: "development.env.json", isTemplate: false },
|
|
1989
2131
|
{ name: "production.env.json", isTemplate: false }
|
|
1990
2132
|
];
|
|
1991
2133
|
for (const { name, isTemplate } of files) {
|
|
1992
|
-
injectIntoJsonFile(
|
|
2134
|
+
const modified = injectIntoJsonFile(path10.join(appEnvDir, name), variableName, jsonValue, isTemplate);
|
|
2135
|
+
if (modified) modifiedFiles.push(modified);
|
|
1993
2136
|
}
|
|
1994
2137
|
}
|
|
1995
2138
|
spinner("PASO 2: Injecting into AppEnvironment");
|
|
1996
|
-
injectAbstractGetter(monorepoRoot, camelName, dartType);
|
|
2139
|
+
const envFile = injectAbstractGetter(monorepoRoot, camelName, dartType);
|
|
2140
|
+
if (envFile) modifiedFiles.push(envFile);
|
|
1997
2141
|
spinner("PASO 3: Injecting into AppBuildEnvironment");
|
|
1998
|
-
injectBuildEnvGetter(monorepoRoot, variableName, camelName, dartType);
|
|
2142
|
+
const buildEnvFile = injectBuildEnvGetter(monorepoRoot, variableName, camelName, dartType);
|
|
2143
|
+
if (buildEnvFile) modifiedFiles.push(buildEnvFile);
|
|
1999
2144
|
if (includeInRemoteConfig) {
|
|
2000
2145
|
spinner("PASO 4: Injecting into VendorsModule RemoteConfig");
|
|
2001
2146
|
for (const pkgName of options.vendorsTargets) {
|
|
2002
|
-
const pkgDir =
|
|
2147
|
+
const pkgDir = path10.join(monorepoRoot, "packages", pkgName);
|
|
2003
2148
|
const vendorsPath = findFileRecursive(pkgDir, "vendors_module.dart");
|
|
2004
2149
|
if (vendorsPath) {
|
|
2005
|
-
console.log(
|
|
2006
|
-
injectIntoRemoteConfigDefaults(vendorsPath, camelName, dartType);
|
|
2150
|
+
console.log(chalk6.cyan(` \u2192 ${pkgName}/vendors_module.dart`));
|
|
2151
|
+
const modified = injectIntoRemoteConfigDefaults(vendorsPath, camelName, dartType);
|
|
2152
|
+
if (modified) modifiedFiles.push(modified);
|
|
2007
2153
|
} else {
|
|
2008
|
-
console.log(
|
|
2154
|
+
console.log(chalk6.yellow(` vendors_module.dart not found in ${pkgName}`));
|
|
2009
2155
|
}
|
|
2010
2156
|
}
|
|
2011
2157
|
}
|
|
@@ -2013,46 +2159,476 @@ Adding environment variable: ${chalk5.cyan(variableName)}
|
|
|
2013
2159
|
spinner("PASO 5: Injecting into AppConfig entity");
|
|
2014
2160
|
const configPath = findAppConfigFile(monorepoRoot);
|
|
2015
2161
|
if (configPath) {
|
|
2016
|
-
injectAppConfigProperty(configPath, camelName, dartType);
|
|
2162
|
+
const modified = injectAppConfigProperty(configPath, camelName, dartType);
|
|
2163
|
+
if (modified) modifiedFiles.push(modified);
|
|
2017
2164
|
} else {
|
|
2018
|
-
console.log(
|
|
2165
|
+
console.log(chalk6.yellow(" app_config.dart not found, skipping AppConfig"));
|
|
2019
2166
|
}
|
|
2020
2167
|
spinner("PASO 6: Injecting into AppConfigModel");
|
|
2021
2168
|
const modelPath = findAppConfigModelFile(monorepoRoot);
|
|
2022
2169
|
if (modelPath) {
|
|
2023
|
-
injectAppConfigModelProperty(modelPath, camelName, dartType);
|
|
2170
|
+
const modified = injectAppConfigModelProperty(modelPath, camelName, dartType);
|
|
2171
|
+
if (modified) modifiedFiles.push(modified);
|
|
2172
|
+
} else {
|
|
2173
|
+
console.log(chalk6.yellow(" app_config_model.dart not found, skipping AppConfigModel"));
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
console.log(chalk6.green("\nEnvironment variable added successfully!"));
|
|
2177
|
+
const dartFiles = modifiedFiles.filter((f) => f.endsWith(".dart"));
|
|
2178
|
+
if (dartFiles.length > 0) {
|
|
2179
|
+
spinner("Running dart format on modified files...");
|
|
2180
|
+
try {
|
|
2181
|
+
const fileList = dartFiles.join(" ");
|
|
2182
|
+
execSync(`dart format ${fileList}`, { cwd: monorepoRoot, stdio: "pipe" });
|
|
2183
|
+
console.log(chalk6.green(` Formatted ${dartFiles.length} file(s)`));
|
|
2184
|
+
} catch {
|
|
2185
|
+
console.log(chalk6.yellow(" dart format failed (you may need to run it manually)"));
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
// src/core/create-package.ts
|
|
2191
|
+
import * as fs12 from "fs";
|
|
2192
|
+
import * as path11 from "path";
|
|
2193
|
+
import { execSync as execSync2 } from "child_process";
|
|
2194
|
+
import chalk7 from "chalk";
|
|
2195
|
+
var MAIN_TESTER_CONTENT = `// ignore_for_file: avoid_print
|
|
2196
|
+
|
|
2197
|
+
import 'dart:io';
|
|
2198
|
+
|
|
2199
|
+
void main() {
|
|
2200
|
+
final currentDir = Directory.current;
|
|
2201
|
+
final testDir = Directory('\${currentDir.path}/test');
|
|
2202
|
+
|
|
2203
|
+
try {
|
|
2204
|
+
final entities = testDir.listSync(recursive: true);
|
|
2205
|
+
final testFiles = <String>[];
|
|
2206
|
+
|
|
2207
|
+
print('Finding test files in \${testDir.path}:');
|
|
2208
|
+
print('----------------------------------------');
|
|
2209
|
+
|
|
2210
|
+
for (final entity in entities) {
|
|
2211
|
+
if (entity is File &&
|
|
2212
|
+
entity.path.endsWith('_test.dart') &&
|
|
2213
|
+
!entity.path.endsWith('/main_test.dart')) {
|
|
2214
|
+
final fullPath = entity.path;
|
|
2215
|
+
final relativePath = fullPath.substring(
|
|
2216
|
+
testDir.path.length + 1,
|
|
2217
|
+
);
|
|
2218
|
+
|
|
2219
|
+
print('Found test file: $relativePath');
|
|
2220
|
+
testFiles.add(relativePath);
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
print('----------------------------------------');
|
|
2225
|
+
print('Total test files found: \${testFiles.length}');
|
|
2226
|
+
|
|
2227
|
+
final mainTestContent = StringBuffer();
|
|
2228
|
+
|
|
2229
|
+
for (final file in testFiles) {
|
|
2230
|
+
final importName = file.replaceAll('/', '_').replaceAll('.dart', '');
|
|
2231
|
+
mainTestContent.writeln("import '$file' as $importName;");
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
mainTestContent.writeln('\\nvoid main() {');
|
|
2235
|
+
|
|
2236
|
+
for (final file in testFiles) {
|
|
2237
|
+
final importName = file.replaceAll('/', '_').replaceAll('.dart', '');
|
|
2238
|
+
mainTestContent.writeln(' $importName.main();');
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
mainTestContent.writeln('}');
|
|
2242
|
+
|
|
2243
|
+
final mainTestFile = File('\${testDir.path}/main_test.dart')
|
|
2244
|
+
..writeAsStringSync(mainTestContent.toString());
|
|
2245
|
+
|
|
2246
|
+
print('\\nCreated file: \${mainTestFile.path}');
|
|
2247
|
+
print('----------------------------------------');
|
|
2248
|
+
} on Exception catch (e) {
|
|
2249
|
+
print('Error: $e');
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
`;
|
|
2253
|
+
function buildPubspecContent(packageName) {
|
|
2254
|
+
const description = `${capitalize(packageName.replace(/_/g, " "))} package`;
|
|
2255
|
+
return `name: ${packageName}
|
|
2256
|
+
description: "${description}"
|
|
2257
|
+
version: 0.0.1
|
|
2258
|
+
publish_to: none
|
|
2259
|
+
|
|
2260
|
+
environment:
|
|
2261
|
+
sdk: ^3.8.1
|
|
2262
|
+
flutter: ">=1.17.0"
|
|
2263
|
+
|
|
2264
|
+
dependencies:
|
|
2265
|
+
core:
|
|
2266
|
+
design_system:
|
|
2267
|
+
flutter:
|
|
2268
|
+
sdk: flutter
|
|
2269
|
+
flutter_bloc: ^9.1.1
|
|
2270
|
+
freezed_annotation: ^3.1.0
|
|
2271
|
+
go_router: ^16.0.0
|
|
2272
|
+
localization:
|
|
2273
|
+
|
|
2274
|
+
dev_dependencies:
|
|
2275
|
+
bloc_test: ^10.0.0
|
|
2276
|
+
build_runner: ^2.5.4
|
|
2277
|
+
flutter_test:
|
|
2278
|
+
sdk: flutter
|
|
2279
|
+
freezed: ^3.2.0
|
|
2280
|
+
mocktail: ^1.0.4
|
|
2281
|
+
very_good_analysis: ^9.0.0
|
|
2282
|
+
|
|
2283
|
+
dependency_overrides:
|
|
2284
|
+
source_gen: ^4.2.0
|
|
2285
|
+
|
|
2286
|
+
flutter:
|
|
2287
|
+
uses-material-design: true
|
|
2288
|
+
`;
|
|
2289
|
+
}
|
|
2290
|
+
function buildGitignoreContent() {
|
|
2291
|
+
return `# Miscellaneous
|
|
2292
|
+
*.class
|
|
2293
|
+
*.log
|
|
2294
|
+
*.pyc
|
|
2295
|
+
*.swp
|
|
2296
|
+
.DS_Store
|
|
2297
|
+
.atom/
|
|
2298
|
+
.buildlog/
|
|
2299
|
+
.history
|
|
2300
|
+
.svn/
|
|
2301
|
+
migrate_working_dir/
|
|
2302
|
+
|
|
2303
|
+
# IntelliJ related
|
|
2304
|
+
*.iml
|
|
2305
|
+
*.ipr
|
|
2306
|
+
*.iws
|
|
2307
|
+
.idea/
|
|
2308
|
+
|
|
2309
|
+
# Flutter/Dart/Pub related
|
|
2310
|
+
/pubspec.lock
|
|
2311
|
+
**/doc/api/
|
|
2312
|
+
.dart_tool/
|
|
2313
|
+
.flutter-plugins
|
|
2314
|
+
.flutter-plugins-dependencies
|
|
2315
|
+
build/
|
|
2316
|
+
|
|
2317
|
+
coverage/
|
|
2318
|
+
|
|
2319
|
+
test/main_test.dart
|
|
2320
|
+
`;
|
|
2321
|
+
}
|
|
2322
|
+
function buildMakefileContent() {
|
|
2323
|
+
return `.PHONY: test
|
|
2324
|
+
test:
|
|
2325
|
+
dart test/main_tester.dart
|
|
2326
|
+
dart format test/main_test.dart
|
|
2327
|
+
dart fix --apply test/main_test.dart
|
|
2328
|
+
flutter test --coverage test/main_test.dart
|
|
2329
|
+
`;
|
|
2330
|
+
}
|
|
2331
|
+
function buildAnalysisOptionsContent() {
|
|
2332
|
+
return `include: ../../analysis_options.yaml
|
|
2333
|
+
`;
|
|
2334
|
+
}
|
|
2335
|
+
function buildBarrelExport(packageName) {
|
|
2336
|
+
return `export 'bloc/bloc.dart';
|
|
2337
|
+
export 'pages/pages.dart';
|
|
2338
|
+
`;
|
|
2339
|
+
}
|
|
2340
|
+
function buildEmptyBarrel() {
|
|
2341
|
+
return "";
|
|
2342
|
+
}
|
|
2343
|
+
function buildGitHubWorkflow(packageName) {
|
|
2344
|
+
return `name: packages/${packageName}
|
|
2345
|
+
|
|
2346
|
+
permissions:
|
|
2347
|
+
contents: read
|
|
2348
|
+
pull-requests: write
|
|
2349
|
+
|
|
2350
|
+
concurrency:
|
|
2351
|
+
group: \${{ github.workflow }}-\${{ github.event.pull_request.number || github.ref }}-${packageName}
|
|
2352
|
+
cancel-in-progress: true
|
|
2353
|
+
|
|
2354
|
+
on:
|
|
2355
|
+
push:
|
|
2356
|
+
branches:
|
|
2357
|
+
- master
|
|
2358
|
+
paths:
|
|
2359
|
+
- "packages/${packageName}/**"
|
|
2360
|
+
- ".github/workflows/${packageName}.yaml"
|
|
2361
|
+
- ".github/workflows/test.yaml"
|
|
2362
|
+
pull_request:
|
|
2363
|
+
branches:
|
|
2364
|
+
- master
|
|
2365
|
+
paths:
|
|
2366
|
+
- "packages/${packageName}/**"
|
|
2367
|
+
- ".github/workflows/${packageName}.yaml"
|
|
2368
|
+
- ".github/workflows/test.yaml"
|
|
2369
|
+
|
|
2370
|
+
jobs:
|
|
2371
|
+
test:
|
|
2372
|
+
uses: ./.github/workflows/test.yaml
|
|
2373
|
+
with:
|
|
2374
|
+
package_name: ${packageName}
|
|
2375
|
+
working_directory: packages/${packageName}
|
|
2376
|
+
`;
|
|
2377
|
+
}
|
|
2378
|
+
function capitalize(s) {
|
|
2379
|
+
return s.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
2380
|
+
}
|
|
2381
|
+
function findWorkspaceFile(monorepoRoot) {
|
|
2382
|
+
const files = fs12.readdirSync(monorepoRoot);
|
|
2383
|
+
return files.find((f) => f.endsWith(".code-workspace")) ?? null;
|
|
2384
|
+
}
|
|
2385
|
+
function updateCodeWorkspace(monorepoRoot, packageName) {
|
|
2386
|
+
const wsFileName = findWorkspaceFile(monorepoRoot);
|
|
2387
|
+
if (!wsFileName) {
|
|
2388
|
+
console.log(chalk7.yellow(" No .code-workspace file found, skipping"));
|
|
2389
|
+
return;
|
|
2390
|
+
}
|
|
2391
|
+
const wsPath = path11.join(monorepoRoot, wsFileName);
|
|
2392
|
+
const content = fs12.readFileSync(wsPath, "utf8");
|
|
2393
|
+
const folderPath = `packages/${packageName}`;
|
|
2394
|
+
if (content.includes(`"path": "${folderPath}"`)) {
|
|
2395
|
+
console.log(chalk7.yellow(` Already in ${wsFileName}`));
|
|
2396
|
+
return;
|
|
2397
|
+
}
|
|
2398
|
+
const folderName = `\u{1F4E6} ${packageName}`;
|
|
2399
|
+
const newBlock = ` {
|
|
2400
|
+
"name": "${folderName}",
|
|
2401
|
+
"path": "${folderPath}"
|
|
2402
|
+
},`;
|
|
2403
|
+
const lines = content.split("\n");
|
|
2404
|
+
const foldersLineIdx = lines.findIndex((l) => l.includes('"folders"'));
|
|
2405
|
+
if (foldersLineIdx === -1) {
|
|
2406
|
+
console.log(chalk7.yellow(' No "folders" key found in workspace, skipping'));
|
|
2407
|
+
return;
|
|
2408
|
+
}
|
|
2409
|
+
let foldersOpenBracket = -1;
|
|
2410
|
+
for (let j = foldersLineIdx; j < lines.length; j++) {
|
|
2411
|
+
if (lines[j].includes("[")) {
|
|
2412
|
+
foldersOpenBracket = j;
|
|
2413
|
+
break;
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
if (foldersOpenBracket === -1) {
|
|
2417
|
+
console.log(chalk7.yellow(" Could not find folders array, skipping"));
|
|
2418
|
+
return;
|
|
2419
|
+
}
|
|
2420
|
+
let bracketCount = 0;
|
|
2421
|
+
let foldersCloseBracket = -1;
|
|
2422
|
+
for (let j = foldersOpenBracket; j < lines.length; j++) {
|
|
2423
|
+
for (const ch of lines[j]) {
|
|
2424
|
+
if (ch === "[") bracketCount++;
|
|
2425
|
+
else if (ch === "]") {
|
|
2426
|
+
bracketCount--;
|
|
2427
|
+
if (bracketCount === 0) {
|
|
2428
|
+
foldersCloseBracket = j;
|
|
2429
|
+
break;
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
if (foldersCloseBracket !== -1) break;
|
|
2434
|
+
}
|
|
2435
|
+
if (foldersCloseBracket === -1) {
|
|
2436
|
+
console.log(chalk7.yellow(" Could not find folders array end, skipping"));
|
|
2437
|
+
return;
|
|
2438
|
+
}
|
|
2439
|
+
let widgetbookBlockStart = -1;
|
|
2440
|
+
let idx = foldersOpenBracket + 1;
|
|
2441
|
+
while (idx <= foldersCloseBracket) {
|
|
2442
|
+
const trimmed = lines[idx].trim();
|
|
2443
|
+
if (trimmed === "{" || trimmed.startsWith("{")) {
|
|
2444
|
+
let braceCount = 0;
|
|
2445
|
+
let blockEnd = -1;
|
|
2446
|
+
for (let j = idx; j <= foldersCloseBracket; j++) {
|
|
2447
|
+
for (const ch of lines[j]) {
|
|
2448
|
+
if (ch === "{") braceCount++;
|
|
2449
|
+
else if (ch === "}") {
|
|
2450
|
+
braceCount--;
|
|
2451
|
+
if (braceCount === 0) {
|
|
2452
|
+
blockEnd = j;
|
|
2453
|
+
break;
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
if (blockEnd !== -1) break;
|
|
2458
|
+
}
|
|
2459
|
+
if (blockEnd !== -1) {
|
|
2460
|
+
const blockText = lines.slice(idx, blockEnd + 1).join("\n");
|
|
2461
|
+
if (blockText.includes("widgetbook")) {
|
|
2462
|
+
widgetbookBlockStart = idx;
|
|
2463
|
+
break;
|
|
2464
|
+
}
|
|
2465
|
+
idx = blockEnd + 1;
|
|
2466
|
+
continue;
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
idx++;
|
|
2470
|
+
}
|
|
2471
|
+
if (widgetbookBlockStart === -1) {
|
|
2472
|
+
console.log(chalk7.yellow(" No widgetbook entry found in workspace, skipping"));
|
|
2473
|
+
return;
|
|
2474
|
+
}
|
|
2475
|
+
lines.splice(widgetbookBlockStart, 0, newBlock);
|
|
2476
|
+
fs12.writeFileSync(wsPath, lines.join("\n"));
|
|
2477
|
+
console.log(chalk7.green(` Updated ${wsFileName}`));
|
|
2478
|
+
}
|
|
2479
|
+
function updateHelixConfig(monorepoRoot, packageName) {
|
|
2480
|
+
const helixPath = path11.join(monorepoRoot, ".helix", "languages.toml");
|
|
2481
|
+
if (!fs12.existsSync(helixPath)) {
|
|
2482
|
+
console.log(chalk7.yellow(" No .helix/languages.toml found, skipping"));
|
|
2483
|
+
return;
|
|
2484
|
+
}
|
|
2485
|
+
let content = fs12.readFileSync(helixPath, "utf8");
|
|
2486
|
+
const entry = `{ path = "packages/${packageName}" }`;
|
|
2487
|
+
if (content.includes(entry)) {
|
|
2488
|
+
console.log(chalk7.yellow(" Already in .helix/languages.toml"));
|
|
2489
|
+
return;
|
|
2490
|
+
}
|
|
2491
|
+
const workspaceLineRegex = /^\s*\{\s*path\s*=\s*"packages\//;
|
|
2492
|
+
const lines = content.split("\n");
|
|
2493
|
+
let lastPackageLineIdx = -1;
|
|
2494
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2495
|
+
if (workspaceLineRegex.test(lines[i])) {
|
|
2496
|
+
lastPackageLineIdx = i;
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
if (lastPackageLineIdx === -1) {
|
|
2500
|
+
const closingBracketIdx = lines.findIndex((l) => l.trim() === "]");
|
|
2501
|
+
if (closingBracketIdx === -1) {
|
|
2502
|
+
console.log(chalk7.yellow(" Could not parse .helix/languages.toml"));
|
|
2503
|
+
return;
|
|
2504
|
+
}
|
|
2505
|
+
lines.splice(closingBracketIdx, 0, ` { path = "packages/${packageName}" },`);
|
|
2506
|
+
} else {
|
|
2507
|
+
const allPackageEntries = [];
|
|
2508
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2509
|
+
const m = lines[i].match(/^\s*\{\s*path\s*=\s*"packages\/([^"]+)"\s*\}/);
|
|
2510
|
+
if (m) {
|
|
2511
|
+
allPackageEntries.push({ lineIdx: i, entry: m[1] });
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
allPackageEntries.push({ lineIdx: -1, entry: packageName });
|
|
2515
|
+
allPackageEntries.sort((a, b) => a.entry.localeCompare(b.entry));
|
|
2516
|
+
const insertIdx = allPackageEntries.findIndex((e) => e.entry === packageName);
|
|
2517
|
+
let targetLine;
|
|
2518
|
+
if (insertIdx === 0) {
|
|
2519
|
+
const firstLine = lines.findIndex((l) => workspaceLineRegex.test(l));
|
|
2520
|
+
targetLine = firstLine;
|
|
2024
2521
|
} else {
|
|
2025
|
-
|
|
2522
|
+
const prevEntry = allPackageEntries[insertIdx - 1];
|
|
2523
|
+
targetLine = prevEntry.lineIdx + 1;
|
|
2026
2524
|
}
|
|
2525
|
+
lines.splice(targetLine, 0, ` { path = "packages/${packageName}" },`);
|
|
2526
|
+
}
|
|
2527
|
+
fs12.writeFileSync(helixPath, lines.join("\n"));
|
|
2528
|
+
console.log(chalk7.green(" Updated .helix/languages.toml"));
|
|
2529
|
+
}
|
|
2530
|
+
async function createPackage(options) {
|
|
2531
|
+
const { monorepoRoot, packageName } = options;
|
|
2532
|
+
const pkgDir = path11.join(monorepoRoot, "packages", packageName);
|
|
2533
|
+
if (fs12.existsSync(pkgDir)) {
|
|
2534
|
+
throw new Error(`packages/${packageName} already exists`);
|
|
2535
|
+
}
|
|
2536
|
+
console.log(chalk7.bold(`
|
|
2537
|
+
Creating package: ${chalk7.cyan(packageName)}
|
|
2538
|
+
`));
|
|
2539
|
+
console.log(chalk7.cyan(" \u2192 Running flutter create..."));
|
|
2540
|
+
execSync2(`flutter create -t package packages/${packageName}`, {
|
|
2541
|
+
cwd: monorepoRoot,
|
|
2542
|
+
stdio: "pipe"
|
|
2543
|
+
});
|
|
2544
|
+
const autoTestFile = path11.join(pkgDir, "test", `${packageName}_test.dart`);
|
|
2545
|
+
if (fs12.existsSync(autoTestFile)) {
|
|
2546
|
+
fs12.unlinkSync(autoTestFile);
|
|
2547
|
+
}
|
|
2548
|
+
console.log(chalk7.cyan(" \u2192 Customizing package files..."));
|
|
2549
|
+
fs12.writeFileSync(
|
|
2550
|
+
path11.join(pkgDir, "pubspec.yaml"),
|
|
2551
|
+
buildPubspecContent(packageName)
|
|
2552
|
+
);
|
|
2553
|
+
fs12.writeFileSync(
|
|
2554
|
+
path11.join(pkgDir, ".gitignore"),
|
|
2555
|
+
buildGitignoreContent()
|
|
2556
|
+
);
|
|
2557
|
+
fs12.writeFileSync(
|
|
2558
|
+
path11.join(pkgDir, "analysis_options.yaml"),
|
|
2559
|
+
buildAnalysisOptionsContent()
|
|
2560
|
+
);
|
|
2561
|
+
fs12.writeFileSync(
|
|
2562
|
+
path11.join(pkgDir, "Makefile"),
|
|
2563
|
+
buildMakefileContent()
|
|
2564
|
+
);
|
|
2565
|
+
const libDir = path11.join(pkgDir, "lib");
|
|
2566
|
+
fs12.writeFileSync(
|
|
2567
|
+
path11.join(libDir, `${packageName}.dart`),
|
|
2568
|
+
buildBarrelExport(packageName)
|
|
2569
|
+
);
|
|
2570
|
+
fs12.mkdirSync(path11.join(libDir, "bloc"), { recursive: true });
|
|
2571
|
+
fs12.writeFileSync(
|
|
2572
|
+
path11.join(libDir, "bloc", "bloc.dart"),
|
|
2573
|
+
buildEmptyBarrel()
|
|
2574
|
+
);
|
|
2575
|
+
fs12.mkdirSync(path11.join(libDir, "pages"), { recursive: true });
|
|
2576
|
+
fs12.writeFileSync(
|
|
2577
|
+
path11.join(libDir, "pages", "pages.dart"),
|
|
2578
|
+
buildEmptyBarrel()
|
|
2579
|
+
);
|
|
2580
|
+
const testDir = path11.join(pkgDir, "test");
|
|
2581
|
+
if (!fs12.existsSync(testDir)) {
|
|
2582
|
+
fs12.mkdirSync(testDir, { recursive: true });
|
|
2583
|
+
}
|
|
2584
|
+
fs12.writeFileSync(
|
|
2585
|
+
path11.join(testDir, "main_tester.dart"),
|
|
2586
|
+
MAIN_TESTER_CONTENT
|
|
2587
|
+
);
|
|
2588
|
+
console.log(chalk7.cyan(" \u2192 Updating workspace configuration..."));
|
|
2589
|
+
updateCodeWorkspace(monorepoRoot, packageName);
|
|
2590
|
+
updateHelixConfig(monorepoRoot, packageName);
|
|
2591
|
+
console.log(chalk7.cyan(" \u2192 Creating GitHub Actions workflow..."));
|
|
2592
|
+
const workflowsDir = path11.join(monorepoRoot, ".github", "workflows");
|
|
2593
|
+
if (fs12.existsSync(workflowsDir)) {
|
|
2594
|
+
fs12.writeFileSync(
|
|
2595
|
+
path11.join(workflowsDir, `${packageName}.yaml`),
|
|
2596
|
+
buildGitHubWorkflow(packageName)
|
|
2597
|
+
);
|
|
2598
|
+
console.log(chalk7.green(` Created .github/workflows/${packageName}.yaml`));
|
|
2599
|
+
} else {
|
|
2600
|
+
console.log(chalk7.yellow(" No .github/workflows/ directory found, skipping CI workflow"));
|
|
2027
2601
|
}
|
|
2028
|
-
console.log(
|
|
2029
|
-
spinner("Running melos format...");
|
|
2602
|
+
console.log(chalk7.cyan(" \u2192 Running melos bootstrap..."));
|
|
2030
2603
|
try {
|
|
2031
|
-
|
|
2032
|
-
console.log(
|
|
2604
|
+
execSync2("melos bootstrap", { cwd: monorepoRoot, stdio: "pipe" });
|
|
2605
|
+
console.log(chalk7.green(" melos bootstrap completed"));
|
|
2033
2606
|
} catch {
|
|
2034
|
-
console.log(
|
|
2607
|
+
console.log(chalk7.yellow(" melos bootstrap failed (you may need to run it manually)"));
|
|
2035
2608
|
}
|
|
2609
|
+
console.log(chalk7.green(`
|
|
2610
|
+
Package "${packageName}" created successfully!`));
|
|
2611
|
+
console.log(chalk7.gray(` ${pkgDir}`));
|
|
2036
2612
|
}
|
|
2037
2613
|
|
|
2038
2614
|
// src/core/docs-serve.ts
|
|
2039
2615
|
import { spawn as spawn2 } from "child_process";
|
|
2040
|
-
import * as
|
|
2041
|
-
import * as
|
|
2042
|
-
import
|
|
2616
|
+
import * as fs13 from "fs";
|
|
2617
|
+
import * as path12 from "path";
|
|
2618
|
+
import chalk8 from "chalk";
|
|
2043
2619
|
function detectBookDir(startDir) {
|
|
2044
2620
|
const root = findMonorepoRoot(startDir) ?? startDir;
|
|
2045
|
-
const bookDir =
|
|
2046
|
-
if (
|
|
2621
|
+
const bookDir = path12.join(root, "book");
|
|
2622
|
+
if (fs13.existsSync(bookDir) && (fs13.existsSync(path12.join(bookDir, "docusaurus.config")) || fs13.existsSync(path12.join(bookDir, "docusaurus.config.ts")) || fs13.existsSync(path12.join(bookDir, "docusaurus.config.mjs")))) {
|
|
2047
2623
|
return bookDir;
|
|
2048
2624
|
}
|
|
2049
2625
|
return void 0;
|
|
2050
2626
|
}
|
|
2051
2627
|
function serveBook(bookDir) {
|
|
2052
|
-
return new Promise((
|
|
2053
|
-
const nodeModules =
|
|
2054
|
-
if (!
|
|
2055
|
-
console.log(
|
|
2628
|
+
return new Promise((resolve5, reject) => {
|
|
2629
|
+
const nodeModules = path12.join(bookDir, "node_modules");
|
|
2630
|
+
if (!fs13.existsSync(nodeModules)) {
|
|
2631
|
+
console.log(chalk8.cyan("Installing book dependencies..."));
|
|
2056
2632
|
const install = spawn2("npm", ["install"], {
|
|
2057
2633
|
cwd: bookDir,
|
|
2058
2634
|
stdio: "inherit"
|
|
@@ -2062,13 +2638,13 @@ function serveBook(bookDir) {
|
|
|
2062
2638
|
reject(new Error(`npm install failed with code ${code}`));
|
|
2063
2639
|
return;
|
|
2064
2640
|
}
|
|
2065
|
-
startDevServer(bookDir,
|
|
2641
|
+
startDevServer(bookDir, resolve5);
|
|
2066
2642
|
});
|
|
2067
2643
|
install.on("error", (err) => {
|
|
2068
2644
|
reject(err);
|
|
2069
2645
|
});
|
|
2070
2646
|
} else {
|
|
2071
|
-
startDevServer(bookDir,
|
|
2647
|
+
startDevServer(bookDir, resolve5);
|
|
2072
2648
|
}
|
|
2073
2649
|
});
|
|
2074
2650
|
}
|
|
@@ -2086,16 +2662,16 @@ function startDevServer(bookDir, done) {
|
|
|
2086
2662
|
}
|
|
2087
2663
|
|
|
2088
2664
|
// src/core/docs-commands.ts
|
|
2089
|
-
import * as
|
|
2090
|
-
import * as
|
|
2091
|
-
import
|
|
2665
|
+
import * as fs14 from "fs";
|
|
2666
|
+
import * as path13 from "path";
|
|
2667
|
+
import chalk9 from "chalk";
|
|
2092
2668
|
import YAML2 from "yaml";
|
|
2093
2669
|
function parseMakefile(filePath) {
|
|
2094
|
-
if (!
|
|
2095
|
-
const content =
|
|
2670
|
+
if (!fs14.existsSync(filePath)) return [];
|
|
2671
|
+
const content = fs14.readFileSync(filePath, "utf8");
|
|
2096
2672
|
const lines = content.split("\n");
|
|
2097
2673
|
const commands = [];
|
|
2098
|
-
const source =
|
|
2674
|
+
const source = path13.basename(filePath);
|
|
2099
2675
|
let pendingComments = [];
|
|
2100
2676
|
for (let i = 0; i < lines.length; i++) {
|
|
2101
2677
|
const line = lines[i];
|
|
@@ -2125,13 +2701,13 @@ function parseMakefile(filePath) {
|
|
|
2125
2701
|
return commands;
|
|
2126
2702
|
}
|
|
2127
2703
|
function parseMelosScripts(filePath) {
|
|
2128
|
-
if (!
|
|
2129
|
-
const content =
|
|
2704
|
+
if (!fs14.existsSync(filePath)) return [];
|
|
2705
|
+
const content = fs14.readFileSync(filePath, "utf8");
|
|
2130
2706
|
const parsed = YAML2.parse(content);
|
|
2131
2707
|
const scripts = parsed?.scripts;
|
|
2132
2708
|
if (!scripts || typeof scripts !== "object") return [];
|
|
2133
2709
|
const commands = [];
|
|
2134
|
-
const source =
|
|
2710
|
+
const source = path13.basename(filePath);
|
|
2135
2711
|
for (const [name, value] of Object.entries(scripts)) {
|
|
2136
2712
|
let description = "";
|
|
2137
2713
|
let command = "";
|
|
@@ -2153,17 +2729,17 @@ function parseMelosScripts(filePath) {
|
|
|
2153
2729
|
function discoverCommands(startDir) {
|
|
2154
2730
|
const root = findMonorepoRoot(startDir) ?? startDir;
|
|
2155
2731
|
const commands = [];
|
|
2156
|
-
const rootMakefile =
|
|
2732
|
+
const rootMakefile = path13.join(root, "Makefile");
|
|
2157
2733
|
commands.push(...parseMakefile(rootMakefile));
|
|
2158
|
-
const melosFile =
|
|
2734
|
+
const melosFile = path13.join(root, "melos.yaml");
|
|
2159
2735
|
commands.push(...parseMelosScripts(melosFile));
|
|
2160
|
-
const bookMakefile =
|
|
2736
|
+
const bookMakefile = path13.join(root, "book", "Makefile");
|
|
2161
2737
|
commands.push(...parseMakefile(bookMakefile));
|
|
2162
2738
|
return commands;
|
|
2163
2739
|
}
|
|
2164
2740
|
function displayCommands(commands) {
|
|
2165
2741
|
if (commands.length === 0) {
|
|
2166
|
-
console.log(
|
|
2742
|
+
console.log(chalk9.yellow("No commands found."));
|
|
2167
2743
|
return;
|
|
2168
2744
|
}
|
|
2169
2745
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -2173,13 +2749,13 @@ function displayCommands(commands) {
|
|
|
2173
2749
|
groups.get(key).push(cmd);
|
|
2174
2750
|
}
|
|
2175
2751
|
for (const [group, entries] of groups) {
|
|
2176
|
-
console.log(
|
|
2752
|
+
console.log(chalk9.bold(chalk9.cyan(`
|
|
2177
2753
|
${group}`)));
|
|
2178
|
-
console.log(
|
|
2754
|
+
console.log(chalk9.cyan("\u2500".repeat(group.length)));
|
|
2179
2755
|
const maxNameLen = Math.max(...entries.map((e) => e.name.length));
|
|
2180
2756
|
for (const entry of entries) {
|
|
2181
|
-
const name =
|
|
2182
|
-
const desc = entry.description ?
|
|
2757
|
+
const name = chalk9.white(entry.name.padEnd(maxNameLen + 2));
|
|
2758
|
+
const desc = entry.description ? chalk9.gray(entry.description) : chalk9.dim("(no description)");
|
|
2183
2759
|
console.log(` ${name} ${desc}`);
|
|
2184
2760
|
}
|
|
2185
2761
|
}
|
|
@@ -2187,9 +2763,9 @@ ${group}`)));
|
|
|
2187
2763
|
}
|
|
2188
2764
|
|
|
2189
2765
|
// src/core/docs-architecture.ts
|
|
2190
|
-
import * as
|
|
2191
|
-
import * as
|
|
2192
|
-
import
|
|
2766
|
+
import * as fs15 from "fs";
|
|
2767
|
+
import * as path14 from "path";
|
|
2768
|
+
import chalk10 from "chalk";
|
|
2193
2769
|
import YAML3 from "yaml";
|
|
2194
2770
|
var KEY_DEPS = [
|
|
2195
2771
|
"freezed",
|
|
@@ -2207,18 +2783,18 @@ var KEY_DEPS = [
|
|
|
2207
2783
|
function discoverArchitecture(startDir) {
|
|
2208
2784
|
const root = findMonorepoRoot(startDir);
|
|
2209
2785
|
if (!root) return null;
|
|
2210
|
-
const melosPath =
|
|
2211
|
-
if (!
|
|
2212
|
-
const melosContent =
|
|
2786
|
+
const melosPath = path14.join(root, "melos.yaml");
|
|
2787
|
+
if (!fs15.existsSync(melosPath)) return null;
|
|
2788
|
+
const melosContent = fs15.readFileSync(melosPath, "utf8");
|
|
2213
2789
|
const melos = YAML3.parse(melosContent);
|
|
2214
|
-
const projectName = melos?.name ??
|
|
2790
|
+
const projectName = melos?.name ?? path14.basename(root);
|
|
2215
2791
|
const apps = [];
|
|
2216
|
-
const appsDir =
|
|
2217
|
-
if (
|
|
2218
|
-
for (const entry of
|
|
2792
|
+
const appsDir = path14.join(root, "apps");
|
|
2793
|
+
if (fs15.existsSync(appsDir)) {
|
|
2794
|
+
for (const entry of fs15.readdirSync(appsDir, { withFileTypes: true })) {
|
|
2219
2795
|
if (!entry.isDirectory()) continue;
|
|
2220
|
-
const candidate =
|
|
2221
|
-
if (
|
|
2796
|
+
const candidate = path14.join(appsDir, entry.name);
|
|
2797
|
+
if (fs15.existsSync(path14.join(candidate, "pubspec.yaml"))) {
|
|
2222
2798
|
apps.push({ name: entry.name, dir: candidate });
|
|
2223
2799
|
}
|
|
2224
2800
|
}
|
|
@@ -2226,14 +2802,14 @@ function discoverArchitecture(startDir) {
|
|
|
2226
2802
|
const packages = [];
|
|
2227
2803
|
const packageBases = ["packages", "packages/features"];
|
|
2228
2804
|
for (const base of packageBases) {
|
|
2229
|
-
const baseDir =
|
|
2230
|
-
if (!
|
|
2231
|
-
for (const entry of
|
|
2805
|
+
const baseDir = path14.join(root, base);
|
|
2806
|
+
if (!fs15.existsSync(baseDir)) continue;
|
|
2807
|
+
for (const entry of fs15.readdirSync(baseDir, { withFileTypes: true })) {
|
|
2232
2808
|
if (!entry.isDirectory()) continue;
|
|
2233
|
-
const candidate =
|
|
2234
|
-
const pubspecPath =
|
|
2235
|
-
if (!
|
|
2236
|
-
const content =
|
|
2809
|
+
const candidate = path14.join(baseDir, entry.name);
|
|
2810
|
+
const pubspecPath = path14.join(candidate, "pubspec.yaml");
|
|
2811
|
+
if (!fs15.existsSync(pubspecPath)) continue;
|
|
2812
|
+
const content = fs15.readFileSync(pubspecPath, "utf8");
|
|
2237
2813
|
const pubspec = YAML3.parse(content);
|
|
2238
2814
|
const deps = {
|
|
2239
2815
|
...pubspec?.dependencies,
|
|
@@ -2268,30 +2844,30 @@ function displayArchitecture(info) {
|
|
|
2268
2844
|
const L = "\u2514\u2500\u2500 ";
|
|
2269
2845
|
const I = "\u2502 ";
|
|
2270
2846
|
const S = " ";
|
|
2271
|
-
console.log(
|
|
2847
|
+
console.log(chalk10.bold(chalk10.cyan(info.projectName)) + chalk10.gray(` (${info.root})`));
|
|
2272
2848
|
console.log(`${I}`);
|
|
2273
2849
|
if (info.apps.length > 0) {
|
|
2274
|
-
console.log(
|
|
2850
|
+
console.log(chalk10.white(`${T}apps/`));
|
|
2275
2851
|
for (let i = 0; i < info.apps.length; i++) {
|
|
2276
2852
|
const app = info.apps[i];
|
|
2277
2853
|
const prefix = i === info.apps.length - 1 ? `${I}${S}${L}` : `${I}${S}${T}`;
|
|
2278
|
-
console.log(`${prefix}${
|
|
2854
|
+
console.log(`${prefix}${chalk10.green(app.name)}`);
|
|
2279
2855
|
}
|
|
2280
2856
|
}
|
|
2281
2857
|
if (info.packages.length > 0) {
|
|
2282
2858
|
const rootPkgs = info.packages.filter(
|
|
2283
|
-
(p) => !p.dir.includes(`${
|
|
2859
|
+
(p) => !p.dir.includes(`${path14.sep}features${path14.sep}`)
|
|
2284
2860
|
);
|
|
2285
2861
|
const featurePkgs = info.packages.filter(
|
|
2286
|
-
(p) => p.dir.includes(`${
|
|
2862
|
+
(p) => p.dir.includes(`${path14.sep}features${path14.sep}`)
|
|
2287
2863
|
);
|
|
2288
|
-
console.log(
|
|
2864
|
+
console.log(chalk10.white(`${T}packages/`));
|
|
2289
2865
|
for (let i = 0; i < rootPkgs.length; i++) {
|
|
2290
2866
|
const pkg2 = rootPkgs[i];
|
|
2291
2867
|
const isLast = i === rootPkgs.length - 1 && featurePkgs.length === 0;
|
|
2292
2868
|
const prefix = isLast ? `${I}${S}${L}` : `${I}${S}${T}`;
|
|
2293
|
-
const deps = pkg2.keyDeps.length > 0 ?
|
|
2294
|
-
console.log(`${prefix}${
|
|
2869
|
+
const deps = pkg2.keyDeps.length > 0 ? chalk10.dim(` [${pkg2.keyDeps.join(", ")}]`) : "";
|
|
2870
|
+
console.log(`${prefix}${chalk10.yellow(pkg2.name)}${deps}`);
|
|
2295
2871
|
}
|
|
2296
2872
|
if (featurePkgs.length > 0) {
|
|
2297
2873
|
console.log(`${I}${S}${T}features/`);
|
|
@@ -2299,33 +2875,33 @@ function displayArchitecture(info) {
|
|
|
2299
2875
|
const pkg2 = featurePkgs[i];
|
|
2300
2876
|
const isLast = i === featurePkgs.length - 1;
|
|
2301
2877
|
const prefix = isLast ? `${I}${S}${S}${L}` : `${I}${S}${S}${T}`;
|
|
2302
|
-
const deps = pkg2.keyDeps.length > 0 ?
|
|
2303
|
-
console.log(`${prefix}${
|
|
2878
|
+
const deps = pkg2.keyDeps.length > 0 ? chalk10.dim(` [${pkg2.keyDeps.join(", ")}]`) : "";
|
|
2879
|
+
console.log(`${prefix}${chalk10.yellow(pkg2.name)}${deps}`);
|
|
2304
2880
|
}
|
|
2305
2881
|
}
|
|
2306
2882
|
}
|
|
2307
2883
|
if (info.hasDesignSystem) {
|
|
2308
|
-
const tiers = info.designSystemTiers.length > 0 ?
|
|
2309
|
-
console.log(`${T}${
|
|
2884
|
+
const tiers = info.designSystemTiers.length > 0 ? chalk10.dim(` (${info.designSystemTiers.join(", ")})`) : "";
|
|
2885
|
+
console.log(`${T}${chalk10.magenta("design_system")}${tiers}`);
|
|
2310
2886
|
}
|
|
2311
|
-
const bookDir =
|
|
2312
|
-
if (
|
|
2313
|
-
console.log(`${T}${
|
|
2887
|
+
const bookDir = path14.join(info.root, "book");
|
|
2888
|
+
if (fs15.existsSync(bookDir)) {
|
|
2889
|
+
console.log(`${T}${chalk10.blue("book/")}` + chalk10.dim(" (Docusaurus)"));
|
|
2314
2890
|
}
|
|
2315
|
-
if (
|
|
2316
|
-
console.log(`${L}${
|
|
2891
|
+
if (fs15.existsSync(path14.join(info.root, "melos.yaml"))) {
|
|
2892
|
+
console.log(`${L}${chalk10.gray("melos.yaml")}`);
|
|
2317
2893
|
}
|
|
2318
2894
|
console.log();
|
|
2319
2895
|
}
|
|
2320
2896
|
|
|
2321
2897
|
// src/interactive.ts
|
|
2322
|
-
var
|
|
2898
|
+
var SNAKE_CASE_REGEX5 = /^[a-z][a-z0-9_]*$/;
|
|
2323
2899
|
async function resolveProject() {
|
|
2324
2900
|
const s = clack.spinner();
|
|
2325
2901
|
s.start("Analyzing current directory...");
|
|
2326
2902
|
const cwdProject = analyzeProject(process.cwd());
|
|
2327
2903
|
if (cwdProject && (cwdProject.hasFreezed || cwdProject.hasBloc)) {
|
|
2328
|
-
s.stop(`Found ${
|
|
2904
|
+
s.stop(`Found ${chalk11.green(cwdProject.projectName)}`);
|
|
2329
2905
|
return cwdProject;
|
|
2330
2906
|
}
|
|
2331
2907
|
s.message("Looking for Melos monorepo...");
|
|
@@ -2338,8 +2914,8 @@ async function resolveProject() {
|
|
|
2338
2914
|
}
|
|
2339
2915
|
}
|
|
2340
2916
|
s.message("Scanning for Flutter projects...");
|
|
2341
|
-
const homeDev =
|
|
2342
|
-
if (
|
|
2917
|
+
const homeDev = path15.join(os.homedir(), "Development");
|
|
2918
|
+
if (fs16.existsSync(homeDev)) {
|
|
2343
2919
|
const projects = discoverProjects(homeDev, 2);
|
|
2344
2920
|
if (projects.length > 0) {
|
|
2345
2921
|
s.stop(`Found ${projects.length} Flutter project(s)`);
|
|
@@ -2347,12 +2923,12 @@ async function resolveProject() {
|
|
|
2347
2923
|
}
|
|
2348
2924
|
}
|
|
2349
2925
|
s.stop("No Flutter projects found");
|
|
2350
|
-
clack.outro(
|
|
2926
|
+
clack.outro(chalk11.red("Could not find any Flutter project with freezed or flutter_bloc."));
|
|
2351
2927
|
return null;
|
|
2352
2928
|
}
|
|
2353
2929
|
async function selectPackage(projects) {
|
|
2354
2930
|
if (projects.length === 1) {
|
|
2355
|
-
clack.log.info(`Using ${
|
|
2931
|
+
clack.log.info(`Using ${chalk11.green(projects[0].projectName)}`);
|
|
2356
2932
|
return projects[0];
|
|
2357
2933
|
}
|
|
2358
2934
|
const selected = await clack.select({
|
|
@@ -2360,7 +2936,7 @@ async function selectPackage(projects) {
|
|
|
2360
2936
|
options: projects.map((p) => ({
|
|
2361
2937
|
value: p,
|
|
2362
2938
|
label: p.projectName,
|
|
2363
|
-
hint:
|
|
2939
|
+
hint: path15.relative(os.homedir(), p.projectRoot)
|
|
2364
2940
|
}))
|
|
2365
2941
|
});
|
|
2366
2942
|
if (clack.isCancel(selected)) {
|
|
@@ -2375,7 +2951,7 @@ async function blocFlow(project) {
|
|
|
2375
2951
|
placeholder: "e.g. user_login",
|
|
2376
2952
|
validate: (value) => {
|
|
2377
2953
|
if (!value.trim()) return "Name is required";
|
|
2378
|
-
if (!
|
|
2954
|
+
if (!SNAKE_CASE_REGEX5.test(value))
|
|
2379
2955
|
return "Must be snake_case (lowercase, digits, underscores)";
|
|
2380
2956
|
}
|
|
2381
2957
|
});
|
|
@@ -2408,13 +2984,13 @@ async function blocFlow(project) {
|
|
|
2408
2984
|
clack.cancel("Cancelled");
|
|
2409
2985
|
return;
|
|
2410
2986
|
}
|
|
2411
|
-
targetDir =
|
|
2987
|
+
targetDir = path15.resolve(customPath);
|
|
2412
2988
|
} else {
|
|
2413
|
-
targetDir =
|
|
2989
|
+
targetDir = path15.join(project.projectRoot, "lib", "features", feature);
|
|
2414
2990
|
}
|
|
2415
|
-
} else if (
|
|
2416
|
-
targetDir =
|
|
2417
|
-
clack.log.info(`Target: ${
|
|
2991
|
+
} else if (fs16.existsSync(path15.join(project.projectRoot, "lib", "bloc"))) {
|
|
2992
|
+
targetDir = path15.join(project.projectRoot, "lib", "bloc");
|
|
2993
|
+
clack.log.info(`Target: ${chalk11.cyan("lib/bloc/")}`);
|
|
2418
2994
|
} else {
|
|
2419
2995
|
clack.note(
|
|
2420
2996
|
"No lib/features/ directory found. Provide a target path manually.",
|
|
@@ -2431,7 +3007,7 @@ async function blocFlow(project) {
|
|
|
2431
3007
|
clack.cancel("Cancelled");
|
|
2432
3008
|
return;
|
|
2433
3009
|
}
|
|
2434
|
-
targetDir =
|
|
3010
|
+
targetDir = path15.resolve(customPath);
|
|
2435
3011
|
}
|
|
2436
3012
|
const defaultRun = project.hasBuildRunner;
|
|
2437
3013
|
const runBuildRunner2 = await clack.confirm({
|
|
@@ -2450,10 +3026,10 @@ async function blocFlow(project) {
|
|
|
2450
3026
|
buildRunner: runBuildRunner2
|
|
2451
3027
|
});
|
|
2452
3028
|
genSpinner.stop("BLoC generated");
|
|
2453
|
-
clack.outro(
|
|
3029
|
+
clack.outro(chalk11.green("Done!"));
|
|
2454
3030
|
} catch (error) {
|
|
2455
3031
|
genSpinner.stop("Failed");
|
|
2456
|
-
clack.outro(
|
|
3032
|
+
clack.outro(chalk11.red(`Error: ${error}`));
|
|
2457
3033
|
}
|
|
2458
3034
|
}
|
|
2459
3035
|
async function widgetFlow() {
|
|
@@ -2461,14 +3037,14 @@ async function widgetFlow() {
|
|
|
2461
3037
|
const ds = detectDesignSystem(projectRoot);
|
|
2462
3038
|
if (!ds) {
|
|
2463
3039
|
clack.outro(
|
|
2464
|
-
|
|
3040
|
+
chalk11.red(
|
|
2465
3041
|
"No design system detected. Run this command from a project with wl_design_system/ directory."
|
|
2466
3042
|
)
|
|
2467
3043
|
);
|
|
2468
3044
|
return;
|
|
2469
3045
|
}
|
|
2470
3046
|
clack.log.info(
|
|
2471
|
-
`Design system: ${
|
|
3047
|
+
`Design system: ${chalk11.cyan(path15.relative(projectRoot, ds.componentsDir))}`
|
|
2472
3048
|
);
|
|
2473
3049
|
const name = await clack.text({
|
|
2474
3050
|
message: "Widget name (snake_case, without wl_ prefix)",
|
|
@@ -2476,7 +3052,7 @@ async function widgetFlow() {
|
|
|
2476
3052
|
validate: (value) => {
|
|
2477
3053
|
if (!value.trim()) return "Name is required";
|
|
2478
3054
|
const clean = value.startsWith("wl_") ? value.slice(3) : value;
|
|
2479
|
-
if (!
|
|
3055
|
+
if (!SNAKE_CASE_REGEX5.test(clean))
|
|
2480
3056
|
return "Must be snake_case (lowercase, digits, underscores)";
|
|
2481
3057
|
}
|
|
2482
3058
|
});
|
|
@@ -2533,17 +3109,17 @@ async function widgetFlow() {
|
|
|
2533
3109
|
genSpinner.stop(`Use-case skipped: ${e}`);
|
|
2534
3110
|
}
|
|
2535
3111
|
}
|
|
2536
|
-
clack.outro(
|
|
3112
|
+
clack.outro(chalk11.green("Done!"));
|
|
2537
3113
|
} catch (error) {
|
|
2538
3114
|
genSpinner.stop("Failed");
|
|
2539
|
-
clack.outro(
|
|
3115
|
+
clack.outro(chalk11.red(`Error: ${error}`));
|
|
2540
3116
|
}
|
|
2541
3117
|
}
|
|
2542
3118
|
async function useCaseFlow() {
|
|
2543
3119
|
const ds = detectDesignSystem(process.cwd());
|
|
2544
3120
|
if (!ds) {
|
|
2545
3121
|
clack.outro(
|
|
2546
|
-
|
|
3122
|
+
chalk11.red(
|
|
2547
3123
|
"No design system detected. Ensure wl_design_system/ directory exists."
|
|
2548
3124
|
)
|
|
2549
3125
|
);
|
|
@@ -2551,7 +3127,7 @@ async function useCaseFlow() {
|
|
|
2551
3127
|
}
|
|
2552
3128
|
if (!ds.widgetbookDir) {
|
|
2553
3129
|
clack.outro(
|
|
2554
|
-
|
|
3130
|
+
chalk11.red(
|
|
2555
3131
|
"No widgetbook package detected. Ensure apps/widgetbook/ exists."
|
|
2556
3132
|
)
|
|
2557
3133
|
);
|
|
@@ -2563,7 +3139,7 @@ async function useCaseFlow() {
|
|
|
2563
3139
|
validate: (value) => {
|
|
2564
3140
|
if (!value.trim()) return "Name is required";
|
|
2565
3141
|
const clean = value.startsWith("wl_") ? value.slice(3) : value;
|
|
2566
|
-
if (!
|
|
3142
|
+
if (!SNAKE_CASE_REGEX5.test(clean))
|
|
2567
3143
|
return "Must be snake_case (lowercase, digits, underscores)";
|
|
2568
3144
|
}
|
|
2569
3145
|
});
|
|
@@ -2600,18 +3176,123 @@ async function useCaseFlow() {
|
|
|
2600
3176
|
buildRunner: runBuildRunner2
|
|
2601
3177
|
});
|
|
2602
3178
|
genSpinner.stop("Use-case generated");
|
|
2603
|
-
clack.outro(
|
|
3179
|
+
clack.outro(chalk11.green("Done!"));
|
|
2604
3180
|
} catch (error) {
|
|
2605
3181
|
genSpinner.stop("Failed");
|
|
2606
|
-
clack.outro(
|
|
3182
|
+
clack.outro(chalk11.red(`Error: ${error}`));
|
|
2607
3183
|
}
|
|
2608
3184
|
}
|
|
3185
|
+
function discoverMonorepoPackages(monorepoRoot) {
|
|
3186
|
+
const results = [];
|
|
3187
|
+
const packageBases = ["packages", "packages/features"];
|
|
3188
|
+
for (const base of packageBases) {
|
|
3189
|
+
const baseDir = path15.join(monorepoRoot, base);
|
|
3190
|
+
if (!fs16.existsSync(baseDir)) continue;
|
|
3191
|
+
const entries = fs16.readdirSync(baseDir, { withFileTypes: true });
|
|
3192
|
+
for (const entry of entries) {
|
|
3193
|
+
if (!entry.isDirectory()) continue;
|
|
3194
|
+
const pkgDir = path15.join(baseDir, entry.name);
|
|
3195
|
+
if (fs16.existsSync(path15.join(pkgDir, "pubspec.yaml"))) {
|
|
3196
|
+
results.push({ name: entry.name, pagesDir: path15.join(pkgDir, "lib", "pages") });
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
return results.sort((a, b) => a.name.localeCompare(b.name));
|
|
3201
|
+
}
|
|
3202
|
+
async function pageFlow(initialName, initialPath) {
|
|
3203
|
+
const name = initialName ?? await clack.text({
|
|
3204
|
+
message: "Page name (snake_case)",
|
|
3205
|
+
placeholder: "e.g. profile, order_detail",
|
|
3206
|
+
validate: (value) => {
|
|
3207
|
+
if (!value.trim()) return "Name is required";
|
|
3208
|
+
if (!SNAKE_CASE_REGEX5.test(value))
|
|
3209
|
+
return "Must be snake_case (lowercase, digits, underscores)";
|
|
3210
|
+
}
|
|
3211
|
+
});
|
|
3212
|
+
if (clack.isCancel(name)) {
|
|
3213
|
+
clack.cancel("Cancelled");
|
|
3214
|
+
return;
|
|
3215
|
+
}
|
|
3216
|
+
let pagesPath = initialPath;
|
|
3217
|
+
if (!pagesPath) {
|
|
3218
|
+
const monorepoRoot = findMonorepoRoot(process.cwd());
|
|
3219
|
+
if (monorepoRoot) {
|
|
3220
|
+
const packages = discoverMonorepoPackages(monorepoRoot);
|
|
3221
|
+
if (packages.length > 0) {
|
|
3222
|
+
const selected = await clack.select({
|
|
3223
|
+
message: "Select package where the page will be created",
|
|
3224
|
+
options: [
|
|
3225
|
+
...packages.map((p) => ({
|
|
3226
|
+
value: p.pagesDir,
|
|
3227
|
+
label: p.name,
|
|
3228
|
+
hint: path15.relative(monorepoRoot, p.pagesDir)
|
|
3229
|
+
})),
|
|
3230
|
+
{ value: "__custom__", label: "Custom path..." }
|
|
3231
|
+
]
|
|
3232
|
+
});
|
|
3233
|
+
if (clack.isCancel(selected)) {
|
|
3234
|
+
clack.cancel("Cancelled");
|
|
3235
|
+
return;
|
|
3236
|
+
}
|
|
3237
|
+
if (selected === "__custom__") {
|
|
3238
|
+
const customPath = await clack.text({
|
|
3239
|
+
message: "Absolute path to pages directory",
|
|
3240
|
+
placeholder: "/path/to/packages/home/lib/pages",
|
|
3241
|
+
validate: (v) => {
|
|
3242
|
+
if (!v.trim()) return "Path is required";
|
|
3243
|
+
const resolved = path15.resolve(v.trim());
|
|
3244
|
+
if (!fs16.existsSync(resolved)) return "Path does not exist";
|
|
3245
|
+
}
|
|
3246
|
+
});
|
|
3247
|
+
if (clack.isCancel(customPath)) {
|
|
3248
|
+
clack.cancel("Cancelled");
|
|
3249
|
+
return;
|
|
3250
|
+
}
|
|
3251
|
+
pagesPath = path15.resolve(customPath);
|
|
3252
|
+
} else {
|
|
3253
|
+
pagesPath = selected;
|
|
3254
|
+
}
|
|
3255
|
+
} else {
|
|
3256
|
+
pagesPath = await askManualPagesPath();
|
|
3257
|
+
}
|
|
3258
|
+
} else {
|
|
3259
|
+
pagesPath = await askManualPagesPath();
|
|
3260
|
+
}
|
|
3261
|
+
}
|
|
3262
|
+
if (!pagesPath) return;
|
|
3263
|
+
const genSpinner = clack.spinner();
|
|
3264
|
+
genSpinner.start("Creating page...");
|
|
3265
|
+
try {
|
|
3266
|
+
await createPage(name, { pagesPath });
|
|
3267
|
+
genSpinner.stop("Page created");
|
|
3268
|
+
clack.outro(chalk11.green("Done!"));
|
|
3269
|
+
} catch (error) {
|
|
3270
|
+
genSpinner.stop("Failed");
|
|
3271
|
+
clack.outro(chalk11.red(`Error: ${error}`));
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
async function askManualPagesPath() {
|
|
3275
|
+
const customPath = await clack.text({
|
|
3276
|
+
message: "Absolute path to pages directory",
|
|
3277
|
+
placeholder: "/path/to/project/lib/pages",
|
|
3278
|
+
validate: (v) => {
|
|
3279
|
+
if (!v.trim()) return "Path is required";
|
|
3280
|
+
const resolved = path15.resolve(v.trim());
|
|
3281
|
+
if (!fs16.existsSync(resolved)) return "Path does not exist";
|
|
3282
|
+
}
|
|
3283
|
+
});
|
|
3284
|
+
if (clack.isCancel(customPath)) {
|
|
3285
|
+
clack.cancel("Cancelled");
|
|
3286
|
+
return void 0;
|
|
3287
|
+
}
|
|
3288
|
+
return path15.resolve(customPath);
|
|
3289
|
+
}
|
|
2609
3290
|
function findBffFiles(dir) {
|
|
2610
3291
|
const results = [];
|
|
2611
|
-
if (!
|
|
2612
|
-
const entries =
|
|
3292
|
+
if (!fs16.existsSync(dir)) return results;
|
|
3293
|
+
const entries = fs16.readdirSync(dir, { withFileTypes: true });
|
|
2613
3294
|
for (const entry of entries) {
|
|
2614
|
-
const fullPath =
|
|
3295
|
+
const fullPath = path15.join(dir, entry.name);
|
|
2615
3296
|
if (entry.isDirectory()) {
|
|
2616
3297
|
results.push(...findBffFiles(fullPath));
|
|
2617
3298
|
} else if (entry.isFile() && entry.name.endsWith(".dart") && !entry.name.includes(".g.")) {
|
|
@@ -2623,8 +3304,8 @@ function findBffFiles(dir) {
|
|
|
2623
3304
|
function inferBffFile(bffFiles, endpointPath) {
|
|
2624
3305
|
const firstSegment = endpointPath.replace(/^\//, "").split("/")[0].toLowerCase();
|
|
2625
3306
|
if (!firstSegment) return null;
|
|
2626
|
-
const withDomains = bffFiles.filter((f) => /^bff_.+_api\.dart$/.test(
|
|
2627
|
-
const base =
|
|
3307
|
+
const withDomains = bffFiles.filter((f) => /^bff_.+_api\.dart$/.test(path15.basename(f))).map((f) => {
|
|
3308
|
+
const base = path15.basename(f, ".dart");
|
|
2628
3309
|
const domain = base.replace(/^bff_/, "").replace(/_api$/, "");
|
|
2629
3310
|
return { file: f, domain };
|
|
2630
3311
|
});
|
|
@@ -2651,19 +3332,19 @@ async function resolveEndpointProject() {
|
|
|
2651
3332
|
s.message(`Monorepo found at ${monorepoRoot}`);
|
|
2652
3333
|
const packageBases = ["packages", "packages/features"];
|
|
2653
3334
|
for (const base of packageBases) {
|
|
2654
|
-
const baseDir =
|
|
2655
|
-
if (!
|
|
2656
|
-
const entries =
|
|
3335
|
+
const baseDir = path15.join(monorepoRoot, base);
|
|
3336
|
+
if (!fs16.existsSync(baseDir)) continue;
|
|
3337
|
+
const entries = fs16.readdirSync(baseDir, { withFileTypes: true });
|
|
2657
3338
|
for (const entry of entries) {
|
|
2658
3339
|
if (!entry.isDirectory()) continue;
|
|
2659
|
-
const candidate =
|
|
2660
|
-
if (!
|
|
2661
|
-
const bffPath =
|
|
2662
|
-
s.message(`Checking ${candidate} \u2192 bff exists: ${
|
|
2663
|
-
if (
|
|
3340
|
+
const candidate = path15.join(baseDir, entry.name);
|
|
3341
|
+
if (!fs16.existsSync(path15.join(candidate, "pubspec.yaml"))) continue;
|
|
3342
|
+
const bffPath = path15.join(candidate, "lib", "data", "api", "bff");
|
|
3343
|
+
s.message(`Checking ${candidate} \u2192 bff exists: ${fs16.existsSync(bffPath)}`);
|
|
3344
|
+
if (fs16.existsSync(bffPath)) {
|
|
2664
3345
|
const project2 = analyzeProject(candidate);
|
|
2665
3346
|
if (project2) {
|
|
2666
|
-
s.stop(`Using ${
|
|
3347
|
+
s.stop(`Using ${chalk11.green(project2.projectName)}`);
|
|
2667
3348
|
return project2;
|
|
2668
3349
|
}
|
|
2669
3350
|
}
|
|
@@ -2673,8 +3354,8 @@ async function resolveEndpointProject() {
|
|
|
2673
3354
|
s.message("No monorepo root found");
|
|
2674
3355
|
}
|
|
2675
3356
|
const cwdProject = analyzeProject(process.cwd());
|
|
2676
|
-
if (cwdProject &&
|
|
2677
|
-
s.stop(`Using ${
|
|
3357
|
+
if (cwdProject && fs16.existsSync(path15.join(cwdProject.projectRoot, "lib", "data", "api", "bff"))) {
|
|
3358
|
+
s.stop(`Using ${chalk11.green(cwdProject.projectName)}`);
|
|
2678
3359
|
return cwdProject;
|
|
2679
3360
|
}
|
|
2680
3361
|
s.stop("No BFF package found");
|
|
@@ -2683,33 +3364,33 @@ async function resolveEndpointProject() {
|
|
|
2683
3364
|
placeholder: "e.g. /path/to/my-package or ./packages/core",
|
|
2684
3365
|
validate: (v) => {
|
|
2685
3366
|
if (!v.trim()) return "Path is required";
|
|
2686
|
-
const resolved2 =
|
|
2687
|
-
if (!
|
|
2688
|
-
if (!
|
|
2689
|
-
if (!
|
|
3367
|
+
const resolved2 = path15.resolve(v.trim());
|
|
3368
|
+
if (!fs16.existsSync(resolved2)) return "Path does not exist";
|
|
3369
|
+
if (!fs16.existsSync(path15.join(resolved2, "pubspec.yaml"))) return "No pubspec.yaml found at this path";
|
|
3370
|
+
if (!fs16.existsSync(path15.join(resolved2, "lib", "data", "api", "bff"))) return "No lib/data/api/bff/ found at this path";
|
|
2690
3371
|
}
|
|
2691
3372
|
});
|
|
2692
3373
|
if (clack.isCancel(manualPath)) {
|
|
2693
3374
|
clack.cancel("Cancelled");
|
|
2694
3375
|
return null;
|
|
2695
3376
|
}
|
|
2696
|
-
const resolved =
|
|
3377
|
+
const resolved = path15.resolve(manualPath);
|
|
2697
3378
|
const project = analyzeProject(resolved);
|
|
2698
3379
|
if (!project) {
|
|
2699
|
-
clack.outro(
|
|
3380
|
+
clack.outro(chalk11.red(`Could not analyze project at ${resolved}`));
|
|
2700
3381
|
return null;
|
|
2701
3382
|
}
|
|
2702
|
-
clack.log.info(`Using ${
|
|
3383
|
+
clack.log.info(`Using ${chalk11.green(project.projectName)}`);
|
|
2703
3384
|
return project;
|
|
2704
3385
|
}
|
|
2705
3386
|
async function endpointFlow() {
|
|
2706
3387
|
const project = await resolveEndpointProject();
|
|
2707
3388
|
if (!project) return;
|
|
2708
|
-
const lib =
|
|
2709
|
-
const bffDir =
|
|
3389
|
+
const lib = path15.join(project.projectRoot, "lib");
|
|
3390
|
+
const bffDir = path15.join(lib, "data", "api", "bff");
|
|
2710
3391
|
const bffFiles = findBffFiles(bffDir);
|
|
2711
3392
|
if (bffFiles.length === 0) {
|
|
2712
|
-
clack.outro(
|
|
3393
|
+
clack.outro(chalk11.red("No BFF API files found. Ensure lib/data/api/bff/ exists with .dart files."));
|
|
2713
3394
|
return;
|
|
2714
3395
|
}
|
|
2715
3396
|
const httpMethod = await clack.select({
|
|
@@ -2739,13 +3420,13 @@ async function endpointFlow() {
|
|
|
2739
3420
|
}
|
|
2740
3421
|
let bffApiFile = inferBffFile(bffFiles, endpointPath);
|
|
2741
3422
|
if (bffApiFile) {
|
|
2742
|
-
clack.log.info(`Auto-detected BFF file: ${
|
|
3423
|
+
clack.log.info(`Auto-detected BFF file: ${chalk11.cyan(path15.basename(bffApiFile))}`);
|
|
2743
3424
|
} else {
|
|
2744
3425
|
const selected = await clack.select({
|
|
2745
3426
|
message: "Could not auto-detect BFF file. Select one:",
|
|
2746
3427
|
options: bffFiles.map((f) => ({
|
|
2747
3428
|
value: f,
|
|
2748
|
-
label:
|
|
3429
|
+
label: path15.relative(bffDir, f)
|
|
2749
3430
|
}))
|
|
2750
3431
|
});
|
|
2751
3432
|
if (clack.isCancel(selected)) {
|
|
@@ -2763,7 +3444,7 @@ async function endpointFlow() {
|
|
|
2763
3444
|
initialValue: inferredName,
|
|
2764
3445
|
validate: (v) => {
|
|
2765
3446
|
if (!v.trim()) return "Name is required";
|
|
2766
|
-
if (!
|
|
3447
|
+
if (!SNAKE_CASE_REGEX5.test(v)) return "Must be snake_case";
|
|
2767
3448
|
}
|
|
2768
3449
|
});
|
|
2769
3450
|
if (clack.isCancel(useCaseName)) {
|
|
@@ -2809,14 +3490,49 @@ async function endpointFlow() {
|
|
|
2809
3490
|
diLazySingleton
|
|
2810
3491
|
});
|
|
2811
3492
|
genSpinner.stop("Endpoint generated");
|
|
2812
|
-
clack.outro(
|
|
3493
|
+
clack.outro(chalk11.green("Done!"));
|
|
3494
|
+
} catch (error) {
|
|
3495
|
+
genSpinner.stop("Failed");
|
|
3496
|
+
clack.outro(chalk11.red(`Error: ${error}`));
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
async function packageFlow() {
|
|
3500
|
+
const monorepoRoot = findMonorepoRoot(process.cwd());
|
|
3501
|
+
if (!monorepoRoot) {
|
|
3502
|
+
clack.outro(chalk11.red("Not inside a monorepo. Run from within a Melos monorepo."));
|
|
3503
|
+
return;
|
|
3504
|
+
}
|
|
3505
|
+
clack.log.info(`Monorepo: ${chalk11.cyan(path15.relative(os.homedir(), monorepoRoot))}`);
|
|
3506
|
+
const name = await clack.text({
|
|
3507
|
+
message: "Package name (snake_case)",
|
|
3508
|
+
placeholder: "e.g. rewards, promotions, notifications",
|
|
3509
|
+
validate: (value) => {
|
|
3510
|
+
if (!value || !value.trim()) return "Name is required";
|
|
3511
|
+
if (!SNAKE_CASE_REGEX5.test(value)) return "Must be snake_case (lowercase, digits, underscores)";
|
|
3512
|
+
const pkgDir = path15.join(monorepoRoot, "packages", value);
|
|
3513
|
+
if (fs16.existsSync(pkgDir)) return `packages/${value} already exists`;
|
|
3514
|
+
}
|
|
3515
|
+
});
|
|
3516
|
+
if (clack.isCancel(name)) {
|
|
3517
|
+
clack.cancel("Cancelled");
|
|
3518
|
+
return;
|
|
3519
|
+
}
|
|
3520
|
+
const genSpinner = clack.spinner();
|
|
3521
|
+
genSpinner.start("Creating package...");
|
|
3522
|
+
try {
|
|
3523
|
+
await createPackage({
|
|
3524
|
+
monorepoRoot,
|
|
3525
|
+
packageName: name
|
|
3526
|
+
});
|
|
3527
|
+
genSpinner.stop("Package created");
|
|
3528
|
+
clack.outro(chalk11.green("Done!"));
|
|
2813
3529
|
} catch (error) {
|
|
2814
3530
|
genSpinner.stop("Failed");
|
|
2815
|
-
clack.outro(
|
|
3531
|
+
clack.outro(chalk11.red(`Error: ${error}`));
|
|
2816
3532
|
}
|
|
2817
3533
|
}
|
|
2818
3534
|
async function docsInteractiveMode() {
|
|
2819
|
-
clack.intro(
|
|
3535
|
+
clack.intro(chalk11.bgCyan(chalk11.black(" wlmaker docs ")));
|
|
2820
3536
|
const action = await clack.select({
|
|
2821
3537
|
message: "What do you want to do?",
|
|
2822
3538
|
options: [
|
|
@@ -2860,46 +3576,46 @@ async function docsInteractiveMode() {
|
|
|
2860
3576
|
}
|
|
2861
3577
|
const remoteUrl = "https://dc-wl-docs.web.app/development_workflow/";
|
|
2862
3578
|
if (!remoteUrl) {
|
|
2863
|
-
clack.outro(
|
|
3579
|
+
clack.outro(chalk11.red("Remote docs URL not configured. Set WL_DOCS_URL in your environment."));
|
|
2864
3580
|
return;
|
|
2865
3581
|
}
|
|
2866
|
-
clack.log.info(`Opening remote docs: ${
|
|
2867
|
-
|
|
2868
|
-
clack.outro(
|
|
3582
|
+
clack.log.info(`Opening remote docs: ${chalk11.cyan(remoteUrl)}`);
|
|
3583
|
+
execSync3(`open "${remoteUrl}"`, { stdio: "ignore" });
|
|
3584
|
+
clack.outro(chalk11.green("Opened remote docs in browser"));
|
|
2869
3585
|
return;
|
|
2870
3586
|
}
|
|
2871
3587
|
const bookDir = detectBookDir(process.cwd());
|
|
2872
3588
|
if (!bookDir) {
|
|
2873
3589
|
clack.outro(
|
|
2874
|
-
|
|
3590
|
+
chalk11.red("No Docusaurus book/ directory found. Run from a monorepo root.")
|
|
2875
3591
|
);
|
|
2876
3592
|
return;
|
|
2877
3593
|
}
|
|
2878
|
-
clack.log.info(`Serving docs from ${
|
|
3594
|
+
clack.log.info(`Serving docs from ${chalk11.cyan(bookDir)}`);
|
|
2879
3595
|
await serveBook(bookDir);
|
|
2880
3596
|
break;
|
|
2881
3597
|
}
|
|
2882
3598
|
case "commands": {
|
|
2883
3599
|
const commands = discoverCommands(process.cwd());
|
|
2884
3600
|
if (commands.length === 0) {
|
|
2885
|
-
clack.outro(
|
|
3601
|
+
clack.outro(chalk11.yellow("No commands found. Run from a monorepo root."));
|
|
2886
3602
|
return;
|
|
2887
3603
|
}
|
|
2888
|
-
clack.log.info(`Found ${
|
|
3604
|
+
clack.log.info(`Found ${chalk11.green(commands.length.toString())} command(s)`);
|
|
2889
3605
|
displayCommands(commands);
|
|
2890
|
-
clack.outro(
|
|
3606
|
+
clack.outro(chalk11.green("Done!"));
|
|
2891
3607
|
break;
|
|
2892
3608
|
}
|
|
2893
3609
|
case "architecture": {
|
|
2894
3610
|
const info = discoverArchitecture(process.cwd());
|
|
2895
3611
|
if (!info) {
|
|
2896
3612
|
clack.outro(
|
|
2897
|
-
|
|
3613
|
+
chalk11.yellow("No monorepo detected. Run from within a Melos monorepo.")
|
|
2898
3614
|
);
|
|
2899
3615
|
return;
|
|
2900
3616
|
}
|
|
2901
3617
|
displayArchitecture(info);
|
|
2902
|
-
clack.outro(
|
|
3618
|
+
clack.outro(chalk11.green("Done!"));
|
|
2903
3619
|
break;
|
|
2904
3620
|
}
|
|
2905
3621
|
}
|
|
@@ -2908,12 +3624,12 @@ var SCREAMING_SNAKE_REGEX = /^[A-Z][A-Z0-9_]*$/;
|
|
|
2908
3624
|
async function envVarFlow() {
|
|
2909
3625
|
const monorepoRoot = findMonorepoRoot(process.cwd());
|
|
2910
3626
|
if (!monorepoRoot) {
|
|
2911
|
-
clack.outro(
|
|
3627
|
+
clack.outro(chalk11.red("Not inside a monorepo. Run from within a Melos monorepo."));
|
|
2912
3628
|
return;
|
|
2913
3629
|
}
|
|
2914
3630
|
const apps = discoverAppsWithEnv(monorepoRoot);
|
|
2915
3631
|
if (apps.length === 0) {
|
|
2916
|
-
clack.outro(
|
|
3632
|
+
clack.outro(chalk11.red("No apps with env/ directory found in the monorepo."));
|
|
2917
3633
|
return;
|
|
2918
3634
|
}
|
|
2919
3635
|
const variableName = await clack.text({
|
|
@@ -3017,14 +3733,14 @@ async function envVarFlow() {
|
|
|
3017
3733
|
includeInAppConfig
|
|
3018
3734
|
});
|
|
3019
3735
|
genSpinner.stop("Done");
|
|
3020
|
-
clack.outro(
|
|
3736
|
+
clack.outro(chalk11.green("Environment variable added!"));
|
|
3021
3737
|
} catch (error) {
|
|
3022
3738
|
genSpinner.stop("Failed");
|
|
3023
|
-
clack.outro(
|
|
3739
|
+
clack.outro(chalk11.red(`Error: ${error}`));
|
|
3024
3740
|
}
|
|
3025
3741
|
}
|
|
3026
3742
|
async function interactiveMode() {
|
|
3027
|
-
clack.intro(
|
|
3743
|
+
clack.intro(chalk11.bgCyan(chalk11.black(" wlmaker ")));
|
|
3028
3744
|
const createType = await clack.select({
|
|
3029
3745
|
message: "What do you want to create?",
|
|
3030
3746
|
options: [
|
|
@@ -3035,11 +3751,21 @@ async function interactiveMode() {
|
|
|
3035
3751
|
label: "Widgetbook Use-Case",
|
|
3036
3752
|
hint: "Component showcase"
|
|
3037
3753
|
},
|
|
3754
|
+
{
|
|
3755
|
+
value: "page",
|
|
3756
|
+
label: "Page",
|
|
3757
|
+
hint: "GoRoute + View with barrels"
|
|
3758
|
+
},
|
|
3038
3759
|
{
|
|
3039
3760
|
value: "endpoint",
|
|
3040
3761
|
label: "Endpoint",
|
|
3041
3762
|
hint: "BFF Clean Architecture stack"
|
|
3042
3763
|
},
|
|
3764
|
+
{
|
|
3765
|
+
value: "package",
|
|
3766
|
+
label: "Package",
|
|
3767
|
+
hint: "Create a new package in the monorepo"
|
|
3768
|
+
},
|
|
3043
3769
|
{
|
|
3044
3770
|
value: "env-var",
|
|
3045
3771
|
label: "Env Var",
|
|
@@ -3070,9 +3796,15 @@ async function interactiveMode() {
|
|
|
3070
3796
|
case "usecase":
|
|
3071
3797
|
await useCaseFlow();
|
|
3072
3798
|
break;
|
|
3799
|
+
case "page":
|
|
3800
|
+
await pageFlow();
|
|
3801
|
+
break;
|
|
3073
3802
|
case "endpoint":
|
|
3074
3803
|
await endpointFlow();
|
|
3075
3804
|
break;
|
|
3805
|
+
case "package":
|
|
3806
|
+
await packageFlow();
|
|
3807
|
+
break;
|
|
3076
3808
|
case "env-var":
|
|
3077
3809
|
await envVarFlow();
|
|
3078
3810
|
break;
|
|
@@ -3098,7 +3830,7 @@ program.command("bloc").description("Create a new BLoC with Freezed sealed class
|
|
|
3098
3830
|
try {
|
|
3099
3831
|
await createBloc(name, options);
|
|
3100
3832
|
} catch (error) {
|
|
3101
|
-
console.error(
|
|
3833
|
+
console.error(chalk12.red(`Error: ${error}`));
|
|
3102
3834
|
process.exit(1);
|
|
3103
3835
|
}
|
|
3104
3836
|
}
|
|
@@ -3115,12 +3847,12 @@ program.command("widget").description("Create a new widget in the design system"
|
|
|
3115
3847
|
projectRoot: options.dir,
|
|
3116
3848
|
buildRunner: false
|
|
3117
3849
|
});
|
|
3118
|
-
console.log(
|
|
3850
|
+
console.log(chalk12.green("Widgetbook use-case created"));
|
|
3119
3851
|
} catch {
|
|
3120
|
-
console.log(
|
|
3852
|
+
console.log(chalk12.yellow("Use-case skipped (may already exist)"));
|
|
3121
3853
|
}
|
|
3122
3854
|
} catch (error) {
|
|
3123
|
-
console.error(
|
|
3855
|
+
console.error(chalk12.red(`Error: ${error}`));
|
|
3124
3856
|
process.exit(1);
|
|
3125
3857
|
}
|
|
3126
3858
|
}
|
|
@@ -3133,7 +3865,21 @@ program.command("usecase").description("Create a Widgetbook use-case for an exis
|
|
|
3133
3865
|
buildRunner: options.buildRunner
|
|
3134
3866
|
});
|
|
3135
3867
|
} catch (error) {
|
|
3136
|
-
console.error(
|
|
3868
|
+
console.error(chalk12.red(`Error: ${error}`));
|
|
3869
|
+
process.exit(1);
|
|
3870
|
+
}
|
|
3871
|
+
}
|
|
3872
|
+
);
|
|
3873
|
+
program.command("page").description("Create a new page (GoRoute + View) with barrel updates").argument("[name]", "Page name in snake_case (e.g. profile, order_detail)").option("-p, --path <path>", "absolute path to the pages directory").action(
|
|
3874
|
+
async (name, options) => {
|
|
3875
|
+
if (!name || !options.path) {
|
|
3876
|
+
await pageFlow(name, options.path);
|
|
3877
|
+
return;
|
|
3878
|
+
}
|
|
3879
|
+
try {
|
|
3880
|
+
await createPage(name, { pagesPath: options.path });
|
|
3881
|
+
} catch (error) {
|
|
3882
|
+
console.error(chalk12.red(`Error: ${error}`));
|
|
3137
3883
|
process.exit(1);
|
|
3138
3884
|
}
|
|
3139
3885
|
}
|
|
@@ -3143,6 +3889,9 @@ program.command("endpoint").description("Generate Clean Architecture stack for a
|
|
|
3143
3889
|
if (!project) return;
|
|
3144
3890
|
await endpointFlow(project);
|
|
3145
3891
|
});
|
|
3892
|
+
program.command("package").description("Create a new package in the monorepo").action(async () => {
|
|
3893
|
+
await packageFlow();
|
|
3894
|
+
});
|
|
3146
3895
|
program.command("env-var").description("Add an environment variable across the Flutter monorepo").action(async () => {
|
|
3147
3896
|
await envVarFlow();
|
|
3148
3897
|
});
|
|
@@ -3150,26 +3899,26 @@ var docsCmd = program.command("docs").description("Project documentation tools")
|
|
|
3150
3899
|
docsCmd.command("serve").description("Start Docusaurus dev server").option("-d, --dir <path>", "project root directory", process.cwd()).action(async (options) => {
|
|
3151
3900
|
const bookDir = detectBookDir(options.dir);
|
|
3152
3901
|
if (!bookDir) {
|
|
3153
|
-
console.error(
|
|
3902
|
+
console.error(chalk12.red("No Docusaurus book/ directory found. Run from a monorepo root."));
|
|
3154
3903
|
process.exit(1);
|
|
3155
3904
|
}
|
|
3156
|
-
console.log(
|
|
3905
|
+
console.log(chalk12.cyan(`Serving docs from ${bookDir}`));
|
|
3157
3906
|
await serveBook(bookDir);
|
|
3158
3907
|
});
|
|
3159
3908
|
docsCmd.command("commands").description("Show Makefile & melos commands reference").option("-d, --dir <path>", "project root directory", process.cwd()).action(async (options) => {
|
|
3160
3909
|
const commands = discoverCommands(options.dir);
|
|
3161
3910
|
if (commands.length === 0) {
|
|
3162
|
-
console.log(
|
|
3911
|
+
console.log(chalk12.yellow("No commands found. Run from a monorepo root."));
|
|
3163
3912
|
return;
|
|
3164
3913
|
}
|
|
3165
|
-
console.log(
|
|
3914
|
+
console.log(chalk12.green(`Found ${commands.length} command(s)
|
|
3166
3915
|
`));
|
|
3167
3916
|
displayCommands(commands);
|
|
3168
3917
|
});
|
|
3169
3918
|
docsCmd.command("architecture").description("Display monorepo architecture tree").option("-d, --dir <path>", "project root directory", process.cwd()).action(async (options) => {
|
|
3170
3919
|
const info = discoverArchitecture(options.dir);
|
|
3171
3920
|
if (!info) {
|
|
3172
|
-
console.log(
|
|
3921
|
+
console.log(chalk12.yellow("No monorepo detected. Run from within a Melos monorepo."));
|
|
3173
3922
|
return;
|
|
3174
3923
|
}
|
|
3175
3924
|
displayArchitecture(info);
|