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