wlmaker 1.2.10 → 1.2.11

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 +138 -74
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -977,20 +977,26 @@ function entityBoilerplate(pascal) {
977
977
  }
978
978
  `;
979
979
  }
980
- function modelBoilerplate(name, pascal) {
981
- return `import 'package:json_annotation/json_annotation.dart';
982
- import '../../domain/entities/${name}/${name}_entity.dart';
980
+ function modelBoilerplate(name, pascal, projectName) {
981
+ return `import 'package:${projectName}/core.dart';
982
+ import 'package:json_annotation/json_annotation.dart';
983
983
 
984
984
  part '${name}_model.g.dart';
985
985
 
986
- @JsonSerializable()
987
- class ${pascal}Model extends ${pascal}Entity {
988
- const ${pascal}Model() : super();
986
+ @JsonSerializable(createToJson: true)
987
+ class ${pascal}Model {
988
+ // TODO: Define constructor parameters and fields
989
+
990
+ const ${pascal}Model();
989
991
 
990
992
  factory ${pascal}Model.fromJson(Map<String, dynamic> json) =>
991
993
  _$${pascal}ModelFromJson(json);
992
994
 
993
- @override
995
+ ${pascal}Entity toEntity() {
996
+ // TODO: Implement toEntity mapping
997
+ throw UnimplementedError();
998
+ }
999
+
994
1000
  Map<String, dynamic> toJson() => _$${pascal}ModelToJson(this);
995
1001
  }
996
1002
  `;
@@ -1000,11 +1006,11 @@ function requestModelBoilerplate(name, pascal) {
1000
1006
 
1001
1007
  part '${name}_request_model.g.dart';
1002
1008
 
1003
- @JsonSerializable()
1009
+ @JsonSerializable(createToJson: true)
1004
1010
  class ${pascal}RequestModel {
1005
- // TODO: Define fields
1011
+ // TODO: Define constructor parameters and fields
1006
1012
 
1007
- ${pascal}RequestModel();
1013
+ const ${pascal}RequestModel();
1008
1014
 
1009
1015
  factory ${pascal}RequestModel.fromJson(Map<String, dynamic> json) =>
1010
1016
  _$${pascal}RequestModelFromJson(json);
@@ -1060,12 +1066,13 @@ function repositoryInterfaceMethod(methodName, returnType, params) {
1060
1066
  const paramList = params.map((p) => `${p.type} ${p.name}`).join(", ");
1061
1067
  return `Future<${returnType}> ${methodName}(${paramList});`;
1062
1068
  }
1063
- function repositoryImplMethod(methodName, returnType, params, datasourceName) {
1069
+ function repositoryImplMethod(methodName, returnType, modelType, params, datasourceName) {
1064
1070
  const paramList = params.map((p) => `${p.type} ${p.name}`).join(", ");
1065
1071
  const args = params.map((p) => p.name).join(", ");
1066
1072
  return `@override
1067
1073
  Future<${returnType}> ${methodName}(${paramList}) async {
1068
- return await ${datasourceName}.${methodName}(${args});
1074
+ final model = await ${datasourceName}.${methodName}(${args});
1075
+ return model.toEntity();
1069
1076
  }`;
1070
1077
  }
1071
1078
 
@@ -1153,26 +1160,27 @@ async function createEndpoint(options) {
1153
1160
  const pathParams = extractPathParams(options.endpointPath);
1154
1161
  const methodParams = buildMethodParams(options, pathParams, needsBody);
1155
1162
  const methodParamsForSignature = methodParams.map((p) => ({ name: p.name, type: p.type }));
1156
- const returnType = `${pascal}Entity`;
1163
+ const modelType = `${pascal}Model`;
1164
+ const entityType = `${pascal}Entity`;
1157
1165
  const repositoryInterfaceSnake = path8.basename(repositoryInterfaceFile, ".dart");
1158
1166
  const spinner2 = (msg) => console.log(chalk4.cyan(` \u2192 ${msg}`));
1159
1167
  spinner2("Generating entity");
1160
- const entityDir = path8.join(lib, "domain", "entities", feature, useCaseSnake);
1168
+ const entityDir = path8.join(lib, "domain", "entities", feature);
1161
1169
  fs9.mkdirSync(entityDir, { recursive: true });
1162
1170
  fs9.writeFileSync(
1163
1171
  path8.join(entityDir, `${useCaseSnake}_entity.dart`),
1164
1172
  entityBoilerplate(pascal)
1165
1173
  );
1166
1174
  spinner2("Generating model");
1167
- const modelDir = path8.join(lib, "data", "models", feature, useCaseSnake);
1175
+ const modelDir = path8.join(lib, "data", "models", feature);
1168
1176
  fs9.mkdirSync(modelDir, { recursive: true });
1169
1177
  fs9.writeFileSync(
1170
1178
  path8.join(modelDir, `${useCaseSnake}_model.dart`),
1171
- modelBoilerplate(useCaseSnake, pascal)
1179
+ modelBoilerplate(useCaseSnake, pascal, options.projectName)
1172
1180
  );
1173
1181
  if (needsBody) {
1174
1182
  spinner2("Generating request model");
1175
- const reqModelDir = path8.join(lib, "data", "models", feature, useCaseSnake);
1183
+ const reqModelDir = path8.join(lib, "data", "models", feature);
1176
1184
  fs9.mkdirSync(reqModelDir, { recursive: true });
1177
1185
  fs9.writeFileSync(
1178
1186
  path8.join(reqModelDir, `${useCaseSnake}_request_model.dart`),
@@ -1183,16 +1191,15 @@ async function createEndpoint(options) {
1183
1191
  const bffPath = path8.resolve(options.bffApiFile);
1184
1192
  if (fs9.existsSync(bffPath)) {
1185
1193
  const retrofitParams = buildRetrofitParams(options, pathParams, needsBody);
1186
- const retrofitReturnType = `${pascal}Model`;
1187
1194
  const method = retrofitMethod(
1188
1195
  camelCase(options.useCaseName),
1189
1196
  options.endpointPath,
1190
1197
  options.httpMethod,
1191
1198
  retrofitParams,
1192
- retrofitReturnType
1199
+ modelType
1193
1200
  );
1194
1201
  injectMethod(bffPath, extractClassName(bffPath), method);
1195
- const modelImport = `import 'package:${feature}/data/models/${useCaseSnake}/${useCaseSnake}_model.dart';`;
1202
+ const modelImport = `import 'package:${options.projectName}/data/models/${feature}/${useCaseSnake}_model.dart';`;
1196
1203
  injectImport(bffPath, modelImport);
1197
1204
  } else {
1198
1205
  console.log(chalk4.yellow(` \u26A0 BFF API file not found: ${bffPath}`));
@@ -1202,7 +1209,7 @@ async function createEndpoint(options) {
1202
1209
  if (fs9.existsSync(dsPath)) {
1203
1210
  const dsMethod = datasourceMethod(
1204
1211
  camelCase(options.useCaseName),
1205
- returnType,
1212
+ modelType,
1206
1213
  methodParamsForSignature
1207
1214
  );
1208
1215
  injectMethod(dsPath, datasourceClassName, dsMethod);
@@ -1214,11 +1221,11 @@ async function createEndpoint(options) {
1214
1221
  if (fs9.existsSync(repoIfacePath)) {
1215
1222
  const ifaceMethod = repositoryInterfaceMethod(
1216
1223
  camelCase(options.useCaseName),
1217
- returnType,
1224
+ entityType,
1218
1225
  methodParamsForSignature
1219
1226
  );
1220
1227
  injectMethod(repoIfacePath, repositoryInterfaceName, ifaceMethod);
1221
- const entityImport = `import 'package:${feature}/domain/entities/${useCaseSnake}/${useCaseSnake}_entity.dart';`;
1228
+ const entityImport = `import 'package:${options.projectName}/domain/entities/${feature}/${useCaseSnake}_entity.dart';`;
1222
1229
  injectImport(repoIfacePath, entityImport);
1223
1230
  } else {
1224
1231
  console.log(chalk4.yellow(` \u26A0 Repository interface not found: ${repoIfacePath}`));
@@ -1229,7 +1236,8 @@ async function createEndpoint(options) {
1229
1236
  const dsVarName = camelCase(datasourceClassName);
1230
1237
  const implMethod = repositoryImplMethod(
1231
1238
  camelCase(options.useCaseName),
1232
- returnType,
1239
+ entityType,
1240
+ modelType,
1233
1241
  methodParamsForSignature,
1234
1242
  dsVarName
1235
1243
  );
@@ -1247,14 +1255,32 @@ async function createEndpoint(options) {
1247
1255
  useCasePascal,
1248
1256
  camelCase(options.useCaseName),
1249
1257
  methodParamsForSignature,
1250
- returnType,
1258
+ entityType,
1251
1259
  repositoryInterfaceSnake
1252
1260
  )
1253
1261
  );
1254
- spinner2("Updating barrel file");
1255
- const barrelPath = path8.join(useCaseDir, "usecases.dart");
1256
- const exportLine = `export '${useCaseSnake}_usecase.dart';`;
1257
- injectExport(barrelPath, exportLine);
1262
+ spinner2("Updating barrel files");
1263
+ const entityBarrel = path8.join(lib, "domain", "entities", feature, `${feature}.dart`);
1264
+ injectExport(entityBarrel, `export '${useCaseSnake}_entity.dart';`);
1265
+ injectExport(
1266
+ path8.join(lib, "domain", "entities", "entities.dart"),
1267
+ `export '${feature}/${feature}.dart';`
1268
+ );
1269
+ const modelBarrel = path8.join(lib, "data", "models", feature, `${feature}.dart`);
1270
+ injectExport(modelBarrel, `export '${useCaseSnake}_model.dart';`);
1271
+ if (needsBody) {
1272
+ injectExport(modelBarrel, `export '${useCaseSnake}_request_model.dart';`);
1273
+ }
1274
+ injectExport(
1275
+ path8.join(lib, "data", "models", "models.dart"),
1276
+ `export '${feature}/${feature}.dart';`
1277
+ );
1278
+ const useCaseBarrel = path8.join(useCaseDir, `${feature}.dart`);
1279
+ injectExport(useCaseBarrel, `export '${useCaseSnake}_usecase.dart';`);
1280
+ injectExport(
1281
+ path8.join(lib, "domain", "usecases", "usecases.dart"),
1282
+ `export '${feature}/${feature}.dart';`
1283
+ );
1258
1284
  console.log(chalk4.green(`
1259
1285
  \u2713 Endpoint "${options.useCaseName}" generated successfully.`));
1260
1286
  }
@@ -1587,8 +1613,76 @@ async function useCaseFlow(project) {
1587
1613
  clack.outro(chalk5.red(`Error: ${error}`));
1588
1614
  }
1589
1615
  }
1590
- async function endpointFlow(project) {
1616
+ function findBffFiles(dir) {
1617
+ const results = [];
1618
+ if (!fs10.existsSync(dir)) return results;
1619
+ const entries = fs10.readdirSync(dir, { withFileTypes: true });
1620
+ for (const entry of entries) {
1621
+ const fullPath = path9.join(dir, entry.name);
1622
+ if (entry.isDirectory()) {
1623
+ results.push(...findBffFiles(fullPath));
1624
+ } else if (entry.isFile() && entry.name.endsWith(".dart") && !entry.name.includes(".g.")) {
1625
+ results.push(fullPath);
1626
+ }
1627
+ }
1628
+ return results.sort();
1629
+ }
1630
+ function inferBffFile(bffFiles, endpointPath) {
1631
+ const firstSegment = endpointPath.replace(/^\//, "").split("/")[0].toLowerCase();
1632
+ if (!firstSegment) return null;
1633
+ const withDomains = bffFiles.map((f) => {
1634
+ const base = path9.basename(f, ".dart");
1635
+ const domain = base.replace(/^bff_/, "").replace(/_api$/, "");
1636
+ return { file: f, domain };
1637
+ });
1638
+ const exact = withDomains.filter((x) => x.domain === firstSegment);
1639
+ if (exact.length === 1) return exact[0].file;
1640
+ const singular = firstSegment.replace(/s$/, "");
1641
+ const singularMatch = withDomains.filter((x) => x.domain === singular);
1642
+ if (singularMatch.length === 1) return singularMatch[0].file;
1643
+ const endsWith = withDomains.filter(
1644
+ (x) => x.domain.endsWith(firstSegment) || x.domain.endsWith(singular)
1645
+ );
1646
+ if (endsWith.length === 1) return endsWith[0].file;
1647
+ const contains = withDomains.filter(
1648
+ (x) => firstSegment.includes(x.domain) || singular.includes(x.domain)
1649
+ );
1650
+ if (contains.length === 1) return contains[0].file;
1651
+ return null;
1652
+ }
1653
+ async function resolveEndpointProject() {
1654
+ const s = clack.spinner();
1655
+ s.start("Finding BFF package...");
1656
+ const monorepoRoot = findMonorepoRoot(process.cwd());
1657
+ if (monorepoRoot) {
1658
+ const packages = discoverPackages(monorepoRoot);
1659
+ const bffPackage = packages.find(
1660
+ (p) => fs10.existsSync(path9.join(p.projectRoot, "lib", "data", "api", "bff"))
1661
+ );
1662
+ if (bffPackage) {
1663
+ s.stop(`Using ${chalk5.green(bffPackage.projectName)}`);
1664
+ return bffPackage;
1665
+ }
1666
+ }
1667
+ const cwdProject = analyzeProject(process.cwd());
1668
+ if (cwdProject && fs10.existsSync(path9.join(cwdProject.projectRoot, "lib", "data", "api", "bff"))) {
1669
+ s.stop(`Using ${chalk5.green(cwdProject.projectName)}`);
1670
+ return cwdProject;
1671
+ }
1672
+ s.stop("No BFF package found");
1673
+ clack.outro(chalk5.red("Could not find a package with lib/data/api/bff/."));
1674
+ return null;
1675
+ }
1676
+ async function endpointFlow() {
1677
+ const project = await resolveEndpointProject();
1678
+ if (!project) return;
1591
1679
  const lib = path9.join(project.projectRoot, "lib");
1680
+ const bffDir = path9.join(lib, "data", "api", "bff");
1681
+ const bffFiles = findBffFiles(bffDir);
1682
+ if (bffFiles.length === 0) {
1683
+ clack.outro(chalk5.red("No BFF API files found. Ensure lib/data/api/bff/ exists with .dart files."));
1684
+ return;
1685
+ }
1592
1686
  const httpMethod = await clack.select({
1593
1687
  message: "HTTP Method",
1594
1688
  options: [
@@ -1614,50 +1708,22 @@ async function endpointFlow(project) {
1614
1708
  clack.cancel("Cancelled");
1615
1709
  return;
1616
1710
  }
1617
- const bffDir = path9.join(lib, "data", "api", "bff");
1618
- let bffApiFile = "";
1619
- if (fs10.existsSync(bffDir)) {
1620
- const dartFiles = fs10.readdirSync(bffDir).filter((f) => f.endsWith(".dart")).sort();
1621
- if (dartFiles.length > 0) {
1622
- const selected = await clack.select({
1623
- message: "Select BFF API file",
1624
- options: dartFiles.map((f) => ({
1625
- value: path9.join(bffDir, f),
1626
- label: f
1627
- }))
1628
- });
1629
- if (clack.isCancel(selected)) {
1630
- clack.cancel("Cancelled");
1631
- return;
1632
- }
1633
- bffApiFile = selected;
1634
- } else {
1635
- const customPath = await clack.text({
1636
- message: "BFF API file path (no .dart files found in bff/)",
1637
- placeholder: "lib/data/api/bff/my_api.dart",
1638
- validate: (v) => {
1639
- if (!v.trim()) return "Path is required";
1640
- }
1641
- });
1642
- if (clack.isCancel(customPath)) {
1643
- clack.cancel("Cancelled");
1644
- return;
1645
- }
1646
- bffApiFile = path9.resolve(customPath);
1647
- }
1711
+ let bffApiFile = inferBffFile(bffFiles, endpointPath);
1712
+ if (bffApiFile) {
1713
+ clack.log.info(`Auto-detected BFF file: ${chalk5.cyan(path9.basename(bffApiFile))}`);
1648
1714
  } else {
1649
- const customPath = await clack.text({
1650
- message: "BFF API file path (bff/ dir not found)",
1651
- placeholder: "lib/data/api/bff/my_api.dart",
1652
- validate: (v) => {
1653
- if (!v.trim()) return "Path is required";
1654
- }
1715
+ const selected = await clack.select({
1716
+ message: "Could not auto-detect BFF file. Select one:",
1717
+ options: bffFiles.map((f) => ({
1718
+ value: f,
1719
+ label: path9.relative(bffDir, f)
1720
+ }))
1655
1721
  });
1656
- if (clack.isCancel(customPath)) {
1722
+ if (clack.isCancel(selected)) {
1657
1723
  clack.cancel("Cancelled");
1658
1724
  return;
1659
1725
  }
1660
- bffApiFile = path9.resolve(customPath);
1726
+ bffApiFile = selected;
1661
1727
  }
1662
1728
  const methodLower = httpMethod.toLowerCase();
1663
1729
  const pathSegments = endpointPath.replace(/^\//, "").split("/").filter((s) => !s.startsWith("{"));
@@ -1680,6 +1746,7 @@ async function endpointFlow(project) {
1680
1746
  try {
1681
1747
  await createEndpoint({
1682
1748
  projectRoot: project.projectRoot,
1749
+ projectName: project.projectName,
1683
1750
  httpMethod,
1684
1751
  endpointPath,
1685
1752
  bffApiFile,
@@ -1732,12 +1799,9 @@ async function interactiveMode() {
1732
1799
  await useCaseFlow(project);
1733
1800
  break;
1734
1801
  }
1735
- case "endpoint": {
1736
- const project = await resolveProject();
1737
- if (!project) return;
1738
- await endpointFlow(project);
1802
+ case "endpoint":
1803
+ await endpointFlow();
1739
1804
  break;
1740
- }
1741
1805
  }
1742
1806
  }
1743
1807
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wlmaker",
3
- "version": "1.2.10",
3
+ "version": "1.2.11",
4
4
  "description": "Create Flutter BLoCs with Freezed sealed classes from the terminal",
5
5
  "keywords": [
6
6
  "flutter",