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.
- package/dist/cli.mjs +138 -74
- 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
|
|
982
|
-
import '
|
|
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
|
|
988
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
1199
|
+
modelType
|
|
1193
1200
|
);
|
|
1194
1201
|
injectMethod(bffPath, extractClassName(bffPath), method);
|
|
1195
|
-
const modelImport = `import 'package:${
|
|
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
|
-
|
|
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
|
-
|
|
1224
|
+
entityType,
|
|
1218
1225
|
methodParamsForSignature
|
|
1219
1226
|
);
|
|
1220
1227
|
injectMethod(repoIfacePath, repositoryInterfaceName, ifaceMethod);
|
|
1221
|
-
const entityImport = `import 'package:${
|
|
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
|
-
|
|
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
|
-
|
|
1258
|
+
entityType,
|
|
1251
1259
|
repositoryInterfaceSnake
|
|
1252
1260
|
)
|
|
1253
1261
|
);
|
|
1254
|
-
spinner2("Updating barrel
|
|
1255
|
-
const
|
|
1256
|
-
|
|
1257
|
-
injectExport(
|
|
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
|
-
|
|
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
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
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
|
|
1650
|
-
message: "
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
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(
|
|
1722
|
+
if (clack.isCancel(selected)) {
|
|
1657
1723
|
clack.cancel("Cancelled");
|
|
1658
1724
|
return;
|
|
1659
1725
|
}
|
|
1660
|
-
bffApiFile =
|
|
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
|
-
|
|
1737
|
-
if (!project) return;
|
|
1738
|
-
await endpointFlow(project);
|
|
1802
|
+
case "endpoint":
|
|
1803
|
+
await endpointFlow();
|
|
1739
1804
|
break;
|
|
1740
|
-
}
|
|
1741
1805
|
}
|
|
1742
1806
|
}
|
|
1743
1807
|
|