vona-cli-set-api 1.1.109 → 1.1.114

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.
@@ -0,0 +1,18 @@
1
+ import type { IDecoratorDtoOptions } from 'vona-module-a-web';
2
+
3
+ import { $makeMetadata } from 'vona-module-a-openapiutils';
4
+ import { $Dto } from 'vona-module-a-orm';
5
+ import { Dto } from 'vona-module-a-web';
6
+ import { Model<%=argv.detailResourceNameCapitalize%> } from '<%=argv.detailPackageName%>';
7
+ import { ZovaRender } from 'zova-rest-cabloy-basic-admin';
8
+
9
+ export interface IDtoOptions<%=cli.helper.firstCharToUpperCase(argv.detailDtoBaseName)%> extends IDecoratorDtoOptions {}
10
+
11
+ @Dto<IDtoOptions<%=cli.helper.firstCharToUpperCase(argv.detailDtoBaseName)%>>({
12
+ fields: {
13
+ id: $makeMetadata(ZovaRender.visible(false)),
14
+ },
15
+ })
16
+ export class Dto<%=cli.helper.firstCharToUpperCase(argv.detailDtoBaseName)%> extends $Dto.get(() => Model<%=argv.detailResourceNameCapitalize%>, {
17
+ columns: ['id', 'deleted', 'name', 'description'],
18
+ }) {}
@@ -0,0 +1,17 @@
1
+ import type { IDecoratorDtoOptions } from 'vona-module-a-web';
2
+
3
+ import { $Dto } from 'vona-module-a-orm';
4
+ import { Dto } from 'vona-module-a-web';
5
+ import { Model<%=argv.detailResourceNameCapitalize%> } from '<%=argv.detailPackageName%>';
6
+ import { ZovaRender } from 'zova-rest-cabloy-basic-admin';
7
+
8
+ import { Dto<%=cli.helper.firstCharToUpperCase(argv.detailDtoBaseName)%> } from './<%=argv.detailDtoBaseName%>.tsx';
9
+
10
+ export interface IDtoOptions<%=cli.helper.firstCharToUpperCase(argv.detailDtoMutateName)%> extends IDecoratorDtoOptions {}
11
+
12
+ @Dto<IDtoOptions<%=cli.helper.firstCharToUpperCase(argv.detailDtoMutateName)%>>({
13
+ blocks: [ZovaRender.block('basic-details:blockForm')],
14
+ })
15
+ export class Dto<%=cli.helper.firstCharToUpperCase(argv.detailDtoMutateName)%> extends $Dto.mutate(() => Model<%=argv.detailResourceNameCapitalize%>, {
16
+ dtoClass: Dto<%=cli.helper.firstCharToUpperCase(argv.detailDtoBaseName)%>,
17
+ }) {}
@@ -0,0 +1,60 @@
1
+ import type { IDecoratorDtoOptions } from 'vona-module-a-web';
2
+
3
+ import { $makeMetadata, Api, v } from 'vona-module-a-openapiutils';
4
+ import { $Dto } from 'vona-module-a-orm';
5
+ import { Dto } from 'vona-module-a-web';
6
+ import { Model<%=argv.detailResourceNameCapitalize%> } from '<%=argv.detailPackageName%>';
7
+ import { ZovaRender } from 'zova-rest-cabloy-basic-admin';
8
+
9
+ import { $locale } from '../.metadata/locales.ts';
10
+ import { Dto<%=cli.helper.firstCharToUpperCase(argv.detailDtoBaseName)%> } from './<%=argv.detailDtoBaseName%>.tsx';
11
+
12
+ export interface IDtoOptions<%=cli.helper.firstCharToUpperCase(argv.detailDtoResItemName)%> extends IDecoratorDtoOptions {}
13
+
14
+ @Dto<IDtoOptions<%=cli.helper.firstCharToUpperCase(argv.detailDtoResItemName)%>>({
15
+ blocks: [
16
+ ZovaRender.block('basic-details:blockDetails', {
17
+ blocks: [
18
+ ZovaRender.block('basic-details:blockToolbarBulk', {
19
+ actions: [
20
+ ZovaRender.detailsActionBulk('basic-details:actionCreate', {
21
+ dialogOptions: { title: $locale('Add<%=argv.detailDialogTitleCapitalize%>') },
22
+ permission: { formScene: ['create', 'edit'] },
23
+ }),
24
+ ],
25
+ }),
26
+ ZovaRender.block('basic-details:blockTable'),
27
+ ],
28
+ }),
29
+ ],
30
+ fields: {
31
+ name: $makeMetadata(
32
+ ZovaRender.cell('basic-details:actionView', {
33
+ dialogOptions: { title: $locale('View<%=argv.detailDialogTitleCapitalize%>') },
34
+ }),
35
+ ),
36
+ },
37
+ })
38
+ export class Dto<%=cli.helper.firstCharToUpperCase(argv.detailDtoResItemName)%> extends $Dto.get(() => Model<%=argv.detailResourceNameCapitalize%>, {
39
+ dtoClass: Dto<%=cli.helper.firstCharToUpperCase(argv.detailDtoBaseName)%>,
40
+ }) {
41
+ @Api.field(v.title('#'), ZovaRender.order(1, 'core'), ZovaRender.cell('basic-details:lineNumber'))
42
+ _lineNumber?: number;
43
+
44
+ @Api.field(
45
+ v.title($locale('Operations')),
46
+ ZovaRender.order(1, 'max'),
47
+ ZovaRender.cell('basic-details:actionOperationsRow', {
48
+ actions: [
49
+ ZovaRender.detailsActionRow('basic-details:actionUpdate', {
50
+ dialogOptions: { title: $locale('Edit<%=argv.detailDialogTitleCapitalize%>') },
51
+ permission: { formScene: ['create', 'edit'] },
52
+ }),
53
+ ZovaRender.detailsActionRow('basic-details:actionDelete', {
54
+ permission: { formScene: ['create', 'edit'] },
55
+ }),
56
+ ],
57
+ }),
58
+ )
59
+ _operationsRow?: unknown;
60
+ }
@@ -0,0 +1,17 @@
1
+ import type { IDecoratorDtoOptions } from 'vona-module-a-web';
2
+
3
+ import { $Dto } from 'vona-module-a-orm';
4
+ import { Dto } from 'vona-module-a-web';
5
+ import { Model<%=argv.detailResourceNameCapitalize%> } from '<%=argv.detailPackageName%>';
6
+ import { ZovaRender } from 'zova-rest-cabloy-basic-admin';
7
+
8
+ import { Dto<%=cli.helper.firstCharToUpperCase(argv.detailDtoBaseName)%> } from './<%=argv.detailDtoBaseName%>.tsx';
9
+
10
+ export interface IDtoOptions<%=cli.helper.firstCharToUpperCase(argv.detailDtoViewName)%> extends IDecoratorDtoOptions {}
11
+
12
+ @Dto<IDtoOptions<%=cli.helper.firstCharToUpperCase(argv.detailDtoViewName)%>>({
13
+ blocks: [ZovaRender.block('basic-details:blockForm')],
14
+ })
15
+ export class Dto<%=cli.helper.firstCharToUpperCase(argv.detailDtoViewName)%> extends $Dto.get(() => Model<%=argv.detailResourceNameCapitalize%>, {
16
+ dtoClass: Dto<%=cli.helper.firstCharToUpperCase(argv.detailDtoBaseName)%>,
17
+ }) {}
package/dist/index.js CHANGED
@@ -1067,12 +1067,8 @@ class CliCreateBean extends BeanCliBase {
1067
1067
  argv.ssrSiteRestNpmWeb = fs.existsSync(path.join(argv.projectPath, 'src/suite/cabloy-start')) ? 'zova-rest-cabloy-start-web' : 'zova-rest-cabloy-basic-web';
1068
1068
  // module name/info
1069
1069
  const moduleName = argv.module;
1070
- argv.moduleInfo = this.helper.parseModuleInfo(moduleName);
1071
- // check if exists
1072
- const _module = this.helper.findModule(moduleName);
1073
- if (!_module) {
1074
- throw new Error(`module does not exist: ${moduleName}`);
1075
- }
1070
+ argv.moduleInfo = this.helper.parseModuleInfoCanonical(moduleName);
1071
+ const _module = this.helper.findModuleCanonical(moduleName);
1076
1072
  // target dir
1077
1073
  const targetDir = await this.helper.ensureDir(_module.root);
1078
1074
  // scene name
@@ -1166,12 +1162,9 @@ class CliCreateModule extends BeanCliBase {
1166
1162
  // suite name/info
1167
1163
  const suiteName = argv.suite;
1168
1164
  if (suiteName) {
1169
- argv.suiteInfo = this.helper.parseSuiteInfo(suiteName);
1165
+ argv.suiteInfo = this.helper.parseSuiteInfoCanonical(suiteName);
1170
1166
  // check if exists
1171
- argv._suite = this.helper.findSuite(suiteName);
1172
- if (!argv._suite) {
1173
- throw new Error(`suite does not exist: ${suiteName}`);
1174
- }
1167
+ argv._suite = this.helper.findSuiteCanonical(suiteName);
1175
1168
  }
1176
1169
  // nameMeta
1177
1170
  const nameMeta = this.helper.parseNameMeta(argv.name);
@@ -1179,7 +1172,7 @@ class CliCreateModule extends BeanCliBase {
1179
1172
  argv.name = nameMeta.short;
1180
1173
  // module name/info
1181
1174
  const moduleName = argv.name;
1182
- argv.moduleInfo = this.helper.parseModuleInfo(moduleName);
1175
+ argv.moduleInfo = this.helper.parseModuleInfoCanonical(moduleName);
1183
1176
  argv.relativeNameCapitalize = this.helper.stringToCapitalize(argv.moduleInfo.relativeName, '-');
1184
1177
  // check if exists
1185
1178
  const _module = this.helper.findModule(moduleName);
@@ -1224,6 +1217,12 @@ class CliCreateModule extends BeanCliBase {
1224
1217
  if (!argv.vscode && !argv.ci) {
1225
1218
  await this.helper.pnpmInstall();
1226
1219
  }
1220
+ // tools.metadata
1221
+ if (!argv.nometadata) {
1222
+ await this.helper.invokeCli([':tools:metadata', moduleName], {
1223
+ cwd: argv.projectPath
1224
+ });
1225
+ }
1227
1226
  }
1228
1227
  }
1229
1228
 
@@ -1240,7 +1239,7 @@ class CliCreateSuite extends BeanCliBase {
1240
1239
  argv.name = nameMeta.short;
1241
1240
  // suite name/info
1242
1241
  const suiteName = argv.name;
1243
- argv.suiteInfo = this.helper.parseSuiteInfo(suiteName);
1242
+ argv.suiteInfo = this.helper.parseSuiteInfoCanonical(suiteName);
1244
1243
  // check if exists
1245
1244
  const _suite = this.helper.findSuite(suiteName);
1246
1245
  if (_suite) {
@@ -1268,12 +1267,8 @@ class CliCreateTest extends BeanCliBase {
1268
1267
  await super.execute();
1269
1268
  // module name/info
1270
1269
  const moduleName = argv.module;
1271
- argv.moduleInfo = this.helper.parseModuleInfo(moduleName);
1272
- // check if exists
1273
- const _module = this.helper.findModule(moduleName);
1274
- if (!_module) {
1275
- throw new Error(`module does not exist: ${moduleName}`);
1276
- }
1270
+ argv.moduleInfo = this.helper.parseModuleInfoCanonical(moduleName);
1271
+ const _module = this.helper.findModuleCanonical(moduleName);
1277
1272
  // target dir
1278
1273
  const targetDir = await this.helper.ensureDir(_module.root);
1279
1274
  // name
@@ -1435,12 +1430,8 @@ class CliInitAsset extends BeanCliBase {
1435
1430
  await super.execute();
1436
1431
  // module name/info
1437
1432
  const moduleName = argv.module;
1438
- argv.moduleInfo = this.helper.parseModuleInfo(moduleName);
1439
- // check if exists
1440
- const _module = this.helper.findModule(moduleName);
1441
- if (!_module) {
1442
- throw new Error(`module does not exist: ${moduleName}`);
1443
- }
1433
+ argv.moduleInfo = this.helper.parseModuleInfoCanonical(moduleName);
1434
+ const _module = this.helper.findModuleCanonical(moduleName);
1444
1435
  // target dir
1445
1436
  const targetDir = await this.helper.ensureDir(_module.root);
1446
1437
  // scene
@@ -1464,12 +1455,8 @@ class CliInitConfig extends BeanCliBase {
1464
1455
  // module name/info
1465
1456
  const moduleName = argv._[0];
1466
1457
  if (!moduleName) return;
1467
- argv.moduleInfo = this.helper.parseModuleInfo(moduleName);
1468
- // check if exists
1469
- const _module = this.helper.findModule(moduleName);
1470
- if (!_module) {
1471
- throw new Error(`module does not exist: ${moduleName}`);
1472
- }
1458
+ argv.moduleInfo = this.helper.parseModuleInfoCanonical(moduleName);
1459
+ const _module = this.helper.findModuleCanonical(moduleName);
1473
1460
  // target dir
1474
1461
  const targetDir = await this.helper.ensureDir(_module.root);
1475
1462
  const configFile = path.join(targetDir, 'src/config/config.ts');
@@ -1502,12 +1489,8 @@ class CliInitConstant extends BeanCliBase {
1502
1489
  // module name/info
1503
1490
  const moduleName = argv._[0];
1504
1491
  if (!moduleName) return;
1505
- argv.moduleInfo = this.helper.parseModuleInfo(moduleName);
1506
- // check if exists
1507
- const _module = this.helper.findModule(moduleName);
1508
- if (!_module) {
1509
- throw new Error(`module does not exist: ${moduleName}`);
1510
- }
1492
+ argv.moduleInfo = this.helper.parseModuleInfoCanonical(moduleName);
1493
+ const _module = this.helper.findModuleCanonical(moduleName);
1511
1494
  // target dir
1512
1495
  const targetDir = await this.helper.ensureDir(_module.root);
1513
1496
  const constantFile = path.join(targetDir, 'src/config/constants.ts');
@@ -1540,12 +1523,8 @@ class CliInitError extends BeanCliBase {
1540
1523
  // module name/info
1541
1524
  const moduleName = argv._[0];
1542
1525
  if (!moduleName) return;
1543
- argv.moduleInfo = this.helper.parseModuleInfo(moduleName);
1544
- // check if exists
1545
- const _module = this.helper.findModule(moduleName);
1546
- if (!_module) {
1547
- throw new Error(`module does not exist: ${moduleName}`);
1548
- }
1526
+ argv.moduleInfo = this.helper.parseModuleInfoCanonical(moduleName);
1527
+ const _module = this.helper.findModuleCanonical(moduleName);
1549
1528
  // target dir
1550
1529
  const targetDir = await this.helper.ensureDir(_module.root);
1551
1530
  const errorFile = path.join(targetDir, 'src/config/errors.ts');
@@ -1589,12 +1568,8 @@ class CliInitLib extends BeanCliBase {
1589
1568
  // module name/info
1590
1569
  const moduleName = argv._[0];
1591
1570
  if (!moduleName) return;
1592
- argv.moduleInfo = this.helper.parseModuleInfo(moduleName);
1593
- // check if exists
1594
- const _module = this.helper.findModule(moduleName);
1595
- if (!_module) {
1596
- throw new Error(`module does not exist: ${moduleName}`);
1597
- }
1571
+ argv.moduleInfo = this.helper.parseModuleInfoCanonical(moduleName);
1572
+ const _module = this.helper.findModuleCanonical(moduleName);
1598
1573
  // target dir
1599
1574
  const targetDir = await this.helper.ensureDir(_module.root);
1600
1575
  const mainFile = path.join(targetDir, 'src/lib/index.ts');
@@ -1627,12 +1602,8 @@ class CliInitLocale extends BeanCliBase {
1627
1602
  // module name/info
1628
1603
  const moduleName = argv._[0];
1629
1604
  if (!moduleName) return;
1630
- argv.moduleInfo = this.helper.parseModuleInfo(moduleName);
1631
- // check if exists
1632
- const _module = this.helper.findModule(moduleName);
1633
- if (!_module) {
1634
- throw new Error(`module does not exist: ${moduleName}`);
1635
- }
1605
+ argv.moduleInfo = this.helper.parseModuleInfoCanonical(moduleName);
1606
+ const _module = this.helper.findModuleCanonical(moduleName);
1636
1607
  // target dir
1637
1608
  const targetDir = await this.helper.ensureDir(_module.root);
1638
1609
  const localeFile = path.join(targetDir, 'src/config/locale');
@@ -1665,12 +1636,8 @@ class CliInitMain extends BeanCliBase {
1665
1636
  // module name/info
1666
1637
  const moduleName = argv._[0];
1667
1638
  if (!moduleName) return;
1668
- argv.moduleInfo = this.helper.parseModuleInfo(moduleName);
1669
- // check if exists
1670
- const _module = this.helper.findModule(moduleName);
1671
- if (!_module) {
1672
- throw new Error(`module does not exist: ${moduleName}`);
1673
- }
1639
+ argv.moduleInfo = this.helper.parseModuleInfoCanonical(moduleName);
1640
+ const _module = this.helper.findModuleCanonical(moduleName);
1674
1641
  // target dir
1675
1642
  const targetDir = await this.helper.ensureDir(_module.root);
1676
1643
  const mainFile = path.join(targetDir, 'src/main.ts');
@@ -1703,12 +1670,8 @@ class CliInitMonkey extends BeanCliBase {
1703
1670
  // module name/info
1704
1671
  const moduleName = argv._[0];
1705
1672
  if (!moduleName) return;
1706
- argv.moduleInfo = this.helper.parseModuleInfo(moduleName);
1707
- // check if exists
1708
- const _module = this.helper.findModule(moduleName);
1709
- if (!_module) {
1710
- throw new Error(`module does not exist: ${moduleName}`);
1711
- }
1673
+ argv.moduleInfo = this.helper.parseModuleInfoCanonical(moduleName);
1674
+ const _module = this.helper.findModuleCanonical(moduleName);
1712
1675
  // target dir
1713
1676
  const targetDir = await this.helper.ensureDir(_module.root);
1714
1677
  const monkeyFile = path.join(targetDir, 'src/monkey.ts');
@@ -1765,12 +1728,8 @@ class CliInitStatic extends BeanCliBase {
1765
1728
  // module name/info
1766
1729
  const moduleName = argv._[0];
1767
1730
  if (!moduleName) return;
1768
- argv.moduleInfo = this.helper.parseModuleInfo(moduleName);
1769
- // check if exists
1770
- const _module = this.helper.findModule(moduleName);
1771
- if (!_module) {
1772
- throw new Error(`module does not exist: ${moduleName}`);
1773
- }
1731
+ argv.moduleInfo = this.helper.parseModuleInfoCanonical(moduleName);
1732
+ const _module = this.helper.findModuleCanonical(moduleName);
1774
1733
  // target dir
1775
1734
  const targetDir = await this.helper.ensureDir(_module.root);
1776
1735
  const staticDir = path.join(targetDir, 'assets/static');
@@ -1801,12 +1760,8 @@ class CliInitTypes extends BeanCliBase {
1801
1760
  // module name/info
1802
1761
  const moduleName = argv._[0];
1803
1762
  if (!moduleName) return;
1804
- argv.moduleInfo = this.helper.parseModuleInfo(moduleName);
1805
- // check if exists
1806
- const _module = this.helper.findModule(moduleName);
1807
- if (!_module) {
1808
- throw new Error(`module does not exist: ${moduleName}`);
1809
- }
1763
+ argv.moduleInfo = this.helper.parseModuleInfoCanonical(moduleName);
1764
+ const _module = this.helper.findModuleCanonical(moduleName);
1810
1765
  // target dir
1811
1766
  const targetDir = await this.helper.ensureDir(_module.root);
1812
1767
  const mainFile = path.join(targetDir, 'src/types/index.ts');
@@ -1843,12 +1798,8 @@ class CliToolsCrud extends BeanCliBase {
1843
1798
  argv.ssrSiteGroupName = isCabloyStart ? 'start-siteadmin:management' : 'basic-siteadmin:management';
1844
1799
  // module name/info
1845
1800
  const moduleName = argv.module;
1846
- argv.moduleInfo = this.helper.parseModuleInfo(moduleName);
1847
- // check if exists
1848
- const _module = this.helper.findModule(moduleName);
1849
- if (!_module) {
1850
- throw new Error(`module does not exist: ${moduleName}`);
1851
- }
1801
+ argv.moduleInfo = this.helper.parseModuleInfoCanonical(moduleName);
1802
+ const _module = this.helper.findModuleCanonical(moduleName);
1852
1803
  // target dir
1853
1804
  const targetDir = await this.helper.ensureDir(_module.root);
1854
1805
  // resourceName
@@ -1889,12 +1840,8 @@ class CliToolsCrudBasic extends BeanCliBase {
1889
1840
  await super.execute();
1890
1841
  // module name/info
1891
1842
  const moduleName = argv.module;
1892
- argv.moduleInfo = this.helper.parseModuleInfo(moduleName);
1893
- // check if exists
1894
- const _module = this.helper.findModule(moduleName);
1895
- if (!_module) {
1896
- throw new Error(`module does not exist: ${moduleName}`);
1897
- }
1843
+ argv.moduleInfo = this.helper.parseModuleInfoCanonical(moduleName);
1844
+ const _module = this.helper.findModuleCanonical(moduleName);
1898
1845
  // target dir
1899
1846
  const targetDir = await this.helper.ensureDir(_module.root);
1900
1847
  // resourceName
@@ -1933,12 +1880,8 @@ class CliToolsCrudStart extends BeanCliBase {
1933
1880
  await super.execute();
1934
1881
  // module name/info
1935
1882
  const moduleName = argv.module;
1936
- argv.moduleInfo = this.helper.parseModuleInfo(moduleName);
1937
- // check if exists
1938
- const _module = this.helper.findModule(moduleName);
1939
- if (!_module) {
1940
- throw new Error(`module does not exist: ${moduleName}`);
1941
- }
1883
+ argv.moduleInfo = this.helper.parseModuleInfoCanonical(moduleName);
1884
+ const _module = this.helper.findModuleCanonical(moduleName);
1942
1885
  // target dir
1943
1886
  const targetDir = await this.helper.ensureDir(_module.root);
1944
1887
  // resourceName
@@ -2043,6 +1986,529 @@ class CliToolsDeps extends BeanCliBase {
2043
1986
  }
2044
1987
  }
2045
1988
 
1989
+ class CliToolsMasterDetail extends BeanCliBase {
1990
+ async execute() {
1991
+ await super.execute();
1992
+ this._prepareArgv();
1993
+ await this._ensureDetailModule();
1994
+ await this._ensureDetailResourceShape();
1995
+ await this._patchDetailModule();
1996
+ await this._renderMasterDetailDtos();
1997
+ await this._patchMasterModule();
1998
+ await this._refreshMetadata();
1999
+ }
2000
+ _prepareArgv() {
2001
+ const {
2002
+ argv
2003
+ } = this.context;
2004
+ argv.detailMode = argv.detailMode || 'aggregate';
2005
+ if (argv.detailMode !== 'aggregate' && argv.detailMode !== 'standalone') {
2006
+ throw new Error(`mode is not valid: ${argv.detailMode}`);
2007
+ }
2008
+ argv.moduleInfo = this.helper.parseModuleInfoCanonical(argv.module, 'master module');
2009
+ const _module = this.helper.findModuleCanonical(argv.module, 'master module');
2010
+ argv._module = _module;
2011
+ argv.resourceNameCapitalize = this.helper.firstCharToUpperCase(argv.resourceName);
2012
+ argv.detailModuleInfo = this.helper.parseModuleInfoCanonical(argv.detailModule, 'detail module');
2013
+ argv._detailModule = this.helper.findModule(argv.detailModule);
2014
+ argv.detailResourceNameCapitalize = this.helper.firstCharToUpperCase(argv.detailResourceName);
2015
+ argv.relationName = argv.relationName || `${argv.detailResourceName}s`;
2016
+ argv.relationNameCapitalize = this.helper.firstCharToUpperCase(argv.relationName);
2017
+ argv.fk = argv.fk || `${argv.resourceName}Id`;
2018
+ argv.detailPackageName = `vona-module-${argv.detailModuleInfo.relativeName}`;
2019
+ argv.detailModuleCapitalize = this.helper.stringToCapitalize(argv.detailModuleInfo.relativeName, '-');
2020
+ argv.detailDtoBaseName = `detail${argv.detailResourceNameCapitalize}Base`;
2021
+ argv.detailDtoMutateName = `detail${argv.detailResourceNameCapitalize}Mutate`;
2022
+ argv.detailDtoViewName = `detail${argv.detailResourceNameCapitalize}View`;
2023
+ argv.detailDtoResItemName = `detail${argv.detailResourceNameCapitalize}ResItem`;
2024
+ argv.detailFieldPrivateName = `_${argv.relationName}`;
2025
+ argv.detailDialogTitleCapitalize = argv.detailModuleCapitalize;
2026
+ }
2027
+ async _ensureDetailModule() {
2028
+ const {
2029
+ argv
2030
+ } = this.context;
2031
+ if (argv._detailModule) return;
2032
+ const args = [':create:module', argv.detailModule, '--nometadata'];
2033
+ if (argv._module.suite) {
2034
+ args.push(`--suite=${argv._module.suite}`);
2035
+ }
2036
+ await this.helper.invokeCli(args, {
2037
+ cwd: argv.projectPath
2038
+ });
2039
+ argv.createdDetailModule = true;
2040
+ argv._detailModule = this.helper.findModule(argv.detailModule);
2041
+ if (!argv._detailModule) {
2042
+ const root = this._resolveCreatedDetailModuleRoot();
2043
+ argv._detailModule = {
2044
+ name: argv.detailModuleInfo.relativeName,
2045
+ info: argv.detailModuleInfo,
2046
+ root,
2047
+ pkg: path.join(root, 'package.json'),
2048
+ package: await this.helper.loadJSONFile(path.join(root, 'package.json')),
2049
+ suite: argv._module.suite
2050
+ };
2051
+ }
2052
+ }
2053
+ async _ensureDetailResourceShape() {
2054
+ const {
2055
+ argv
2056
+ } = this.context;
2057
+ const {
2058
+ hasCoreDetailFiles,
2059
+ hasStandaloneSurface
2060
+ } = this._detailModuleShape();
2061
+ if (!hasCoreDetailFiles) {
2062
+ await this._createDetailCoreResource();
2063
+ argv.createdDetailResource = true;
2064
+ }
2065
+ if (argv.detailMode === 'aggregate') {
2066
+ this._handleAggregateDetailMode(hasStandaloneSurface);
2067
+ return;
2068
+ }
2069
+ this._handleStandaloneDetailMode();
2070
+ }
2071
+ async _patchDetailModule() {
2072
+ await this._runSequential(this._detailPatchTasks());
2073
+ }
2074
+ async _patchDetailEntity() {
2075
+ const {
2076
+ argv
2077
+ } = this.context;
2078
+ await this._patchFileContent({
2079
+ fileName: this._detailPaths().entity,
2080
+ existsNeedle: `${argv.fk}: TableIdentity;`,
2081
+ patch: (content, fileName) => {
2082
+ content = this._patchDetailEntityImport(content);
2083
+ return this._patchDetailEntityField(content, fileName);
2084
+ }
2085
+ });
2086
+ }
2087
+ _patchDetailEntityImport(content) {
2088
+ if (!content.includes("import type { TableIdentity } from 'table-identity';")) {
2089
+ content = `import type { TableIdentity } from 'table-identity';\n${content}`;
2090
+ }
2091
+ return content;
2092
+ }
2093
+ _patchDetailEntityField(content, fileName) {
2094
+ const {
2095
+ argv
2096
+ } = this.context;
2097
+ const marker = `export class Entity${argv.detailResourceNameCapitalize} extends EntityBase {\n`;
2098
+ if (!content.includes(marker)) {
2099
+ throw new Error(`detail entity is not in the expected shape: ${fileName}`);
2100
+ }
2101
+ const fieldCode = ` @Api.field(v.required(), ZovaRender.visible(false))\n ${argv.fk}: TableIdentity;\n\n`;
2102
+ return content.replace(marker, `${marker}${fieldCode}`);
2103
+ }
2104
+ async _patchDetailMetaVersion() {
2105
+ const {
2106
+ argv
2107
+ } = this.context;
2108
+ await this._patchFileByMarker({
2109
+ fileName: this._detailPaths().metaVersion,
2110
+ existsNeedle: `entity${argv.detailResourceNameCapitalize}.${argv.fk}`,
2111
+ marker: ' table.basicFields();\n',
2112
+ line: this._detailMetaVersionLine(),
2113
+ shapeName: 'detail meta.version'
2114
+ });
2115
+ }
2116
+ async _patchDetailMetaIndex() {
2117
+ const {
2118
+ argv
2119
+ } = this.context;
2120
+ await this._patchFileByMarker({
2121
+ fileName: this._detailPaths().metaIndex,
2122
+ existsNeedle: `'${argv.fk}'`,
2123
+ marker: ' indexes: {\n',
2124
+ line: this._detailMetaIndexLine(),
2125
+ shapeName: 'detail meta.index'
2126
+ });
2127
+ }
2128
+ _detailMetaVersionLine() {
2129
+ const {
2130
+ argv
2131
+ } = this.context;
2132
+ return ` table.tableIdentity(entity${argv.detailResourceNameCapitalize}.${argv.fk}).comment(entity${argv.detailResourceNameCapitalize}.$comment.${argv.fk});\n`;
2133
+ }
2134
+ _detailMetaIndexLine() {
2135
+ const {
2136
+ argv
2137
+ } = this.context;
2138
+ return ` ...$tableColumns('${this.helper.combineModuleNameAndResource(argv.detailModuleInfo.relativeName, argv.detailResourceName)}', '${argv.fk}'),\n`;
2139
+ }
2140
+ async _patchFileByMarker({
2141
+ fileName,
2142
+ existsNeedle,
2143
+ marker,
2144
+ line,
2145
+ shapeName
2146
+ }) {
2147
+ let content = this._readFile(fileName);
2148
+ if (content.includes(existsNeedle)) return;
2149
+ if (!content.includes(marker)) {
2150
+ throw new Error(`${shapeName} is not in the expected shape: ${fileName}`);
2151
+ }
2152
+ content = content.replace(marker, `${marker}${line}`);
2153
+ await this._saveFile(fileName, content);
2154
+ }
2155
+ async _patchFileContent({
2156
+ fileName,
2157
+ existsNeedle,
2158
+ patch
2159
+ }) {
2160
+ let content = this._readFile(fileName);
2161
+ if (content.includes(existsNeedle)) return;
2162
+ content = patch(content, fileName);
2163
+ await this._saveFile(fileName, content);
2164
+ }
2165
+ async _renderMasterDetailDtos() {
2166
+ await this.helper.ensureDir(this._masterPaths().dtoDir);
2167
+ for (const [dtoBaseName, templateBaseName] of this._detailDtoTemplates()) {
2168
+ await this._renderMasterDetailDtoFile(dtoBaseName, templateBaseName);
2169
+ }
2170
+ }
2171
+ async _patchMasterModule() {
2172
+ await this._runSequential(this._masterPatchTasks());
2173
+ }
2174
+ _detailPatchTasks() {
2175
+ return [() => this._patchDetailEntity(), () => this._patchDetailMetaVersion(), () => this._patchDetailMetaIndex()];
2176
+ }
2177
+ _masterPatchTasks() {
2178
+ return [() => this._patchMasterModel(), () => this._patchMasterService(), ...this._masterDtoScenes().map(scene => () => this._patchMasterDto(scene)), ...this._masterLocales().map(locale => () => this._patchMasterLocale(locale))];
2179
+ }
2180
+ async _runSequential(tasks) {
2181
+ for (const task of tasks) {
2182
+ await task();
2183
+ }
2184
+ }
2185
+ async _patchMasterModel() {
2186
+ const {
2187
+ argv
2188
+ } = this.context;
2189
+ const fileName = this._masterPaths().model;
2190
+ let content = this._readFile(fileName);
2191
+ if (content.includes(`${argv.relationName}: $relation.hasMany(`)) return;
2192
+ content = this._patchMasterModelImport(content, fileName);
2193
+ content = this._patchMasterModelRelations(content, fileName);
2194
+ await this._saveFile(fileName, content);
2195
+ }
2196
+ async _patchMasterService() {
2197
+ const {
2198
+ argv
2199
+ } = this.context;
2200
+ const fileName = this._masterPaths().service;
2201
+ let content = this._readFile(fileName);
2202
+ if (content.includes(`include: { ${argv.relationName}: true }`)) return;
2203
+ for (const [search, replacement] of this._masterServiceIncludeReplacements()) {
2204
+ content = this._replaceStrict(content, search, replacement, fileName);
2205
+ }
2206
+ await this._saveFile(fileName, content);
2207
+ }
2208
+ _patchMasterModelImport(content, fileName) {
2209
+ if (content.includes("import { $relation, BeanModelBase, Model } from 'vona-module-a-orm';")) {
2210
+ return content;
2211
+ }
2212
+ return this._replaceStrict(content, "import { BeanModelBase, Model } from 'vona-module-a-orm';", "import { $relation, BeanModelBase, Model } from 'vona-module-a-orm';", fileName);
2213
+ }
2214
+ _patchMasterModelRelations(content, fileName) {
2215
+ const relationsBlockMarker = ' relations: {\n';
2216
+ if (content.includes(relationsBlockMarker)) {
2217
+ return content.replace(relationsBlockMarker, `${relationsBlockMarker}${this._masterModelRelationCode()}`);
2218
+ }
2219
+ return this._patchMasterModelDecorator(content, fileName);
2220
+ }
2221
+ _patchMasterModelDecorator(content, fileName) {
2222
+ const {
2223
+ argv
2224
+ } = this.context;
2225
+ const marker = `@Model<IModelOptions${argv.resourceNameCapitalize}>({ entity: Entity${argv.resourceNameCapitalize} })`;
2226
+ if (!content.includes(marker)) {
2227
+ throw new Error(`master model is not in the expected shape: ${fileName}`);
2228
+ }
2229
+ return content.replace(marker, `@Model<IModelOptions${argv.resourceNameCapitalize}>({\n entity: Entity${argv.resourceNameCapitalize},\n relations: {\n${this._masterModelRelationCode()} },\n})`);
2230
+ }
2231
+ _masterModelRelationCode() {
2232
+ const {
2233
+ argv
2234
+ } = this.context;
2235
+ return ` ${argv.relationName}: $relation.hasMany('${argv.detailModuleInfo.relativeName}:${argv.detailResourceName}', '${argv.fk}', {\n columns: ['id', 'name', 'description'],\n }),\n`;
2236
+ }
2237
+ _masterServiceIncludeReplacements() {
2238
+ const {
2239
+ argv
2240
+ } = this.context;
2241
+ return [[` return await this.scope.model.${argv.resourceName}.insert(${argv.resourceName});`, ` return await this.scope.model.${argv.resourceName}.insert(${argv.resourceName}, { include: { ${argv.relationName}: true } });`], [` return await this.scope.model.${argv.resourceName}.getById(id);`, ` return await this.scope.model.${argv.resourceName}.getById(id, { include: { ${argv.relationName}: true } });`], [` return await this.scope.model.${argv.resourceName}.updateById(id, ${argv.resourceName});`, ` return await this.scope.model.${argv.resourceName}.updateById(id, ${argv.resourceName}, {\n include: { ${argv.relationName}: true },\n });`], [` return await this.scope.model.${argv.resourceName}.deleteById(id);`, ` return await this.scope.model.${argv.resourceName}.deleteById(id, { include: { ${argv.relationName}: true } });`]];
2242
+ }
2243
+ async _patchMasterDto(scene) {
2244
+ const {
2245
+ argv
2246
+ } = this.context;
2247
+ const fileName = path.join(argv._module.root, 'src/dto', `${argv.resourceName}${scene}.tsx`);
2248
+ let content = this._readFile(fileName);
2249
+ const detailDtoResItemCapitalize = this._detailDtoResItemCapitalize();
2250
+ const detailDtoClassCapitalize = this._detailDtoClassCapitalize(scene);
2251
+ if (content.includes(`${argv.detailFieldPrivateName}?: Dto${detailDtoResItemCapitalize}[];`)) {
2252
+ return;
2253
+ }
2254
+ content = this._patchMasterDtoImports(content, fileName, scene, detailDtoClassCapitalize, detailDtoResItemCapitalize);
2255
+ content = this._patchMasterDtoFields(content, fileName, scene);
2256
+ content = this._patchMasterDtoClass(content, fileName, scene, detailDtoClassCapitalize, detailDtoResItemCapitalize);
2257
+ await this._saveFile(fileName, content);
2258
+ }
2259
+ async _patchMasterLocale(locale) {
2260
+ const fileName = path.join(this._masterPaths().localeDir, `${locale}.ts`);
2261
+ let content = this._readFile(fileName);
2262
+ content = this._patchLocaleAdditions(content, this._masterLocaleAdditions(locale));
2263
+ await this._saveFile(fileName, content);
2264
+ }
2265
+ async _refreshMetadata() {
2266
+ const {
2267
+ argv
2268
+ } = this.context;
2269
+ for (const moduleName of this._metadataModules()) {
2270
+ await this.helper.invokeCli([':tools:metadata', moduleName], {
2271
+ cwd: argv.projectPath
2272
+ });
2273
+ }
2274
+ }
2275
+ _patchLocaleAdditions(content, additions) {
2276
+ for (const addition of additions) {
2277
+ const key = this._localeAdditionKey(addition);
2278
+ if (content.includes(`${key}:`)) continue;
2279
+ content = content.replace('};\n', ` ${addition},\n};\n`);
2280
+ }
2281
+ return content;
2282
+ }
2283
+ _localeAdditionKey(addition) {
2284
+ return addition.split(':')[0];
2285
+ }
2286
+ _metadataModules() {
2287
+ const {
2288
+ argv
2289
+ } = this.context;
2290
+ return [argv.detailModule, argv.module];
2291
+ }
2292
+ async _renderMasterDetailDtoFile(dtoBaseName, templateBaseName) {
2293
+ const targetFile = path.join(this._masterPaths().dtoDir, `${dtoBaseName}.tsx`);
2294
+ if (fs.existsSync(targetFile)) return;
2295
+ const templateFile = this.template.resolveTemplatePath(__ThisSetName__, `tools/masterDetail/boilerplate/dto/${templateBaseName}`);
2296
+ const templateContent = fs.readFileSync(templateFile).toString('utf8');
2297
+ const renderedContent = await this.template.renderContent({
2298
+ content: templateContent
2299
+ });
2300
+ await this._saveFile(targetFile, renderedContent);
2301
+ }
2302
+ _patchMasterDtoImports(content, fileName, scene, detailDtoClassCapitalize, detailDtoResItemCapitalize) {
2303
+ const {
2304
+ argv
2305
+ } = this.context;
2306
+ if (!content.includes("import { $makeMetadata, Api, v } from 'vona-module-a-openapiutils';")) {
2307
+ content = this._replaceStrict(content, "import { $Dto } from 'vona-module-a-orm';", "import { $makeMetadata, Api, v } from 'vona-module-a-openapiutils';\nimport { $Dto } from 'vona-module-a-orm';", fileName);
2308
+ }
2309
+ if (!content.includes("import { $locale } from '../.metadata/locales.ts';")) {
2310
+ content = this._replaceStrict(content, "import { ZovaRender } from 'zova-rest-cabloy-basic-admin';\n", "import { ZovaRender } from 'zova-rest-cabloy-basic-admin';\n\nimport { $locale } from '../.metadata/locales.ts';\n", fileName);
2311
+ }
2312
+ const importMarker = `import { Model${argv.resourceNameCapitalize} } from '../model/${argv.resourceName}.ts';\n`;
2313
+ if (!content.includes(`import { Dto${detailDtoClassCapitalize} } from './`)) {
2314
+ content = this._replaceStrict(content, importMarker, `${importMarker}${this._masterDtoDetailImports(scene, detailDtoClassCapitalize, detailDtoResItemCapitalize)}`, fileName);
2315
+ }
2316
+ return content;
2317
+ }
2318
+ _patchMasterDtoFields(content, fileName, scene) {
2319
+ const fieldCode = this._masterDtoFieldCode(scene);
2320
+ if (content.includes(' fields: {\n')) {
2321
+ return content.replace(' fields: {\n', ` fields: {\n${fieldCode}`);
2322
+ }
2323
+ const decoratorMarker = ' ],\n})';
2324
+ if (!content.includes(decoratorMarker)) {
2325
+ throw new Error(`master dto decorator is not in the expected shape: ${fileName}`);
2326
+ }
2327
+ return content.replace(decoratorMarker, ` ],\n fields: {\n${fieldCode} },\n})`);
2328
+ }
2329
+ _patchMasterDtoClass(content, fileName, scene, detailDtoClassCapitalize, detailDtoResItemCapitalize) {
2330
+ const classReplaceSource = this._masterDtoClassReplaceSource(scene);
2331
+ if (!content.includes(classReplaceSource)) {
2332
+ throw new Error(`master dto class is not in the expected generated shape: ${fileName}`);
2333
+ }
2334
+ return content.replace(classReplaceSource, this._masterDtoClassReplaceTarget(scene, detailDtoClassCapitalize, detailDtoResItemCapitalize));
2335
+ }
2336
+ _masterDtoDetailImports(scene, detailDtoClassCapitalize, detailDtoResItemCapitalize) {
2337
+ const {
2338
+ argv
2339
+ } = this.context;
2340
+ return `import { Dto${detailDtoClassCapitalize} } from './${scene === 'View' ? argv.detailDtoViewName : argv.detailDtoMutateName}.tsx';\nimport { Dto${detailDtoResItemCapitalize} } from './${argv.detailDtoResItemName}.tsx';\n`;
2341
+ }
2342
+ _masterDtoFieldCode(scene) {
2343
+ const {
2344
+ argv
2345
+ } = this.context;
2346
+ return ` ${argv.relationName}: $makeMetadata(\n v.title($locale('${argv.relationNameCapitalize}')),\n ZovaRender.order(5),\n ZovaRender.field('basic-details:formFieldDetails'),\n${scene === 'Create' ? ' v.optional(),\n' : ''} ),\n`;
2347
+ }
2348
+ _masterDtoClassReplaceSource(scene) {
2349
+ const {
2350
+ argv
2351
+ } = this.context;
2352
+ const kind = scene === 'Create' ? 'create' : scene === 'Update' ? 'update' : 'get';
2353
+ return `export class Dto${argv.resourceNameCapitalize}${scene} extends $Dto.${kind}(() => Model${argv.resourceNameCapitalize}) {}`;
2354
+ }
2355
+ _masterDtoClassReplaceTarget(scene, detailDtoClassCapitalize, detailDtoResItemCapitalize) {
2356
+ const {
2357
+ argv
2358
+ } = this.context;
2359
+ const kind = scene === 'Create' ? 'create' : scene === 'Update' ? 'update' : 'get';
2360
+ return `export class Dto${argv.resourceNameCapitalize}${scene} extends $Dto.${kind}(() => Model${argv.resourceNameCapitalize}, {\n include: { ${argv.relationName}: { dtoClass: Dto${detailDtoClassCapitalize} } },\n}) {\n @Api.field(ZovaRender.visible(false), v.optional(), v.array(Dto${detailDtoResItemCapitalize}))\n ${argv.detailFieldPrivateName}?: Dto${detailDtoResItemCapitalize}[];\n}`;
2361
+ }
2362
+ _detailDtoClassCapitalize(scene) {
2363
+ const {
2364
+ argv
2365
+ } = this.context;
2366
+ return scene === 'View' ? this._capitalize(argv.detailDtoViewName) : this._capitalize(argv.detailDtoMutateName);
2367
+ }
2368
+ _masterLocaleAdditions(locale) {
2369
+ const {
2370
+ argv
2371
+ } = this.context;
2372
+ if (locale === 'en-us') {
2373
+ return [`${argv.relationNameCapitalize}: '${this._titleize(argv.relationName)}'`, `Add${argv.detailDialogTitleCapitalize}: 'Add ${this._titleize(argv.detailModuleInfo.relativeName)}'`, `Edit${argv.detailDialogTitleCapitalize}: 'Edit ${this._titleize(argv.detailModuleInfo.relativeName)}'`, `View${argv.detailDialogTitleCapitalize}: 'View ${this._titleize(argv.detailModuleInfo.relativeName)}'`];
2374
+ }
2375
+ return [`${argv.relationNameCapitalize}: '明细'`, `Add${argv.detailDialogTitleCapitalize}: '添加明细'`, `Edit${argv.detailDialogTitleCapitalize}: '编辑明细'`, `View${argv.detailDialogTitleCapitalize}: '查看明细'`];
2376
+ }
2377
+ async _createDetailCoreResource() {
2378
+ const {
2379
+ argv
2380
+ } = this.context;
2381
+ await this.helper.invokeCli([':tools:crudBasic', argv.detailResourceName, `--module=${argv.detailModule}`, '--nometadata'], {
2382
+ cwd: argv.projectPath
2383
+ });
2384
+ }
2385
+ _handleAggregateDetailMode(hasStandaloneSurface) {
2386
+ const {
2387
+ argv
2388
+ } = this.context;
2389
+ if (hasStandaloneSurface && !argv.createdDetailResource) {
2390
+ throw new Error(`detail module already has a standalone resource surface: ${argv.detailModule}:${argv.detailResourceName}`);
2391
+ }
2392
+ if (this._hasStandaloneDetailSurface()) {
2393
+ this._removeStandaloneDetailSurface();
2394
+ }
2395
+ }
2396
+ _handleStandaloneDetailMode() {
2397
+ const {
2398
+ argv
2399
+ } = this.context;
2400
+ if (!this._hasStandaloneDetailSurface()) {
2401
+ throw new Error(`detail module does not have a standalone resource surface: ${argv.detailModule}:${argv.detailResourceName}`);
2402
+ }
2403
+ }
2404
+ _detailModuleShape() {
2405
+ const files = this._detailPaths();
2406
+ return {
2407
+ hasCoreDetailFiles: fs.existsSync(files.entity) && fs.existsSync(files.model),
2408
+ hasStandaloneSurface: this._hasStandaloneDetailSurface()
2409
+ };
2410
+ }
2411
+ _masterPaths() {
2412
+ const {
2413
+ argv
2414
+ } = this.context;
2415
+ const masterRoot = argv._module.root;
2416
+ return {
2417
+ root: masterRoot,
2418
+ model: path.join(masterRoot, 'src/model', `${argv.resourceName}.ts`),
2419
+ service: path.join(masterRoot, 'src/service', `${argv.resourceName}.ts`),
2420
+ dtoDir: path.join(masterRoot, 'src/dto'),
2421
+ localeDir: path.join(masterRoot, 'src/config/locale')
2422
+ };
2423
+ }
2424
+ _detailPaths() {
2425
+ const {
2426
+ argv
2427
+ } = this.context;
2428
+ const detailRoot = argv._detailModule.root;
2429
+ return {
2430
+ root: detailRoot,
2431
+ entity: path.join(detailRoot, 'src/entity', `${argv.detailResourceName}.tsx`),
2432
+ model: path.join(detailRoot, 'src/model', `${argv.detailResourceName}.ts`),
2433
+ controller: path.join(detailRoot, 'src/controller', `${argv.detailResourceName}.ts`),
2434
+ service: path.join(detailRoot, 'src/service', `${argv.detailResourceName}.ts`),
2435
+ dtoDir: path.join(detailRoot, 'src/dto'),
2436
+ test: path.join(detailRoot, 'test', `${argv.detailResourceName}.test.ts`),
2437
+ metaVersion: path.join(detailRoot, 'src/bean', 'meta.version.ts'),
2438
+ metaIndex: path.join(detailRoot, 'src/bean', 'meta.index.ts')
2439
+ };
2440
+ }
2441
+ _hasStandaloneDetailSurface() {
2442
+ const files = this._detailPaths();
2443
+ return fs.existsSync(files.controller) || fs.existsSync(files.service) || fs.existsSync(files.dtoDir);
2444
+ }
2445
+ _removeStandaloneDetailSurface() {
2446
+ const files = this._detailPaths();
2447
+ for (const file of [files.controller, files.service, files.test]) {
2448
+ if (fs.existsSync(file)) {
2449
+ fs.rmSync(file, {
2450
+ force: true
2451
+ });
2452
+ }
2453
+ }
2454
+ if (fs.existsSync(files.dtoDir)) {
2455
+ fs.rmSync(files.dtoDir, {
2456
+ recursive: true,
2457
+ force: true
2458
+ });
2459
+ }
2460
+ }
2461
+ _detailDtoResItemCapitalize() {
2462
+ return this._capitalize(this.context.argv.detailDtoResItemName);
2463
+ }
2464
+ _detailDtoTemplates() {
2465
+ const {
2466
+ argv
2467
+ } = this.context;
2468
+ return [[argv.detailDtoBaseName, '<%=argv.detailDtoBaseName%>.tsx_'], [argv.detailDtoMutateName, '<%=argv.detailDtoMutateName%>.tsx_'], [argv.detailDtoViewName, '<%=argv.detailDtoViewName%>.tsx_'], [argv.detailDtoResItemName, '<%=argv.detailDtoResItemName%>.tsx_']];
2469
+ }
2470
+ _masterDtoScenes() {
2471
+ return ['Create', 'Update', 'View'];
2472
+ }
2473
+ _masterLocales() {
2474
+ return ['en-us', 'zh-cn'];
2475
+ }
2476
+ _resolveCreatedDetailModuleRoot() {
2477
+ const {
2478
+ argv
2479
+ } = this.context;
2480
+ if (argv._module.suite) {
2481
+ return path.join(argv.projectPath, 'src/suite', argv._module.suite, 'modules', argv.detailModule);
2482
+ }
2483
+ return path.join(argv.projectPath, 'src/module', argv.detailModule);
2484
+ }
2485
+ _capitalize(name) {
2486
+ return this.helper.firstCharToUpperCase(name);
2487
+ }
2488
+ _titleize(name) {
2489
+ return this.helper.stringToCapitalize(name, '-').replace(/([A-Z])/g, ' $1').trim();
2490
+ }
2491
+ _readFile(fileName) {
2492
+ if (!fs.existsSync(fileName)) {
2493
+ throw new Error(`file does not exist: ${fileName}`);
2494
+ }
2495
+ return fs.readFileSync(fileName).toString('utf8');
2496
+ }
2497
+ async _saveFile(fileName, content) {
2498
+ fs.writeFileSync(fileName, content);
2499
+ await this.helper.formatFile({
2500
+ fileName,
2501
+ logPrefix: 'format: '
2502
+ });
2503
+ }
2504
+ _replaceStrict(content, search, replacement, fileName) {
2505
+ if (!content.includes(search)) {
2506
+ throw new Error(`file is not in the expected shape: ${fileName}`);
2507
+ }
2508
+ return content.replace(search, replacement);
2509
+ }
2510
+ }
2511
+
2046
2512
  function checkIgnoreOfParts(parts) {
2047
2513
  const indexLast = parts.length - 1;
2048
2514
  if (parts[indexLast].endsWith('_')) {
@@ -2536,8 +3002,7 @@ class CliToolsMetadata extends BeanCliBase {
2536
3002
  }
2537
3003
  }
2538
3004
  async _generateMetadata(moduleName, force) {
2539
- const module = this.helper.findModule(moduleName);
2540
- if (!module) throw new Error(`module not found: ${moduleName}`);
3005
+ const module = this.helper.findModuleCanonical(moduleName);
2541
3006
  const modulePath = module.root;
2542
3007
  const metaDir = path.join(modulePath, 'src/.metadata');
2543
3008
  const metaIndexFile = path.join(metaDir, 'index.ts');
@@ -2782,7 +3247,8 @@ const beans = {
2782
3247
  'tools.metadata': CliToolsMetadata,
2783
3248
  'tools.crudBasic': CliToolsCrudBasic,
2784
3249
  'tools.crudStart': CliToolsCrudStart,
2785
- 'tools.crud': CliToolsCrud
3250
+ 'tools.crud': CliToolsCrud,
3251
+ 'tools.masterDetail': CliToolsMasterDetail
2786
3252
  };
2787
3253
 
2788
3254
  var binBuild = {
@@ -3355,6 +3821,84 @@ var toolsDeps = {
3355
3821
  options: {}
3356
3822
  };
3357
3823
 
3824
+ var toolsMasterDetail = {
3825
+ bean: 'tools.masterDetail',
3826
+ info: {
3827
+ version: '5.0.0',
3828
+ title: 'Cli: Tools: Master Detail',
3829
+ usage: 'npm run vona :tools:masterDetail resourceName -- [--module=] [--detailModule=] [--detailResourceName=] [--relationName=] [--fk=] [--detailMode=aggregate|standalone]'
3830
+ },
3831
+ options: {
3832
+ module: {
3833
+ description: 'master module relative name, such as training-student',
3834
+ type: 'string'
3835
+ },
3836
+ detailModule: {
3837
+ description: 'detail module relative name, such as training-record',
3838
+ type: 'string'
3839
+ },
3840
+ detailResourceName: {
3841
+ description: 'detail resource name',
3842
+ type: 'string'
3843
+ },
3844
+ relationName: {
3845
+ description: 'master relation name',
3846
+ type: 'string'
3847
+ },
3848
+ fk: {
3849
+ description: 'detail foreign key field name',
3850
+ type: 'string'
3851
+ },
3852
+ detailMode: {
3853
+ description: 'detail mode: aggregate or standalone',
3854
+ type: 'string'
3855
+ }
3856
+ },
3857
+ groups: {
3858
+ default: {
3859
+ questions: {
3860
+ resourceName: {
3861
+ type: 'input',
3862
+ message: 'master resourceName',
3863
+ initial: {
3864
+ expression: 'arg0'
3865
+ },
3866
+ required: true
3867
+ },
3868
+ module: {
3869
+ type: 'input',
3870
+ message: 'master module relative name (for example training-student)',
3871
+ required: true
3872
+ },
3873
+ detailModule: {
3874
+ type: 'input',
3875
+ message: 'detail module relative name (for example training-record)',
3876
+ required: true
3877
+ },
3878
+ detailResourceName: {
3879
+ type: 'input',
3880
+ message: 'detail resourceName',
3881
+ required: true
3882
+ },
3883
+ relationName: {
3884
+ type: 'input',
3885
+ message: 'master relation name'
3886
+ },
3887
+ fk: {
3888
+ type: 'input',
3889
+ message: 'detail foreign key field name'
3890
+ },
3891
+ detailMode: {
3892
+ type: 'input',
3893
+ message: 'detail mode (aggregate|standalone)',
3894
+ initial: 'aggregate',
3895
+ required: true
3896
+ }
3897
+ }
3898
+ }
3899
+ }
3900
+ };
3901
+
3358
3902
  var toolsMetadata = {
3359
3903
  bean: 'tools.metadata',
3360
3904
  info: {
@@ -3409,7 +3953,8 @@ const commands = {
3409
3953
  metadata: toolsMetadata,
3410
3954
  crudBasic: toolsCrudBasic,
3411
3955
  crudStart: toolsCrudStart,
3412
- crud: toolsCrud
3956
+ crud: toolsCrud,
3957
+ masterDetail: toolsMasterDetail
3413
3958
  }
3414
3959
  };
3415
3960
 
@@ -0,0 +1,94 @@
1
+ import type { IModule, IModuleInfo } from '@cabloy/module-info';
2
+ import { BeanCliBase } from '@cabloy/cli';
3
+ type DetailMode = 'aggregate' | 'standalone';
4
+ declare module '@cabloy/cli' {
5
+ interface ICommandArgv {
6
+ module: string;
7
+ moduleInfo: IModuleInfo;
8
+ _module: IModule;
9
+ resourceName: string;
10
+ resourceNameCapitalize: string;
11
+ detailModule: string;
12
+ detailModuleInfo: IModuleInfo;
13
+ _detailModule?: IModule;
14
+ detailResourceName: string;
15
+ detailResourceNameCapitalize: string;
16
+ relationName: string;
17
+ relationNameCapitalize: string;
18
+ fk: string;
19
+ detailMode: DetailMode;
20
+ detailPackageName: string;
21
+ detailModuleCapitalize: string;
22
+ detailDtoBaseName: string;
23
+ detailDtoMutateName: string;
24
+ detailDtoViewName: string;
25
+ detailDtoResItemName: string;
26
+ detailFieldPrivateName: string;
27
+ detailDialogTitleCapitalize: string;
28
+ createdDetailModule?: boolean;
29
+ createdDetailResource?: boolean;
30
+ }
31
+ }
32
+ export declare class CliToolsMasterDetail extends BeanCliBase {
33
+ execute(): Promise<void>;
34
+ private _prepareArgv;
35
+ private _ensureDetailModule;
36
+ private _ensureDetailResourceShape;
37
+ private _patchDetailModule;
38
+ private _patchDetailEntity;
39
+ private _patchDetailEntityImport;
40
+ private _patchDetailEntityField;
41
+ private _patchDetailMetaVersion;
42
+ private _patchDetailMetaIndex;
43
+ private _detailMetaVersionLine;
44
+ private _detailMetaIndexLine;
45
+ private _patchFileByMarker;
46
+ private _patchFileContent;
47
+ private _renderMasterDetailDtos;
48
+ private _patchMasterModule;
49
+ private _detailPatchTasks;
50
+ private _masterPatchTasks;
51
+ private _runSequential;
52
+ private _patchMasterModel;
53
+ private _patchMasterService;
54
+ private _patchMasterModelImport;
55
+ private _patchMasterModelRelations;
56
+ private _patchMasterModelDecorator;
57
+ private _masterModelRelationCode;
58
+ private _masterServiceIncludeReplacements;
59
+ private _patchMasterDto;
60
+ private _patchMasterLocale;
61
+ private _refreshMetadata;
62
+ private _patchLocaleAdditions;
63
+ private _localeAdditionKey;
64
+ private _metadataModules;
65
+ private _renderMasterDetailDtoFile;
66
+ private _patchMasterDtoImports;
67
+ private _patchMasterDtoFields;
68
+ private _patchMasterDtoClass;
69
+ private _masterDtoDetailImports;
70
+ private _masterDtoFieldCode;
71
+ private _masterDtoClassReplaceSource;
72
+ private _masterDtoClassReplaceTarget;
73
+ private _detailDtoClassCapitalize;
74
+ private _masterLocaleAdditions;
75
+ private _createDetailCoreResource;
76
+ private _handleAggregateDetailMode;
77
+ private _handleStandaloneDetailMode;
78
+ private _detailModuleShape;
79
+ private _masterPaths;
80
+ private _detailPaths;
81
+ private _hasStandaloneDetailSurface;
82
+ private _removeStandaloneDetailSurface;
83
+ private _detailDtoResItemCapitalize;
84
+ private _detailDtoTemplates;
85
+ private _masterDtoScenes;
86
+ private _masterLocales;
87
+ private _resolveCreatedDetailModuleRoot;
88
+ private _capitalize;
89
+ private _titleize;
90
+ private _readFile;
91
+ private _saveFile;
92
+ private _replaceStrict;
93
+ }
94
+ export {};
@@ -26,6 +26,7 @@ import { CliToolsCrud } from './bean/cli.tools.crud.ts';
26
26
  import { CliToolsCrudBasic } from './bean/cli.tools.crudBasic.ts';
27
27
  import { CliToolsCrudStart } from './bean/cli.tools.crudStart.ts';
28
28
  import { CliToolsDeps } from './bean/cli.tools.deps.ts';
29
+ import { CliToolsMasterDetail } from './bean/cli.tools.masterDetail.ts';
29
30
  import { CliToolsMetadata } from './bean/cli.tools.metadata.ts';
30
31
  export declare const beans: {
31
32
  'default.list': typeof CliDefaultList;
@@ -57,4 +58,5 @@ export declare const beans: {
57
58
  'tools.crudBasic': typeof CliToolsCrudBasic;
58
59
  'tools.crudStart': typeof CliToolsCrudStart;
59
60
  'tools.crud': typeof CliToolsCrud;
61
+ 'tools.masterDetail': typeof CliToolsMasterDetail;
60
62
  };
@@ -0,0 +1,78 @@
1
+ declare const _default: {
2
+ bean: string;
3
+ info: {
4
+ version: string;
5
+ title: string;
6
+ usage: string;
7
+ };
8
+ options: {
9
+ module: {
10
+ description: string;
11
+ type: string;
12
+ };
13
+ detailModule: {
14
+ description: string;
15
+ type: string;
16
+ };
17
+ detailResourceName: {
18
+ description: string;
19
+ type: string;
20
+ };
21
+ relationName: {
22
+ description: string;
23
+ type: string;
24
+ };
25
+ fk: {
26
+ description: string;
27
+ type: string;
28
+ };
29
+ detailMode: {
30
+ description: string;
31
+ type: string;
32
+ };
33
+ };
34
+ groups: {
35
+ default: {
36
+ questions: {
37
+ resourceName: {
38
+ type: string;
39
+ message: string;
40
+ initial: {
41
+ expression: string;
42
+ };
43
+ required: boolean;
44
+ };
45
+ module: {
46
+ type: string;
47
+ message: string;
48
+ required: boolean;
49
+ };
50
+ detailModule: {
51
+ type: string;
52
+ message: string;
53
+ required: boolean;
54
+ };
55
+ detailResourceName: {
56
+ type: string;
57
+ message: string;
58
+ required: boolean;
59
+ };
60
+ relationName: {
61
+ type: string;
62
+ message: string;
63
+ };
64
+ fk: {
65
+ type: string;
66
+ message: string;
67
+ };
68
+ detailMode: {
69
+ type: string;
70
+ message: string;
71
+ initial: string;
72
+ required: boolean;
73
+ };
74
+ };
75
+ };
76
+ };
77
+ };
78
+ export default _default;
@@ -545,5 +545,82 @@ export declare const commands: {
545
545
  };
546
546
  };
547
547
  };
548
+ masterDetail: {
549
+ bean: string;
550
+ info: {
551
+ version: string;
552
+ title: string;
553
+ usage: string;
554
+ };
555
+ options: {
556
+ module: {
557
+ description: string;
558
+ type: string;
559
+ };
560
+ detailModule: {
561
+ description: string;
562
+ type: string;
563
+ };
564
+ detailResourceName: {
565
+ description: string;
566
+ type: string;
567
+ };
568
+ relationName: {
569
+ description: string;
570
+ type: string;
571
+ };
572
+ fk: {
573
+ description: string;
574
+ type: string;
575
+ };
576
+ detailMode: {
577
+ description: string;
578
+ type: string;
579
+ };
580
+ };
581
+ groups: {
582
+ default: {
583
+ questions: {
584
+ resourceName: {
585
+ type: string;
586
+ message: string;
587
+ initial: {
588
+ expression: string;
589
+ };
590
+ required: boolean;
591
+ };
592
+ module: {
593
+ type: string;
594
+ message: string;
595
+ required: boolean;
596
+ };
597
+ detailModule: {
598
+ type: string;
599
+ message: string;
600
+ required: boolean;
601
+ };
602
+ detailResourceName: {
603
+ type: string;
604
+ message: string;
605
+ required: boolean;
606
+ };
607
+ relationName: {
608
+ type: string;
609
+ message: string;
610
+ };
611
+ fk: {
612
+ type: string;
613
+ message: string;
614
+ };
615
+ detailMode: {
616
+ type: string;
617
+ message: string;
618
+ initial: string;
619
+ required: boolean;
620
+ };
621
+ };
622
+ };
623
+ };
624
+ };
548
625
  };
549
626
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vona-cli-set-api",
3
- "version": "1.1.109",
4
- "gitHead": "c7429e0d6a259341ae6e23d018b3238fb2483fdf",
3
+ "version": "1.1.114",
4
+ "gitHead": "ae3d4871534ad17e465e5b8f8789e13b80583344",
5
5
  "description": "vona cli-set-api",
6
6
  "keywords": [
7
7
  "framework",
@@ -41,7 +41,7 @@
41
41
  "@babel/plugin-proposal-decorators": "^7.29.0",
42
42
  "@babel/plugin-transform-class-properties": "^7.28.6",
43
43
  "@babel/plugin-transform-typescript": "^7.28.6",
44
- "@cabloy/cli": "^3.1.17",
44
+ "@cabloy/cli": "^3.1.23",
45
45
  "@cabloy/dotenv": "^1.2.8",
46
46
  "@cabloy/extend": "^3.2.8",
47
47
  "@cabloy/module-glob": "^5.3.13",
@@ -73,7 +73,7 @@
73
73
  "ts-node-maintained": "^10.9.6",
74
74
  "urllib": "^4.9.0",
75
75
  "uuid": "^11.1.0",
76
- "vona-core": "^5.1.23",
76
+ "vona-core": "^5.1.24",
77
77
  "why-is-node-running": "^3.2.2",
78
78
  "yargs-parser": "^22.0.0"
79
79
  }