wlmaker 1.2.9 → 1.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.mjs +70 -405
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -968,97 +968,16 @@ import * as path8 from "path";
968
968
  import chalk4 from "chalk";
969
969
  import { pascalCase as pascalCase4, camelCase } from "change-case";
970
970
 
971
- // src/core/json-to-dart.ts
972
- function jsonToFields(jsonStr) {
973
- const parsed = JSON.parse(jsonStr);
974
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
975
- throw new Error("Expected a JSON object at the top level");
976
- }
977
- return objectToFields(parsed);
978
- }
979
- function objectToFields(obj) {
980
- const fields = [];
981
- for (const [key, value] of Object.entries(obj)) {
982
- fields.push({
983
- name: key,
984
- dartType: dartTypeOf(value, key),
985
- isNullable: value === null
986
- });
987
- }
988
- return fields;
989
- }
990
- function dartTypeOf(value, nameHint) {
991
- if (value === null) return "dynamic";
992
- if (typeof value === "string") return "String";
993
- if (typeof value === "boolean") return "bool";
994
- if (typeof value === "number") {
995
- return Number.isInteger(value) ? "int" : "double";
996
- }
997
- if (Array.isArray(value)) {
998
- if (value.length === 0) return "List<dynamic>";
999
- const elementType = dartTypeOf(value[0], nameHint);
1000
- return `List<${elementType}>`;
1001
- }
1002
- if (typeof value === "object") {
1003
- return "Map<String, dynamic>";
1004
- }
1005
- return "dynamic";
1006
- }
1007
- function fieldsToConstructorParams(fields, indent = " ") {
1008
- return fields.map((f) => {
1009
- const nullable = f.isNullable ? "?" : "";
1010
- return `${indent}required ${f.dartType}${nullable} ${f.name},`;
1011
- }).join("\n");
1012
- }
1013
- function fieldsToFromJson(fields, modelName) {
1014
- const entries = fields.map((f) => {
1015
- if (f.dartType.startsWith("List<")) {
1016
- return ` ${f.name}: (${_jsonFieldName(f)} as List<dynamic>).map((e) => e as ${_extractGenericType(f.dartType)}).toList(),`;
1017
- }
1018
- return ` ${f.name}: ${_jsonFieldName(f)} as ${f.dartType}${f.isNullable ? "?" : ""},`;
1019
- }).join("\n");
1020
- return `factory ${modelName}.fromJson(Map<String, dynamic> json) => ${modelName}(
1021
- ${entries}
1022
- );`;
1023
- }
1024
- function fieldsToJson(fields, modelName) {
1025
- const entries = fields.map((f) => ` '${f.name}': ${f.name},`).join("\n");
1026
- return `Map<String, dynamic> toJson() => {
1027
- ${entries}
1028
- };`;
1029
- }
1030
- function _jsonFieldName(f) {
1031
- return `json['${f.name}']`;
1032
- }
1033
- function _extractGenericType(listType) {
1034
- const match = listType.match(/^List<(.+)>$/);
1035
- return match ? match[1] : "dynamic";
1036
- }
1037
-
1038
971
  // src/core/endpoint-templates.ts
