cabloy 5.1.65 → 5.1.66
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/CHANGELOG.md +44 -0
- package/cabloy-docs/.vitepress/config.mjs +1 -1
- package/cabloy-docs/backend/crud-workflow.md +33 -0
- package/cabloy-docs/backend/master-detail-workflow.md +149 -0
- package/cabloy-docs/frontend/bean-scene-authoring.md +1 -1
- package/cabloy-docs/frontend/cli.md +12 -1
- package/cabloy-docs/frontend/command-scene-authoring.md +12 -1
- package/cabloy-docs/frontend/component-guide.md +10 -0
- package/cabloy-docs/frontend/permission-formscene-action-visibility-guide.md +69 -7
- package/cabloy-docs/frontend/table-cell-cookbook.md +10 -0
- package/cabloy-docs/fullstack/backend-metadata-to-frontend-table-actions.md +83 -2
- package/cabloy-docs/reference/bean-scene-boilerplates.md +9 -4
- package/package.json +1 -1
- package/vona/packages-cli/cabloy-cli/package.json +1 -1
- package/vona/packages-cli/cabloy-cli/src/lib/local.helper.ts +38 -0
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/masterDetail/boilerplate/dto/<%=argv.detailDtoBaseName%>.tsx_ +18 -0
- package/vona/packages-cli/cli-set-api/cli/templates/tools/masterDetail/boilerplate/dto/<%=argv.detailDtoMutateName%>.tsx_ +17 -0
- package/vona/packages-cli/cli-set-api/cli/templates/tools/masterDetail/boilerplate/dto/<%=argv.detailDtoResItemName%>.tsx_ +60 -0
- package/vona/packages-cli/cli-set-api/cli/templates/tools/masterDetail/boilerplate/dto/<%=argv.detailDtoViewName%>.tsx_ +17 -0
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.create.bean.ts +2 -6
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.create.module.ts +3 -6
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.create.suite.ts +1 -1
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.create.test.ts +2 -6
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.init.asset.ts +2 -6
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.init.config.ts +2 -6
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.init.constant.ts +2 -6
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.init.error.ts +2 -6
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.init.lib.ts +2 -6
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.init.locale.ts +2 -6
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.init.main.ts +2 -6
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.init.monkey.ts +2 -6
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.init.static.ts +2 -6
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.init.types.ts +2 -6
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.tools.crud.ts +2 -6
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.tools.crudBasic.ts +2 -6
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.tools.crudStart.ts +2 -6
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.tools.masterDetail.ts +699 -0
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.tools.metadata.ts +1 -2
- package/vona/packages-cli/cli-set-api/src/lib/beans.ts +2 -0
- package/vona/packages-cli/cli-set-api/src/lib/command/tools.masterDetail.ts +78 -0
- package/vona/packages-cli/cli-set-api/src/lib/commands.ts +2 -0
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/packages-vona/vona-core/package.json +1 -1
- package/vona/packages-vona/vona-core/src/lib/mappedClass/type.ts +1 -0
- package/vona/packages-vona/vona-core/src/lib/mappedClass/utils.ts +36 -14
- package/vona/packages-vona/vona-mock/package.json +1 -1
- package/vona/pnpm-lock.yaml +20 -11
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +3 -0
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +3 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/detailRecordBase.tsx +8 -2
- package/vona/src/suite/a-training/modules/training-student/src/dto/detailRecordMutate.tsx +1 -15
- package/vona/src/suite/a-training/modules/training-student/src/dto/detailRecordResItem.tsx +25 -5
- package/vona/src/suite/a-training/modules/training-student/src/dto/detailRecordView.tsx +1 -12
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentCreate.tsx +6 -4
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectReq.tsx +3 -6
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentSummary.tsx +1 -1
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentUpdate.tsx +14 -1
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentView.tsx +14 -1
- package/vona/src/suite/a-training/modules/training-student/src/entity/student.tsx +1 -5
- package/vona/src/suite/a-training/modules/training-student/src/service/student.ts +9 -4
- package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/src/lib/const/decorator.ts +3 -1
- package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/src/lib/schema/makeSchemaLikes.ts +21 -17
- package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/src/lib/utils.ts +44 -2
- package/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoGet.ts +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoMutate.ts +8 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/relations_.ts +32 -16
- package/vona/src/suite-vendor/a-vona/modules/a-web/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-web/src/lib/decorator/bean.ts +26 -5
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/packages-cli/cli/package.json +3 -3
- package/zova/packages-cli/cli-set-front/cli/templates/create/component/boilerplateDetailsActionBulk/controller.tsx_ +31 -0
- package/zova/packages-cli/cli-set-front/cli/templates/rest/component.ts +21 -0
- package/zova/packages-cli/cli-set-front/cli/templates/rest/render.ts +4 -0
- package/zova/packages-cli/cli-set-front/package.json +2 -2
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.create.bean.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.create.mock.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.create.module.ts +3 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.create.suite.ts +1 -1
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.init.asset.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.init.config.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.init.constant.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.init.error.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.init.icon.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.init.lib.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.init.locale.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.init.main.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.init.mainSys.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.init.monkey.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.init.monkeySys.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.init.types.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.openapi.config.ts +1 -4
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.openapi.generate.ts +2 -2
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.refactor.anotherRender.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.refactor.anotherStyle.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.refactor.componentEmits.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.refactor.componentGeneric.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.refactor.componentModel.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.refactor.componentProps.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.refactor.componentSlots.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.refactor.firstRender.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.refactor.firstStyle.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.refactor.pageParams.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.refactor.pageQuery.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.refactor.renameComponent.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.tools.metadata.ts +1 -2
- package/zova/packages-cli/cli-set-front/src/lib/common/cliCreateComponent.ts +2 -6
- package/zova/packages-cli/cli-set-front/src/lib/common/cliCreatePage.ts +2 -6
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/pnpm-lock.yaml +744 -884
- package/zova/src/suite/a-training/modules/training-student/package.json +2 -1
- package/zova/src/suite/cabloy-basic/modules/basic-app/src/bean/behavior.appModal.tsx +131 -13
- package/zova/src/suite/cabloy-basic/modules/basic-app/src/config/config.ts +15 -0
- package/zova/src/suite/cabloy-basic/modules/basic-app/src/service/appModal.ts +23 -5
- package/zova/src/suite/cabloy-basic/modules/basic-app/src/types/appModal.ts +30 -4
- package/zova/src/suite/cabloy-basic/modules/basic-details/package.json +6 -1
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/.metadata/component/blockForm.ts +31 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/.metadata/index.ts +149 -2
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/bean/command.delete.tsx +47 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/bean/tableCell.actionDelete.tsx +52 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/bean/tableCell.actionOperationsRow.tsx +44 -6
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/bean/tableCell.actionUpdate.tsx +73 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/bean/tableCell.actionView.tsx +64 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/bean/tableCell.lineNumber.tsx +30 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/actionCreate/controller.tsx +29 -5
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/blockDetails/controller.tsx +28 -12
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/blockForm/controller.tsx +53 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/blockTable/controller.tsx +7 -2
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/blockToolbarBulk/controller.tsx +3 -1
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/formFieldDetails/controller.tsx +24 -12
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/config/locale/en-us.ts +4 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/config/locale/zh-cn.ts +4 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/index.ts +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/lib/index.ts +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/lib/utils.ts +12 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/service/detail.tsx +172 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/types/detail.ts +18 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/types/details.ts +11 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/types/dialogForm.ts +22 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/types/index.ts +2 -0
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/entry/controller.tsx +1 -2
- package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/modules/a-command/cli/command/boilerplate/{{sceneName}}.{{beanName}}.tsx_ +6 -1
- package/zova/src/suite-vendor/a-zova/modules/a-command/cli/commandBulk/boilerplate/{{sceneName}}.{{beanName}}.tsx_ +6 -1
- package/zova/src/suite-vendor/a-zova/modules/a-command/cli/commandDetailsRow/boilerplate/{{sceneName}}.{{beanName}}.tsx_ +20 -0
- package/zova/src/suite-vendor/a-zova/modules/a-command/cli/commandRow/boilerplate/{{sceneName}}.{{beanName}}.tsx_ +6 -1
- package/zova/src/suite-vendor/a-zova/modules/a-command/package.json +3 -2
- package/zova/src/suite-vendor/a-zova/modules/a-command/src/types/command.ts +2 -0
- package/zova/src/suite-vendor/a-zova/modules/a-form/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/component/form/controller.tsx +1 -0
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/lib/schema.ts +20 -4
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/action.ts +2 -1
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/detail/detail.ts +10 -0
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/detail/details.ts +4 -1
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/detail/index.ts +1 -0
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/rest.ts +2 -0
- package/zova/src/suite-vendor/a-zova/modules/a-table/cli/detailsActionRow/boilerplate/{{sceneName}}.{{beanName}}.tsx_ +24 -0
- package/zova/src/suite-vendor/a-zova/modules/a-table/package.json +3 -2
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/controller.tsx +5 -3
- package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/package.json +6 -6
|
@@ -0,0 +1,699 @@
|
|
|
1
|
+
import type { IModule, IModuleInfo } from '@cabloy/module-info';
|
|
2
|
+
|
|
3
|
+
import { BeanCliBase } from '@cabloy/cli';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
|
|
7
|
+
import { __ThisSetName__ } from '../this.ts';
|
|
8
|
+
|
|
9
|
+
type DetailMode = 'aggregate' | 'standalone';
|
|
10
|
+
type MasterDtoScene = 'Create' | 'Update' | 'View';
|
|
11
|
+
type LocaleName = 'en-us' | 'zh-cn';
|
|
12
|
+
|
|
13
|
+
declare module '@cabloy/cli' {
|
|
14
|
+
interface ICommandArgv {
|
|
15
|
+
module: string;
|
|
16
|
+
moduleInfo: IModuleInfo;
|
|
17
|
+
_module: IModule;
|
|
18
|
+
resourceName: string;
|
|
19
|
+
resourceNameCapitalize: string;
|
|
20
|
+
detailModule: string;
|
|
21
|
+
detailModuleInfo: IModuleInfo;
|
|
22
|
+
_detailModule?: IModule;
|
|
23
|
+
detailResourceName: string;
|
|
24
|
+
detailResourceNameCapitalize: string;
|
|
25
|
+
relationName: string;
|
|
26
|
+
relationNameCapitalize: string;
|
|
27
|
+
fk: string;
|
|
28
|
+
detailMode: DetailMode;
|
|
29
|
+
detailPackageName: string;
|
|
30
|
+
detailModuleCapitalize: string;
|
|
31
|
+
detailDtoBaseName: string;
|
|
32
|
+
detailDtoMutateName: string;
|
|
33
|
+
detailDtoViewName: string;
|
|
34
|
+
detailDtoResItemName: string;
|
|
35
|
+
detailFieldPrivateName: string;
|
|
36
|
+
detailDialogTitleCapitalize: string;
|
|
37
|
+
createdDetailModule?: boolean;
|
|
38
|
+
createdDetailResource?: boolean;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class CliToolsMasterDetail extends BeanCliBase {
|
|
43
|
+
async execute() {
|
|
44
|
+
await super.execute();
|
|
45
|
+
this._prepareArgv();
|
|
46
|
+
await this._ensureDetailModule();
|
|
47
|
+
await this._ensureDetailResourceShape();
|
|
48
|
+
await this._patchDetailModule();
|
|
49
|
+
await this._renderMasterDetailDtos();
|
|
50
|
+
await this._patchMasterModule();
|
|
51
|
+
await this._refreshMetadata();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private _prepareArgv() {
|
|
55
|
+
const { argv } = this.context;
|
|
56
|
+
argv.detailMode = (argv.detailMode || 'aggregate') as DetailMode;
|
|
57
|
+
if (argv.detailMode !== 'aggregate' && argv.detailMode !== 'standalone') {
|
|
58
|
+
throw new Error(`mode is not valid: ${argv.detailMode}`);
|
|
59
|
+
}
|
|
60
|
+
argv.moduleInfo = this.helper.parseModuleInfoCanonical(argv.module, 'master module');
|
|
61
|
+
const _module = this.helper.findModuleCanonical(argv.module, 'master module');
|
|
62
|
+
argv._module = _module;
|
|
63
|
+
argv.resourceNameCapitalize = this.helper.firstCharToUpperCase(argv.resourceName);
|
|
64
|
+
|
|
65
|
+
argv.detailModuleInfo = this.helper.parseModuleInfoCanonical(
|
|
66
|
+
argv.detailModule,
|
|
67
|
+
'detail module',
|
|
68
|
+
);
|
|
69
|
+
argv._detailModule = this.helper.findModule(argv.detailModule);
|
|
70
|
+
argv.detailResourceNameCapitalize = this.helper.firstCharToUpperCase(argv.detailResourceName);
|
|
71
|
+
argv.relationName = argv.relationName || `${argv.detailResourceName}s`;
|
|
72
|
+
argv.relationNameCapitalize = this.helper.firstCharToUpperCase(argv.relationName);
|
|
73
|
+
argv.fk = argv.fk || `${argv.resourceName}Id`;
|
|
74
|
+
argv.detailPackageName = `vona-module-${argv.detailModuleInfo.relativeName}`;
|
|
75
|
+
argv.detailModuleCapitalize = this.helper.stringToCapitalize(
|
|
76
|
+
argv.detailModuleInfo.relativeName,
|
|
77
|
+
'-',
|
|
78
|
+
);
|
|
79
|
+
argv.detailDtoBaseName = `detail${argv.detailResourceNameCapitalize}Base`;
|
|
80
|
+
argv.detailDtoMutateName = `detail${argv.detailResourceNameCapitalize}Mutate`;
|
|
81
|
+
argv.detailDtoViewName = `detail${argv.detailResourceNameCapitalize}View`;
|
|
82
|
+
argv.detailDtoResItemName = `detail${argv.detailResourceNameCapitalize}ResItem`;
|
|
83
|
+
argv.detailFieldPrivateName = `_${argv.relationName}`;
|
|
84
|
+
argv.detailDialogTitleCapitalize = argv.detailModuleCapitalize;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private async _ensureDetailModule() {
|
|
88
|
+
const { argv } = this.context;
|
|
89
|
+
if (argv._detailModule) return;
|
|
90
|
+
const args = [':create:module', argv.detailModule, '--nometadata'];
|
|
91
|
+
if (argv._module.suite) {
|
|
92
|
+
args.push(`--suite=${argv._module.suite}`);
|
|
93
|
+
}
|
|
94
|
+
await this.helper.invokeCli(args, { cwd: argv.projectPath });
|
|
95
|
+
argv.createdDetailModule = true;
|
|
96
|
+
argv._detailModule = this.helper.findModule(argv.detailModule);
|
|
97
|
+
if (!argv._detailModule) {
|
|
98
|
+
const root = this._resolveCreatedDetailModuleRoot();
|
|
99
|
+
argv._detailModule = {
|
|
100
|
+
name: argv.detailModuleInfo.relativeName,
|
|
101
|
+
info: argv.detailModuleInfo,
|
|
102
|
+
root,
|
|
103
|
+
pkg: path.join(root, 'package.json'),
|
|
104
|
+
package: await this.helper.loadJSONFile(path.join(root, 'package.json')),
|
|
105
|
+
suite: argv._module.suite,
|
|
106
|
+
} as IModule;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private async _ensureDetailResourceShape() {
|
|
111
|
+
const { argv } = this.context;
|
|
112
|
+
const { hasCoreDetailFiles, hasStandaloneSurface } = this._detailModuleShape();
|
|
113
|
+
|
|
114
|
+
if (!hasCoreDetailFiles) {
|
|
115
|
+
await this._createDetailCoreResource();
|
|
116
|
+
argv.createdDetailResource = true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (argv.detailMode === 'aggregate') {
|
|
120
|
+
this._handleAggregateDetailMode(hasStandaloneSurface);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
this._handleStandaloneDetailMode();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private async _patchDetailModule() {
|
|
128
|
+
await this._runSequential(this._detailPatchTasks());
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private async _patchDetailEntity() {
|
|
132
|
+
const { argv } = this.context;
|
|
133
|
+
await this._patchFileContent({
|
|
134
|
+
fileName: this._detailPaths().entity,
|
|
135
|
+
existsNeedle: `${argv.fk}: TableIdentity;`,
|
|
136
|
+
patch: (content, fileName) => {
|
|
137
|
+
content = this._patchDetailEntityImport(content);
|
|
138
|
+
return this._patchDetailEntityField(content, fileName);
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private _patchDetailEntityImport(content: string) {
|
|
144
|
+
if (!content.includes("import type { TableIdentity } from 'table-identity';")) {
|
|
145
|
+
content = `import type { TableIdentity } from 'table-identity';\n${content}`;
|
|
146
|
+
}
|
|
147
|
+
return content;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private _patchDetailEntityField(content: string, fileName: string) {
|
|
151
|
+
const { argv } = this.context;
|
|
152
|
+
const marker = `export class Entity${argv.detailResourceNameCapitalize} extends EntityBase {\n`;
|
|
153
|
+
if (!content.includes(marker)) {
|
|
154
|
+
throw new Error(`detail entity is not in the expected shape: ${fileName}`);
|
|
155
|
+
}
|
|
156
|
+
const fieldCode = ` @Api.field(v.required(), ZovaRender.visible(false))\n ${argv.fk}: TableIdentity;\n\n`;
|
|
157
|
+
return content.replace(marker, `${marker}${fieldCode}`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private async _patchDetailMetaVersion() {
|
|
161
|
+
const { argv } = this.context;
|
|
162
|
+
await this._patchFileByMarker({
|
|
163
|
+
fileName: this._detailPaths().metaVersion,
|
|
164
|
+
existsNeedle: `entity${argv.detailResourceNameCapitalize}.${argv.fk}`,
|
|
165
|
+
marker: ' table.basicFields();\n',
|
|
166
|
+
line: this._detailMetaVersionLine(),
|
|
167
|
+
shapeName: 'detail meta.version',
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private async _patchDetailMetaIndex() {
|
|
172
|
+
const { argv } = this.context;
|
|
173
|
+
await this._patchFileByMarker({
|
|
174
|
+
fileName: this._detailPaths().metaIndex,
|
|
175
|
+
existsNeedle: `'${argv.fk}'`,
|
|
176
|
+
marker: ' indexes: {\n',
|
|
177
|
+
line: this._detailMetaIndexLine(),
|
|
178
|
+
shapeName: 'detail meta.index',
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private _detailMetaVersionLine() {
|
|
183
|
+
const { argv } = this.context;
|
|
184
|
+
return ` table.tableIdentity(entity${argv.detailResourceNameCapitalize}.${argv.fk}).comment(entity${argv.detailResourceNameCapitalize}.$comment.${argv.fk});\n`;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private _detailMetaIndexLine() {
|
|
188
|
+
const { argv } = this.context;
|
|
189
|
+
return ` ...$tableColumns('${this.helper.combineModuleNameAndResource(argv.detailModuleInfo.relativeName, argv.detailResourceName)}', '${argv.fk}'),\n`;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private async _patchFileByMarker({
|
|
193
|
+
fileName,
|
|
194
|
+
existsNeedle,
|
|
195
|
+
marker,
|
|
196
|
+
line,
|
|
197
|
+
shapeName,
|
|
198
|
+
}: {
|
|
199
|
+
fileName: string;
|
|
200
|
+
existsNeedle: string;
|
|
201
|
+
marker: string;
|
|
202
|
+
line: string;
|
|
203
|
+
shapeName: string;
|
|
204
|
+
}) {
|
|
205
|
+
let content = this._readFile(fileName);
|
|
206
|
+
if (content.includes(existsNeedle)) return;
|
|
207
|
+
if (!content.includes(marker)) {
|
|
208
|
+
throw new Error(`${shapeName} is not in the expected shape: ${fileName}`);
|
|
209
|
+
}
|
|
210
|
+
content = content.replace(marker, `${marker}${line}`);
|
|
211
|
+
await this._saveFile(fileName, content);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private async _patchFileContent({
|
|
215
|
+
fileName,
|
|
216
|
+
existsNeedle,
|
|
217
|
+
patch,
|
|
218
|
+
}: {
|
|
219
|
+
fileName: string;
|
|
220
|
+
existsNeedle: string;
|
|
221
|
+
patch: (content: string, fileName: string) => string;
|
|
222
|
+
}) {
|
|
223
|
+
let content = this._readFile(fileName);
|
|
224
|
+
if (content.includes(existsNeedle)) return;
|
|
225
|
+
content = patch(content, fileName);
|
|
226
|
+
await this._saveFile(fileName, content);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
private async _renderMasterDetailDtos() {
|
|
230
|
+
await this.helper.ensureDir(this._masterPaths().dtoDir);
|
|
231
|
+
for (const [dtoBaseName, templateBaseName] of this._detailDtoTemplates()) {
|
|
232
|
+
await this._renderMasterDetailDtoFile(dtoBaseName, templateBaseName);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private async _patchMasterModule() {
|
|
237
|
+
await this._runSequential(this._masterPatchTasks());
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private _detailPatchTasks(): Array<() => Promise<void>> {
|
|
241
|
+
return [
|
|
242
|
+
() => this._patchDetailEntity(),
|
|
243
|
+
() => this._patchDetailMetaVersion(),
|
|
244
|
+
() => this._patchDetailMetaIndex(),
|
|
245
|
+
];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private _masterPatchTasks(): Array<() => Promise<void>> {
|
|
249
|
+
return [
|
|
250
|
+
() => this._patchMasterModel(),
|
|
251
|
+
() => this._patchMasterService(),
|
|
252
|
+
...this._masterDtoScenes().map(scene => () => this._patchMasterDto(scene)),
|
|
253
|
+
...this._masterLocales().map(locale => () => this._patchMasterLocale(locale)),
|
|
254
|
+
];
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private async _runSequential(tasks: Array<() => Promise<void>>) {
|
|
258
|
+
for (const task of tasks) {
|
|
259
|
+
await task();
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private async _patchMasterModel() {
|
|
264
|
+
const { argv } = this.context;
|
|
265
|
+
const fileName = this._masterPaths().model;
|
|
266
|
+
let content = this._readFile(fileName);
|
|
267
|
+
if (content.includes(`${argv.relationName}: $relation.hasMany(`)) return;
|
|
268
|
+
content = this._patchMasterModelImport(content, fileName);
|
|
269
|
+
content = this._patchMasterModelRelations(content, fileName);
|
|
270
|
+
await this._saveFile(fileName, content);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private async _patchMasterService() {
|
|
274
|
+
const { argv } = this.context;
|
|
275
|
+
const fileName = this._masterPaths().service;
|
|
276
|
+
let content = this._readFile(fileName);
|
|
277
|
+
if (content.includes(`include: { ${argv.relationName}: true }`)) return;
|
|
278
|
+
for (const [search, replacement] of this._masterServiceIncludeReplacements()) {
|
|
279
|
+
content = this._replaceStrict(content, search, replacement, fileName);
|
|
280
|
+
}
|
|
281
|
+
await this._saveFile(fileName, content);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
private _patchMasterModelImport(content: string, fileName: string) {
|
|
285
|
+
if (content.includes("import { $relation, BeanModelBase, Model } from 'vona-module-a-orm';")) {
|
|
286
|
+
return content;
|
|
287
|
+
}
|
|
288
|
+
return this._replaceStrict(
|
|
289
|
+
content,
|
|
290
|
+
"import { BeanModelBase, Model } from 'vona-module-a-orm';",
|
|
291
|
+
"import { $relation, BeanModelBase, Model } from 'vona-module-a-orm';",
|
|
292
|
+
fileName,
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
private _patchMasterModelRelations(content: string, fileName: string) {
|
|
297
|
+
const relationsBlockMarker = ' relations: {\n';
|
|
298
|
+
if (content.includes(relationsBlockMarker)) {
|
|
299
|
+
return content.replace(
|
|
300
|
+
relationsBlockMarker,
|
|
301
|
+
`${relationsBlockMarker}${this._masterModelRelationCode()}`,
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
return this._patchMasterModelDecorator(content, fileName);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
private _patchMasterModelDecorator(content: string, fileName: string) {
|
|
308
|
+
const { argv } = this.context;
|
|
309
|
+
const marker = `@Model<IModelOptions${argv.resourceNameCapitalize}>({ entity: Entity${argv.resourceNameCapitalize} })`;
|
|
310
|
+
if (!content.includes(marker)) {
|
|
311
|
+
throw new Error(`master model is not in the expected shape: ${fileName}`);
|
|
312
|
+
}
|
|
313
|
+
return content.replace(
|
|
314
|
+
marker,
|
|
315
|
+
`@Model<IModelOptions${argv.resourceNameCapitalize}>({\n entity: Entity${argv.resourceNameCapitalize},\n relations: {\n${this._masterModelRelationCode()} },\n})`,
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
private _masterModelRelationCode() {
|
|
320
|
+
const { argv } = this.context;
|
|
321
|
+
return ` ${argv.relationName}: $relation.hasMany('${argv.detailModuleInfo.relativeName}:${argv.detailResourceName}', '${argv.fk}', {\n columns: ['id', 'name', 'description'],\n }),\n`;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
private _masterServiceIncludeReplacements() {
|
|
325
|
+
const { argv } = this.context;
|
|
326
|
+
return [
|
|
327
|
+
[
|
|
328
|
+
` return await this.scope.model.${argv.resourceName}.insert(${argv.resourceName});`,
|
|
329
|
+
` return await this.scope.model.${argv.resourceName}.insert(${argv.resourceName}, { include: { ${argv.relationName}: true } });`,
|
|
330
|
+
],
|
|
331
|
+
[
|
|
332
|
+
` return await this.scope.model.${argv.resourceName}.getById(id);`,
|
|
333
|
+
` return await this.scope.model.${argv.resourceName}.getById(id, { include: { ${argv.relationName}: true } });`,
|
|
334
|
+
],
|
|
335
|
+
[
|
|
336
|
+
` return await this.scope.model.${argv.resourceName}.updateById(id, ${argv.resourceName});`,
|
|
337
|
+
` return await this.scope.model.${argv.resourceName}.updateById(id, ${argv.resourceName}, {\n include: { ${argv.relationName}: true },\n });`,
|
|
338
|
+
],
|
|
339
|
+
[
|
|
340
|
+
` return await this.scope.model.${argv.resourceName}.deleteById(id);`,
|
|
341
|
+
` return await this.scope.model.${argv.resourceName}.deleteById(id, { include: { ${argv.relationName}: true } });`,
|
|
342
|
+
],
|
|
343
|
+
] as const;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
private async _patchMasterDto(scene: MasterDtoScene) {
|
|
347
|
+
const { argv } = this.context;
|
|
348
|
+
const fileName = path.join(argv._module.root, 'src/dto', `${argv.resourceName}${scene}.tsx`);
|
|
349
|
+
let content = this._readFile(fileName);
|
|
350
|
+
const detailDtoResItemCapitalize = this._detailDtoResItemCapitalize();
|
|
351
|
+
const detailDtoClassCapitalize = this._detailDtoClassCapitalize(scene);
|
|
352
|
+
|
|
353
|
+
if (content.includes(`${argv.detailFieldPrivateName}?: Dto${detailDtoResItemCapitalize}[];`)) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
content = this._patchMasterDtoImports(
|
|
358
|
+
content,
|
|
359
|
+
fileName,
|
|
360
|
+
scene,
|
|
361
|
+
detailDtoClassCapitalize,
|
|
362
|
+
detailDtoResItemCapitalize,
|
|
363
|
+
);
|
|
364
|
+
content = this._patchMasterDtoFields(content, fileName, scene);
|
|
365
|
+
content = this._patchMasterDtoClass(
|
|
366
|
+
content,
|
|
367
|
+
fileName,
|
|
368
|
+
scene,
|
|
369
|
+
detailDtoClassCapitalize,
|
|
370
|
+
detailDtoResItemCapitalize,
|
|
371
|
+
);
|
|
372
|
+
await this._saveFile(fileName, content);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
private async _patchMasterLocale(locale: LocaleName) {
|
|
376
|
+
const fileName = path.join(this._masterPaths().localeDir, `${locale}.ts`);
|
|
377
|
+
let content = this._readFile(fileName);
|
|
378
|
+
content = this._patchLocaleAdditions(content, this._masterLocaleAdditions(locale));
|
|
379
|
+
await this._saveFile(fileName, content);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
private async _refreshMetadata() {
|
|
383
|
+
const { argv } = this.context;
|
|
384
|
+
for (const moduleName of this._metadataModules()) {
|
|
385
|
+
await this.helper.invokeCli([':tools:metadata', moduleName], {
|
|
386
|
+
cwd: argv.projectPath,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
private _patchLocaleAdditions(content: string, additions: string[]) {
|
|
392
|
+
for (const addition of additions) {
|
|
393
|
+
const key = this._localeAdditionKey(addition);
|
|
394
|
+
if (content.includes(`${key}:`)) continue;
|
|
395
|
+
content = content.replace('};\n', ` ${addition},\n};\n`);
|
|
396
|
+
}
|
|
397
|
+
return content;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
private _localeAdditionKey(addition: string) {
|
|
401
|
+
return addition.split(':')[0];
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
private _metadataModules() {
|
|
405
|
+
const { argv } = this.context;
|
|
406
|
+
return [argv.detailModule, argv.module] as const;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
private async _renderMasterDetailDtoFile(dtoBaseName: string, templateBaseName: string) {
|
|
410
|
+
const targetFile = path.join(this._masterPaths().dtoDir, `${dtoBaseName}.tsx`);
|
|
411
|
+
if (fs.existsSync(targetFile)) return;
|
|
412
|
+
const templateFile = this.template.resolveTemplatePath(
|
|
413
|
+
__ThisSetName__,
|
|
414
|
+
`tools/masterDetail/boilerplate/dto/${templateBaseName}`,
|
|
415
|
+
);
|
|
416
|
+
const templateContent = fs.readFileSync(templateFile).toString('utf8');
|
|
417
|
+
const renderedContent = await this.template.renderContent({ content: templateContent });
|
|
418
|
+
await this._saveFile(targetFile, renderedContent);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
private _patchMasterDtoImports(
|
|
422
|
+
content: string,
|
|
423
|
+
fileName: string,
|
|
424
|
+
scene: MasterDtoScene,
|
|
425
|
+
detailDtoClassCapitalize: string,
|
|
426
|
+
detailDtoResItemCapitalize: string,
|
|
427
|
+
) {
|
|
428
|
+
const { argv } = this.context;
|
|
429
|
+
if (!content.includes("import { $makeMetadata, Api, v } from 'vona-module-a-openapiutils';")) {
|
|
430
|
+
content = this._replaceStrict(
|
|
431
|
+
content,
|
|
432
|
+
"import { $Dto } from 'vona-module-a-orm';",
|
|
433
|
+
"import { $makeMetadata, Api, v } from 'vona-module-a-openapiutils';\nimport { $Dto } from 'vona-module-a-orm';",
|
|
434
|
+
fileName,
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
if (!content.includes("import { $locale } from '../.metadata/locales.ts';")) {
|
|
438
|
+
content = this._replaceStrict(
|
|
439
|
+
content,
|
|
440
|
+
"import { ZovaRender } from 'zova-rest-cabloy-basic-admin';\n",
|
|
441
|
+
"import { ZovaRender } from 'zova-rest-cabloy-basic-admin';\n\nimport { $locale } from '../.metadata/locales.ts';\n",
|
|
442
|
+
fileName,
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
const importMarker = `import { Model${argv.resourceNameCapitalize} } from '../model/${argv.resourceName}.ts';\n`;
|
|
446
|
+
if (!content.includes(`import { Dto${detailDtoClassCapitalize} } from './`)) {
|
|
447
|
+
content = this._replaceStrict(
|
|
448
|
+
content,
|
|
449
|
+
importMarker,
|
|
450
|
+
`${importMarker}${this._masterDtoDetailImports(scene, detailDtoClassCapitalize, detailDtoResItemCapitalize)}`,
|
|
451
|
+
fileName,
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
return content;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
private _patchMasterDtoFields(content: string, fileName: string, scene: MasterDtoScene) {
|
|
458
|
+
const fieldCode = this._masterDtoFieldCode(scene);
|
|
459
|
+
if (content.includes(' fields: {\n')) {
|
|
460
|
+
return content.replace(' fields: {\n', ` fields: {\n${fieldCode}`);
|
|
461
|
+
}
|
|
462
|
+
const decoratorMarker = ' ],\n})';
|
|
463
|
+
if (!content.includes(decoratorMarker)) {
|
|
464
|
+
throw new Error(`master dto decorator is not in the expected shape: ${fileName}`);
|
|
465
|
+
}
|
|
466
|
+
return content.replace(decoratorMarker, ` ],\n fields: {\n${fieldCode} },\n})`);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
private _patchMasterDtoClass(
|
|
470
|
+
content: string,
|
|
471
|
+
fileName: string,
|
|
472
|
+
scene: MasterDtoScene,
|
|
473
|
+
detailDtoClassCapitalize: string,
|
|
474
|
+
detailDtoResItemCapitalize: string,
|
|
475
|
+
) {
|
|
476
|
+
const classReplaceSource = this._masterDtoClassReplaceSource(scene);
|
|
477
|
+
if (!content.includes(classReplaceSource)) {
|
|
478
|
+
throw new Error(`master dto class is not in the expected generated shape: ${fileName}`);
|
|
479
|
+
}
|
|
480
|
+
return content.replace(
|
|
481
|
+
classReplaceSource,
|
|
482
|
+
this._masterDtoClassReplaceTarget(
|
|
483
|
+
scene,
|
|
484
|
+
detailDtoClassCapitalize,
|
|
485
|
+
detailDtoResItemCapitalize,
|
|
486
|
+
),
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
private _masterDtoDetailImports(
|
|
491
|
+
scene: MasterDtoScene,
|
|
492
|
+
detailDtoClassCapitalize: string,
|
|
493
|
+
detailDtoResItemCapitalize: string,
|
|
494
|
+
) {
|
|
495
|
+
const { argv } = this.context;
|
|
496
|
+
return `import { Dto${detailDtoClassCapitalize} } from './${scene === 'View' ? argv.detailDtoViewName : argv.detailDtoMutateName}.tsx';\nimport { Dto${detailDtoResItemCapitalize} } from './${argv.detailDtoResItemName}.tsx';\n`;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
private _masterDtoFieldCode(scene: MasterDtoScene) {
|
|
500
|
+
const { argv } = this.context;
|
|
501
|
+
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`;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
private _masterDtoClassReplaceSource(scene: MasterDtoScene) {
|
|
505
|
+
const { argv } = this.context;
|
|
506
|
+
const kind = scene === 'Create' ? 'create' : scene === 'Update' ? 'update' : 'get';
|
|
507
|
+
return `export class Dto${argv.resourceNameCapitalize}${scene} extends $Dto.${kind}(() => Model${argv.resourceNameCapitalize}) {}`;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
private _masterDtoClassReplaceTarget(
|
|
511
|
+
scene: MasterDtoScene,
|
|
512
|
+
detailDtoClassCapitalize: string,
|
|
513
|
+
detailDtoResItemCapitalize: string,
|
|
514
|
+
) {
|
|
515
|
+
const { argv } = this.context;
|
|
516
|
+
const kind = scene === 'Create' ? 'create' : scene === 'Update' ? 'update' : 'get';
|
|
517
|
+
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}`;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
private _detailDtoClassCapitalize(scene: MasterDtoScene) {
|
|
521
|
+
const { argv } = this.context;
|
|
522
|
+
return scene === 'View'
|
|
523
|
+
? this._capitalize(argv.detailDtoViewName)
|
|
524
|
+
: this._capitalize(argv.detailDtoMutateName);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
private _masterLocaleAdditions(locale: LocaleName) {
|
|
528
|
+
const { argv } = this.context;
|
|
529
|
+
if (locale === 'en-us') {
|
|
530
|
+
return [
|
|
531
|
+
`${argv.relationNameCapitalize}: '${this._titleize(argv.relationName)}'`,
|
|
532
|
+
`Add${argv.detailDialogTitleCapitalize}: 'Add ${this._titleize(argv.detailModuleInfo.relativeName)}'`,
|
|
533
|
+
`Edit${argv.detailDialogTitleCapitalize}: 'Edit ${this._titleize(argv.detailModuleInfo.relativeName)}'`,
|
|
534
|
+
`View${argv.detailDialogTitleCapitalize}: 'View ${this._titleize(argv.detailModuleInfo.relativeName)}'`,
|
|
535
|
+
];
|
|
536
|
+
}
|
|
537
|
+
return [
|
|
538
|
+
`${argv.relationNameCapitalize}: '明细'`,
|
|
539
|
+
`Add${argv.detailDialogTitleCapitalize}: '添加明细'`,
|
|
540
|
+
`Edit${argv.detailDialogTitleCapitalize}: '编辑明细'`,
|
|
541
|
+
`View${argv.detailDialogTitleCapitalize}: '查看明细'`,
|
|
542
|
+
];
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
private async _createDetailCoreResource() {
|
|
546
|
+
const { argv } = this.context;
|
|
547
|
+
await this.helper.invokeCli(
|
|
548
|
+
[
|
|
549
|
+
':tools:crudBasic',
|
|
550
|
+
argv.detailResourceName,
|
|
551
|
+
`--module=${argv.detailModule}`,
|
|
552
|
+
'--nometadata',
|
|
553
|
+
],
|
|
554
|
+
{ cwd: argv.projectPath },
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
private _handleAggregateDetailMode(hasStandaloneSurface: boolean) {
|
|
559
|
+
const { argv } = this.context;
|
|
560
|
+
if (hasStandaloneSurface && !argv.createdDetailResource) {
|
|
561
|
+
throw new Error(
|
|
562
|
+
`detail module already has a standalone resource surface: ${argv.detailModule}:${argv.detailResourceName}`,
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
if (this._hasStandaloneDetailSurface()) {
|
|
566
|
+
this._removeStandaloneDetailSurface();
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
private _handleStandaloneDetailMode() {
|
|
571
|
+
const { argv } = this.context;
|
|
572
|
+
if (!this._hasStandaloneDetailSurface()) {
|
|
573
|
+
throw new Error(
|
|
574
|
+
`detail module does not have a standalone resource surface: ${argv.detailModule}:${argv.detailResourceName}`,
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
private _detailModuleShape() {
|
|
580
|
+
const files = this._detailPaths();
|
|
581
|
+
return {
|
|
582
|
+
hasCoreDetailFiles: fs.existsSync(files.entity) && fs.existsSync(files.model),
|
|
583
|
+
hasStandaloneSurface: this._hasStandaloneDetailSurface(),
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
private _masterPaths() {
|
|
588
|
+
const { argv } = this.context;
|
|
589
|
+
const masterRoot = argv._module.root;
|
|
590
|
+
return {
|
|
591
|
+
root: masterRoot,
|
|
592
|
+
model: path.join(masterRoot, 'src/model', `${argv.resourceName}.ts`),
|
|
593
|
+
service: path.join(masterRoot, 'src/service', `${argv.resourceName}.ts`),
|
|
594
|
+
dtoDir: path.join(masterRoot, 'src/dto'),
|
|
595
|
+
localeDir: path.join(masterRoot, 'src/config/locale'),
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
private _detailPaths() {
|
|
600
|
+
const { argv } = this.context;
|
|
601
|
+
const detailRoot = argv._detailModule!.root;
|
|
602
|
+
return {
|
|
603
|
+
root: detailRoot,
|
|
604
|
+
entity: path.join(detailRoot, 'src/entity', `${argv.detailResourceName}.tsx`),
|
|
605
|
+
model: path.join(detailRoot, 'src/model', `${argv.detailResourceName}.ts`),
|
|
606
|
+
controller: path.join(detailRoot, 'src/controller', `${argv.detailResourceName}.ts`),
|
|
607
|
+
service: path.join(detailRoot, 'src/service', `${argv.detailResourceName}.ts`),
|
|
608
|
+
dtoDir: path.join(detailRoot, 'src/dto'),
|
|
609
|
+
test: path.join(detailRoot, 'test', `${argv.detailResourceName}.test.ts`),
|
|
610
|
+
metaVersion: path.join(detailRoot, 'src/bean', 'meta.version.ts'),
|
|
611
|
+
metaIndex: path.join(detailRoot, 'src/bean', 'meta.index.ts'),
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
private _hasStandaloneDetailSurface() {
|
|
616
|
+
const files = this._detailPaths();
|
|
617
|
+
return (
|
|
618
|
+
fs.existsSync(files.controller) || fs.existsSync(files.service) || fs.existsSync(files.dtoDir)
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
private _removeStandaloneDetailSurface() {
|
|
623
|
+
const files = this._detailPaths();
|
|
624
|
+
for (const file of [files.controller, files.service, files.test]) {
|
|
625
|
+
if (fs.existsSync(file)) {
|
|
626
|
+
fs.rmSync(file, { force: true });
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
if (fs.existsSync(files.dtoDir)) {
|
|
630
|
+
fs.rmSync(files.dtoDir, { recursive: true, force: true });
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
private _detailDtoResItemCapitalize() {
|
|
635
|
+
return this._capitalize(this.context.argv.detailDtoResItemName);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
private _detailDtoTemplates() {
|
|
639
|
+
const { argv } = this.context;
|
|
640
|
+
return [
|
|
641
|
+
[argv.detailDtoBaseName, '<%=argv.detailDtoBaseName%>.tsx_'],
|
|
642
|
+
[argv.detailDtoMutateName, '<%=argv.detailDtoMutateName%>.tsx_'],
|
|
643
|
+
[argv.detailDtoViewName, '<%=argv.detailDtoViewName%>.tsx_'],
|
|
644
|
+
[argv.detailDtoResItemName, '<%=argv.detailDtoResItemName%>.tsx_'],
|
|
645
|
+
] as const;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
private _masterDtoScenes() {
|
|
649
|
+
return ['Create', 'Update', 'View'] as const satisfies readonly MasterDtoScene[];
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
private _masterLocales() {
|
|
653
|
+
return ['en-us', 'zh-cn'] as const satisfies readonly LocaleName[];
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
private _resolveCreatedDetailModuleRoot() {
|
|
657
|
+
const { argv } = this.context;
|
|
658
|
+
if (argv._module.suite) {
|
|
659
|
+
return path.join(
|
|
660
|
+
argv.projectPath,
|
|
661
|
+
'src/suite',
|
|
662
|
+
argv._module.suite,
|
|
663
|
+
'modules',
|
|
664
|
+
argv.detailModule,
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
return path.join(argv.projectPath, 'src/module', argv.detailModule);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
private _capitalize(name: string) {
|
|
671
|
+
return this.helper.firstCharToUpperCase(name);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
private _titleize(name: string) {
|
|
675
|
+
return this.helper
|
|
676
|
+
.stringToCapitalize(name, '-')
|
|
677
|
+
.replace(/([A-Z])/g, ' $1')
|
|
678
|
+
.trim();
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
private _readFile(fileName: string) {
|
|
682
|
+
if (!fs.existsSync(fileName)) {
|
|
683
|
+
throw new Error(`file does not exist: ${fileName}`);
|
|
684
|
+
}
|
|
685
|
+
return fs.readFileSync(fileName).toString('utf8');
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
private async _saveFile(fileName: string, content: string) {
|
|
689
|
+
fs.writeFileSync(fileName, content);
|
|
690
|
+
await this.helper.formatFile({ fileName, logPrefix: 'format: ' });
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
private _replaceStrict(content: string, search: string, replacement: string, fileName: string) {
|
|
694
|
+
if (!content.includes(search)) {
|
|
695
|
+
throw new Error(`file is not in the expected shape: ${fileName}`);
|
|
696
|
+
}
|
|
697
|
+
return content.replace(search, replacement);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
@@ -54,8 +54,7 @@ export class CliToolsMetadata extends BeanCliBase {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
async _generateMetadata(moduleName: string, force: boolean) {
|
|
57
|
-
const module = this.helper.
|
|
58
|
-
if (!module) throw new Error(`module not found: ${moduleName}`);
|
|
57
|
+
const module = this.helper.findModuleCanonical(moduleName);
|
|
59
58
|
const modulePath = module.root;
|
|
60
59
|
const metaDir = path.join(modulePath, 'src/.metadata');
|
|
61
60
|
const metaIndexFile = path.join(metaDir, 'index.ts');
|