1039
- function entityTemplate(name, pascal, fields) {
1040
- const params = fieldsToConstructorParams(fields);
1041
- const props = fields.map((f) => {
1042
- const nullable = f.isNullable ? "?" : "";
1043
- return ` final ${f.dartType}${nullable} ${f.name};`;
1044
- }).join("\n");
972
+ function entityBoilerplate(pascal) {
1045
973
  return `class ${pascal}Entity {
1046
- ${props}
974
+ // TODO: Define fields
1047
975
 
1048
- const ${pascal}Entity({
1049
- ${params}
1050
- });
976
+ const ${pascal}Entity();
1051
977
  }
1052
978
  `;
1053
979
  }
1054
- function modelTemplate(name, pascal, fields) {
1055
- const params = fieldsToConstructorParams(fields);
1056
- const props = fields.map((f) => {
1057
- const nullable = f.isNullable ? "?" : "";
1058
- return ` final ${f.dartType}${nullable} ${f.name};`;
1059
- }).join("\n");
1060
- const fromJson = fieldsToFromJson(fields, `${pascal}Model`);
1061
- const toJson = fieldsToJson(fields, `${pascal}Model`);
980
+ function modelBoilerplate(name, pascal) {
1062
981
  return `import 'package:json_annotation/json_annotation.dart';
1063
982
  import '../../domain/entities/${name}/${name}_entity.dart';
1064
983
 
@@ -1066,15 +985,7 @@ part '${name}_model.g.dart';
1066
985
 
1067
986
  @JsonSerializable()
1068
987
  class ${pascal}Model extends ${pascal}Entity {
1069
- const ${pascal}Model({
1070
- ${params}
1071
- }) : super(
1072
- ${fields.map((f) => ` ${f.name}: ${f.name},`).join("\n")}
1073
- );
1074
-
1075
- ${fromJson}
1076
-
1077
- ${toJson}
988
+ const ${pascal}Model() : super();
1078
989
 
1079
990
  factory ${pascal}Model.fromJson(Map<String, dynamic> json) =>
1080
991
  _$${pascal}ModelFromJson(json);
@@ -1084,23 +995,16 @@ ${fields.map((f) => ` ${f.name}: ${f.name},`).join("\n")}
1084
995
  }
1085
996
  `;
1086
997
  }
1087
- function requestModelTemplate(name, pascal, fields) {
1088
- const params = fieldsToConstructorParams(fields);
1089
- const props = fields.map((f) => {
1090
- const nullable = f.isNullable ? "?" : "";
1091
- return ` final ${f.dartType}${nullable} ${f.name};`;
1092
- }).join("\n");
998
+ function requestModelBoilerplate(name, pascal) {
1093
999
  return `import 'package:json_annotation/json_annotation.dart';
1094
1000
 
1095
1001
  part '${name}_request_model.g.dart';
1096
1002
 
1097
1003
  @JsonSerializable()
1098
1004
  class ${pascal}RequestModel {
1099
- ${props}
1005
+ // TODO: Define fields
1100
1006
 
1101
- ${pascal}RequestModel({
1102
- ${params}
1103
- });
1007
+ ${pascal}RequestModel();
1104
1008
 
1105
1009
  factory ${pascal}RequestModel.fromJson(Map<String, dynamic> json) =>
1106
1010
  _$${pascal}RequestModelFromJson(json);
@@ -1234,46 +1138,51 @@ function injectExport(filePath, exportLine) {
1234
1138
  // src/core/create-endpoint.ts
1235
1139
  async function createEndpoint(options) {
1236
1140
  const lib = path8.join(options.projectRoot, "lib");
1237
- const pascal = pascalCase4(options.responseModelName);
1141
+ const pascal = pascalCase4(options.useCaseName);
1238
1142
  const useCasePascal = pascalCase4(options.useCaseName);
1239
1143
  const useCaseSnake = options.useCaseName;
1240
- const hasRequestBody = ["POST", "PUT", "PATCH"].includes(options.httpMethod) && options.requestFields;
1144
+ const needsBody = ["POST", "PUT", "PATCH"].includes(options.httpMethod);
1145
+ const domain = extractDomain(options.bffApiFile);
1146
+ const datasourceFile = path8.join(lib, "data", "datasources", `${domain}_rest_datasource.dart`);
1147
+ const datasourceClassName = `${pascalCase4(domain)}RestDataSource`;
1148
+ const repositoryInterfaceFile = path8.join(lib, "domain", "repositories", `${domain}_repository.dart`);
1149
+ const repositoryInterfaceName = `${pascalCase4(domain)}Repository`;
1150
+ const repositoryImplFile = path8.join(lib, "data", "repositories", `${domain}_repository_data.dart`);
1151
+ const repositoryImplClassName = `${pascalCase4(domain)}RepositoryData`;
1152
+ const feature = domain;
1241
1153
  const pathParams = extractPathParams(options.endpointPath);
1242
- const methodParams = buildMethodParams(options, pathParams, hasRequestBody);
1154
+ const methodParams = buildMethodParams(options, pathParams, needsBody);
1243
1155
  const methodParamsForSignature = methodParams.map((p) => ({ name: p.name, type: p.type }));
1244
1156
  const returnType = `${pascal}Entity`;
1245
- const repositoryInterfaceSnake = path8.basename(options.repositoryInterfaceFile, ".dart");
1157
+ const repositoryInterfaceSnake = path8.basename(repositoryInterfaceFile, ".dart");
1246
1158
  const spinner2 = (msg) => console.log(chalk4.cyan(` \u2192 ${msg}`));
1247
- if (options.responseFields && options.responseFields.length > 0) {
1248
- spinner2("Generating entity");
1249
- const entityDir = path8.join(lib, "domain", "entities", options.feature);
1250
- fs9.mkdirSync(entityDir, { recursive: true });
1251
- fs9.writeFileSync(
1252
- path8.join(entityDir, `${options.responseModelName}_entity.dart`),
1253
- entityTemplate(options.responseModelName, pascal, options.responseFields)
1254
- );
1255
- spinner2("Generating model");
1256
- const modelDir = path8.join(lib, "data", "models", options.feature);
1257
- fs9.mkdirSync(modelDir, { recursive: true });
1258
- fs9.writeFileSync(
1259
- path8.join(modelDir, `${options.responseModelName}_model.dart`),
1260
- modelTemplate(options.responseModelName, pascal, options.responseFields)
1261
- );
1262
- }
1263
- if (hasRequestBody && options.requestFields && options.requestModelName) {
1159
+ spinner2("Generating entity");
1160
+ const entityDir = path8.join(lib, "domain", "entities", feature, useCaseSnake);
1161
+ fs9.mkdirSync(entityDir, { recursive: true });
1162
+ fs9.writeFileSync(
1163
+ path8.join(entityDir, `${useCaseSnake}_entity.dart`),
1164
+ entityBoilerplate(pascal)
1165
+ );
1166
+ spinner2("Generating model");
1167
+ const modelDir = path8.join(lib, "data", "models", feature, useCaseSnake);
1168
+ fs9.mkdirSync(modelDir, { recursive: true });
1169
+ fs9.writeFileSync(
1170
+ path8.join(modelDir, `${useCaseSnake}_model.dart`),
1171
+ modelBoilerplate(useCaseSnake, pascal)
1172
+ );
1173
+ if (needsBody) {
1264
1174
  spinner2("Generating request model");
1265
- const reqPascal = pascalCase4(options.requestModelName);
1266
- const reqModelDir = path8.join(lib, "data", "models", options.feature);
1175
+ const reqModelDir = path8.join(lib, "data", "models", feature, useCaseSnake);
1267
1176
  fs9.mkdirSync(reqModelDir, { recursive: true });
1268
1177
  fs9.writeFileSync(
1269
- path8.join(reqModelDir, `${options.requestModelName}_request_model.dart`),
1270
- requestModelTemplate(options.requestModelName, reqPascal, options.requestFields)
1178
+ path8.join(reqModelDir, `${useCaseSnake}_request_model.dart`),
1179
+ requestModelBoilerplate(useCaseSnake, pascal)
1271
1180
  );
1272
1181
  }
1273
1182
  spinner2("Injecting Retrofit method");
1274
1183
  const bffPath = path8.resolve(options.bffApiFile);
1275
1184
  if (fs9.existsSync(bffPath)) {
1276
- const retrofitParams = buildRetrofitParams(options, pathParams, hasRequestBody);
1185
+ const retrofitParams = buildRetrofitParams(options, pathParams, needsBody);
1277
1186
  const retrofitReturnType = `${pascal}Model`;
1278
1187
  const method = retrofitMethod(
1279
1188
  camelCase(options.useCaseName),
@@ -1283,53 +1192,53 @@ async function createEndpoint(options) {
1283
1192
  retrofitReturnType
1284
1193
  );
1285
1194
  injectMethod(bffPath, extractClassName(bffPath), method);
1286
- const modelImport = `import 'package:${options.feature}/data/models/${options.responseModelName}/${options.responseModelName}_model.dart';`;
1195
+ const modelImport = `import 'package:${feature}/data/models/${useCaseSnake}/${useCaseSnake}_model.dart';`;
1287
1196
  injectImport(bffPath, modelImport);
1288
1197
  } else {
1289
1198
  console.log(chalk4.yellow(` \u26A0 BFF API file not found: ${bffPath}`));
1290
1199
  }
1291
1200
  spinner2("Injecting datasource method");
1292
- const dsPath = path8.resolve(options.datasourceFile);
1201
+ const dsPath = path8.resolve(datasourceFile);
1293
1202
  if (fs9.existsSync(dsPath)) {
1294
1203
  const dsMethod = datasourceMethod(
1295
1204
  camelCase(options.useCaseName),
1296
1205
  returnType,
1297
1206
  methodParamsForSignature
1298
1207
  );
1299
- injectMethod(dsPath, options.datasourceClassName, dsMethod);
1208
+ injectMethod(dsPath, datasourceClassName, dsMethod);
1300
1209
  } else {
1301
1210
  console.log(chalk4.yellow(` \u26A0 Datasource file not found: ${dsPath}`));
1302
1211
  }
1303
1212
  spinner2("Injecting repository interface method");
1304
- const repoIfacePath = path8.resolve(options.repositoryInterfaceFile);
1213
+ const repoIfacePath = path8.resolve(repositoryInterfaceFile);
1305
1214
  if (fs9.existsSync(repoIfacePath)) {
1306
1215
  const ifaceMethod = repositoryInterfaceMethod(
1307
1216
  camelCase(options.useCaseName),
1308
1217
  returnType,
1309
1218
  methodParamsForSignature
1310
1219
  );
1311
- injectMethod(repoIfacePath, options.repositoryInterfaceName, ifaceMethod);
1312
- const entityImport = `import 'package:${options.feature}/domain/entities/${options.responseModelName}/${options.responseModelName}_entity.dart';`;
1220
+ injectMethod(repoIfacePath, repositoryInterfaceName, ifaceMethod);
1221
+ const entityImport = `import 'package:${feature}/domain/entities/${useCaseSnake}/${useCaseSnake}_entity.dart';`;
1313
1222
  injectImport(repoIfacePath, entityImport);
1314
1223
  } else {
1315
1224
  console.log(chalk4.yellow(` \u26A0 Repository interface not found: ${repoIfacePath}`));
1316
1225
  }
1317
- spinner2("Injectating repository implementation method");
1318
- const repoImplPath = path8.resolve(options.repositoryImplFile);
1226
+ spinner2("Injecting repository implementation method");
1227
+ const repoImplPath = path8.resolve(repositoryImplFile);
1319
1228
  if (fs9.existsSync(repoImplPath)) {
1320
- const dsVarName = camelCase(options.datasourceClassName);
1229
+ const dsVarName = camelCase(datasourceClassName);
1321
1230
  const implMethod = repositoryImplMethod(
1322
1231
  camelCase(options.useCaseName),
1323
1232
  returnType,
1324
1233
  methodParamsForSignature,
1325
1234
  dsVarName
1326
1235
  );
1327
- injectMethod(repoImplPath, options.repositoryImplClassName, implMethod);
1236
+ injectMethod(repoImplPath, repositoryImplClassName, implMethod);
1328
1237
  } else {
1329
1238
  console.log(chalk4.yellow(` \u26A0 Repository implementation not found: ${repoImplPath}`));
1330
1239
  }
1331
1240
  spinner2("Generating UseCase");
1332
- const useCaseDir = path8.join(lib, "domain", "usecases", options.feature);
1241
+ const useCaseDir = path8.join(lib, "domain", "usecases", feature);
1333
1242
  fs9.mkdirSync(useCaseDir, { recursive: true });
1334
1243
  fs9.writeFileSync(
1335
1244
  path8.join(useCaseDir, `${useCaseSnake}_usecase.dart`),
@@ -1339,26 +1248,22 @@ async function createEndpoint(options) {
1339
1248
  camelCase(options.useCaseName),
1340
1249
  methodParamsForSignature,
1341
1250
  returnType,
1342
- repositoryInterfaceName
1251
+ repositoryInterfaceSnake
1343
1252
  )
1344
1253
  );
1345
1254
  spinner2("Updating barrel file");
1346
1255
  const barrelPath = path8.join(useCaseDir, "usecases.dart");
1347
1256
  const exportLine = `export '${useCaseSnake}_usecase.dart';`;
1348
1257
  injectExport(barrelPath, exportLine);
1349
- if (options.runBuildRunner) {
1350
- const projectRoot = findPubspecDir(options.projectRoot);
1351
- if (projectRoot && hasBuildRunner(projectRoot)) {
1352
- spinner2("Running build_runner...");
1353
- await runBuildRunner(projectRoot);
1354
- console.log(chalk4.green(" \u2713 build_runner completed."));
1355
- } else {
1356
- console.log(chalk4.yellow(" \u26A0 Skipping build_runner (not found)."));
1357
- }
1358
- }
1359
1258
  console.log(chalk4.green(`
1360
1259
  \u2713 Endpoint "${options.useCaseName}" generated successfully.`));
1361
1260
  }
1261
+ function extractDomain(bffApiFile) {
1262
+ const baseName = path8.basename(bffApiFile);
1263
+ const match = baseName.match(/^bff_(.+)_api\.dart$/);
1264
+ if (match) return match[1];
1265
+ return baseName.replace(/\.dart$/, "");
1266
+ }
1362
1267
  function extractPathParams(endpointPath) {
1363
1268
  const regex = /\{(\w+)\}/g;
1364
1269
  const params = [];
@@ -1370,8 +1275,8 @@ function extractPathParams(endpointPath) {
1370
1275
  }
1371
1276
  function buildMethodParams(options, pathParams, hasRequestBody) {
1372
1277
  const params = [...pathParams];
1373
- if (hasRequestBody && options.requestModelName) {
1374
- const reqPascal = pascalCase4(options.requestModelName);
1278
+ if (hasRequestBody) {
1279
+ const reqPascal = pascalCase4(options.useCaseName);
1375
1280
  params.push({ name: "body", type: `${reqPascal}RequestModel` });
1376
1281
  }
1377
1282
  return params;
@@ -1381,8 +1286,8 @@ function buildRetrofitParams(options, pathParams, hasRequestBody) {
1381
1286
  for (const pp of pathParams) {
1382
1287
  params.push({ ...pp, isPath: true });
1383
1288
  }
1384
- if (hasRequestBody && options.requestModelName) {
1385
- const reqPascal = pascalCase4(options.requestModelName);
1289
+ if (hasRequestBody) {
1290
+ const reqPascal = pascalCase4(options.useCaseName);
1386
1291
  params.push({ name: "body", type: `${reqPascal}RequestModel`, isBody: true });
1387
1292
  }
1388
1293
  return params;
@@ -1395,7 +1300,6 @@ function extractClassName(filePath) {
1395
1300
  }
1396
1301
 
1397
1302
  // src/interactive.ts
1398
- import { pascalCase as pascalCase5 } from "change-case";
1399
1303
  var SNAKE_CASE_REGEX4 = /^[a-z][a-z0-9_]*$/;
1400
1304
  async function resolveProject() {
1401
1305
  const s = clack.spinner();
@@ -1700,7 +1604,7 @@ async function endpointFlow(project) {
1700
1604
  return;
1701
1605
  }
1702
1606
  const endpointPath = await clack.text({
1703
- message: "Endpoint path (e.g. /cp/{zipCode})",
1607
+ message: "Endpoint path (e.g. /products/{id})",
1704
1608
  placeholder: "/api/users/{id}",
1705
1609
  validate: (v) => {
1706
1610
  if (!v.trim()) return "Path is required";
@@ -1717,34 +1621,16 @@ async function endpointFlow(project) {
1717
1621
  if (dartFiles.length > 0) {
1718
1622
  const selected = await clack.select({
1719
1623
  message: "Select BFF API file",
1720
- options: [
1721
- ...dartFiles.map((f) => ({
1722
- value: path9.join(bffDir, f),
1723
- label: f
1724
- })),
1725
- { value: "__custom__", label: "Custom path..." }
1726
- ]
1624
+ options: dartFiles.map((f) => ({
1625
+ value: path9.join(bffDir, f),
1626
+ label: f
1627
+ }))
1727
1628
  });
1728
1629
  if (clack.isCancel(selected)) {
1729
1630
  clack.cancel("Cancelled");
1730
1631
  return;
1731
1632
  }
1732
- if (selected === "__custom__") {
1733
- const customPath = await clack.text({
1734
- message: "BFF API file path",
1735
- placeholder: "lib/data/api/bff/my_api.dart",
1736
- validate: (v) => {
1737
- if (!v.trim()) return "Path is required";
1738
- }
1739
- });
1740
- if (clack.isCancel(customPath)) {
1741
- clack.cancel("Cancelled");
1742
- return;
1743
- }
1744
- bffApiFile = path9.resolve(customPath);
1745
- } else {
1746
- bffApiFile = selected;
1747
- }
1633
+ bffApiFile = selected;
1748
1634
  } else {
1749
1635
  const customPath = await clack.text({
1750
1636
  message: "BFF API file path (no .dart files found in bff/)",
@@ -1773,128 +1659,9 @@ async function endpointFlow(project) {
1773
1659
  }
1774
1660
  bffApiFile = path9.resolve(customPath);
1775
1661
  }
1776
- const responseChoice = await clack.select({
1777
- message: "Response model",
1778
- options: [
1779
- { value: "existing", label: "Use existing model", hint: "Reference an already created model" },
1780
- { value: "json", label: "Paste JSON", hint: "Generate entity + model from JSON" }
1781
- ]
1782
- });
1783
- if (clack.isCancel(responseChoice)) {
1784
- clack.cancel("Cancelled");
1785
- return;
1786
- }
1787
- let responseModelName = "";
1788
- let responseFields;
1789
- if (responseChoice === "json") {
1790
- const modelName = await clack.text({
1791
- message: "Response model name (snake_case)",
1792
- placeholder: "e.g. user_profile",
1793
- validate: (v) => {
1794
- if (!v.trim()) return "Name is required";
1795
- if (!SNAKE_CASE_REGEX4.test(v)) return "Must be snake_case";
1796
- }
1797
- });
1798
- if (clack.isCancel(modelName)) {
1799
- clack.cancel("Cancelled");
1800
- return;
1801
- }
1802
- responseModelName = modelName;
1803
- const jsonStr = await clack.text({
1804
- message: "Paste the JSON response",
1805
- placeholder: '{ "id": 1, "name": "John" }',
1806
- validate: (v) => {
1807
- if (!v.trim()) return "JSON is required";
1808
- try {
1809
- JSON.parse(v);
1810
- } catch {
1811
- return "Invalid JSON";
1812
- }
1813
- }
1814
- });
1815
- if (clack.isCancel(jsonStr)) {
1816
- clack.cancel("Cancelled");
1817
- return;
1818
- }
1819
- responseFields = jsonToFields(jsonStr);
1820
- } else {
1821
- const modelName = await clack.text({
1822
- message: "Existing response model name (snake_case)",
1823
- placeholder: "e.g. user_profile",
1824
- validate: (v) => {
1825
- if (!v.trim()) return "Name is required";
1826
- }
1827
- });
1828
- if (clack.isCancel(modelName)) {
1829
- clack.cancel("Cancelled");
1830
- return;
1831
- }
1832
- responseModelName = modelName;
1833
- }
1834
- let requestModelName;
1835
- let requestFields;
1836
- const needsBody = ["POST", "PUT", "PATCH"].includes(httpMethod);
1837
- if (needsBody) {
1838
- const requestChoice = await clack.select({
1839
- message: "Request body",
1840
- options: [
1841
- { value: "json", label: "Paste JSON", hint: "Generate request model from JSON" },
1842
- { value: "existing", label: "Use existing model", hint: "Reference an already created model" },
1843
- { value: "none", label: "No body", hint: "Just path/query params" }
1844
- ]
1845
- });
1846
- if (clack.isCancel(requestChoice)) {
1847
- clack.cancel("Cancelled");
1848
- return;
1849
- }
1850
- if (requestChoice === "json") {
1851
- const reqName = await clack.text({
1852
- message: "Request model name (snake_case)",
1853
- placeholder: `e.g. create_${responseModelName}`,
1854
- validate: (v) => {
1855
- if (!v.trim()) return "Name is required";
1856
- if (!SNAKE_CASE_REGEX4.test(v)) return "Must be snake_case";
1857
- }
1858
- });
1859
- if (clack.isCancel(reqName)) {
1860
- clack.cancel("Cancelled");
1861
- return;
1862
- }
1863
- requestModelName = reqName;
1864
- const reqJson = await clack.text({
1865
- message: "Paste the request JSON body",
1866
- placeholder: '{ "name": "John", "email": "john@example.com" }',
1867
- validate: (v) => {
1868
- if (!v.trim()) return "JSON is required";
1869
- try {
1870
- JSON.parse(v);
1871
- } catch {
1872
- return "Invalid JSON";
1873
- }
1874
- }
1875
- });
1876
- if (clack.isCancel(reqJson)) {
1877
- clack.cancel("Cancelled");
1878
- return;
1879
- }
1880
- requestFields = jsonToFields(reqJson);
1881
- } else if (requestChoice === "existing") {
1882
- const reqName = await clack.text({
1883
- message: "Existing request model name (snake_case)",
1884
- placeholder: `e.g. create_${responseModelName}`,
1885
- validate: (v) => {
1886
- if (!v.trim()) return "Name is required";
1887
- }
1888
- });
1889
- if (clack.isCancel(reqName)) {
1890
- clack.cancel("Cancelled");
1891
- return;
1892
- }
1893
- requestModelName = reqName;
1894
- }
1895
- }
1662
+ const methodLower = httpMethod.toLowerCase();
1896
1663
  const pathSegments = endpointPath.replace(/^\//, "").split("/").filter((s) => !s.startsWith("{"));
1897
- const inferredName = pathSegments.length > 0 ? "get_" + pathSegments.join("_") : "get_data";
1664
+ const inferredName = pathSegments.length > 0 ? `${methodLower}_${pathSegments.join("_")}` : `${methodLower}_data`;
1898
1665
  const useCaseName = await clack.text({
1899
1666
  message: "UseCase name (snake_case)",
1900
1667
  placeholder: inferredName,
@@ -1908,96 +1675,6 @@ async function endpointFlow(project) {
1908
1675
  clack.cancel("Cancelled");
1909
1676
  return;
1910
1677
  }
1911
- const datasourceFile = await clack.text({
1912
- message: "Datasource file path",
1913
- placeholder: `lib/data/datasources/${responseModelName}_datasource.dart`,
1914
- validate: (v) => {
1915
- if (!v.trim()) return "Path is required";
1916
- }
1917
- });
1918
- if (clack.isCancel(datasourceFile)) {
1919
- clack.cancel("Cancelled");
1920
- return;
1921
- }
1922
- const datasourceClassName = await clack.text({
1923
- message: "Datasource class name",
1924
- placeholder: `${pascalCase5(responseModelName)}Datasource`,
1925
- initialValue: `${pascalCase5(responseModelName)}Datasource`,
1926
- validate: (v) => {
1927
- if (!v.trim()) return "Class name is required";
1928
- }
1929
- });
1930
- if (clack.isCancel(datasourceClassName)) {
1931
- clack.cancel("Cancelled");
1932
- return;
1933
- }
1934
- const repositoryInterfaceFile = await clack.text({
1935
- message: "Repository interface file path",
1936
- placeholder: `lib/domain/repositories/${responseModelName}_repository.dart`,
1937
- validate: (v) => {
1938
- if (!v.trim()) return "Path is required";
1939
- }
1940
- });
1941
- if (clack.isCancel(repositoryInterfaceFile)) {
1942
- clack.cancel("Cancelled");
1943
- return;
1944
- }
1945
- const repositoryInterfaceName2 = await clack.text({
1946
- message: "Repository interface name",
1947
- placeholder: `${pascalCase5(responseModelName)}Repository`,
1948
- initialValue: `${pascalCase5(responseModelName)}Repository`,
1949
- validate: (v) => {
1950
- if (!v.trim()) return "Name is required";
1951
- }
1952
- });
1953
- if (clack.isCancel(repositoryInterfaceName2)) {
1954
- clack.cancel("Cancelled");
1955
- return;
1956
- }
1957
- const repositoryImplFile = await clack.text({
1958
- message: "Repository implementation file path",
1959
- placeholder: `lib/data/repositories/${responseModelName}_repository_impl.dart`,
1960
- validate: (v) => {
1961
- if (!v.trim()) return "Path is required";
1962
- }
1963
- });
1964
- if (clack.isCancel(repositoryImplFile)) {
1965
- clack.cancel("Cancelled");
1966
- return;
1967
- }
1968
- const repositoryImplClassName = await clack.text({
1969
- message: "Repository implementation class name",
1970
- placeholder: `${pascalCase5(responseModelName)}RepositoryImpl`,
1971
- initialValue: `${pascalCase5(responseModelName)}RepositoryImpl`,
1972
- validate: (v) => {
1973
- if (!v.trim()) return "Name is required";
1974
- }
1975
- });
1976
- if (clack.isCancel(repositoryImplClassName)) {
1977
- clack.cancel("Cancelled");
1978
- return;
1979
- }
1980
- const feature = await clack.text({
1981
- message: "Feature name (snake_case, for directory grouping)",
1982
- placeholder: responseModelName,
1983
- initialValue: responseModelName,
1984
- validate: (v) => {
1985
- if (!v.trim()) return "Feature name is required";
1986
- if (!SNAKE_CASE_REGEX4.test(v)) return "Must be snake_case";
1987
- }
1988
- });
1989
- if (clack.isCancel(feature)) {
1990
- clack.cancel("Cancelled");
1991
- return;
1992
- }
1993
- const runBuildRunner2 = await clack.confirm({
1994
- message: "Run build_runner after generation?",
1995
- initialValue: project.hasBuildRunner
1996
- });
1997
- if (clack.isCancel(runBuildRunner2)) {
1998
- clack.cancel("Cancelled");
1999
- return;
2000
- }
2001
1678
  const genSpinner = clack.spinner();
2002
1679
  genSpinner.start("Generating endpoint stack...");
2003
1680
  try {
@@ -2006,19 +1683,7 @@ async function endpointFlow(project) {
2006
1683
  httpMethod,
2007
1684
  endpointPath,
2008
1685
  bffApiFile,
2009
- responseModelName,
2010
- responseFields,
2011
- requestModelName,
2012
- requestFields,
2013
- useCaseName,
2014
- datasourceFile,
2015
- datasourceClassName,
2016
- repositoryInterfaceFile,
2017
- repositoryInterfaceName: repositoryInterfaceName2,
2018
- repositoryImplFile,
2019
- repositoryImplClassName,
2020
- feature,
2021
- runBuildRunner: runBuildRunner2
1686
+ useCaseName
2022
1687
  });
2023
1688
  genSpinner.stop("Endpoint generated");
2024
1689
  clack.outro(chalk5.green("Done!"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wlmaker",
3
- "version": "1.2.9",
3
+ "version": "1.2.10",
4
4
  "description": "Create Flutter BLoCs with Freezed sealed classes from the terminal",
5
5
  "keywords": [
6
6
  "flutter",