@blocklet/pages-kit-block-studio 0.4.64 → 0.4.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/lib/cjs/constants/new-block-template/index.tsx +13 -4
- package/lib/cjs/middlewares/init-block-studio-router.js +194 -0
- package/lib/cjs/plugins/_theme.js +536 -3
- package/lib/cjs/plugins/vite-plugin-block-studio.js +2 -2
- package/lib/cjs/tsconfig.tsbuildinfo +1 -1
- package/lib/cjs/types/properties.js +2 -0
- package/lib/cjs/utils/block-props-utils.js +151 -0
- package/lib/cjs/utils/generate-wrapper-code.js +127 -345
- package/lib/cjs/utils/ts-morph-utils.js +255 -0
- package/lib/cjs/utils/zod-utils.js +314 -0
- package/lib/esm/constants/new-block-template/index.tsx +13 -4
- package/lib/esm/middlewares/init-block-studio-router.js +161 -0
- package/lib/esm/plugins/_theme.js +501 -3
- package/lib/esm/plugins/vite-plugin-block-studio.js +2 -2
- package/lib/esm/tsconfig.tsbuildinfo +1 -1
- package/lib/esm/types/properties.js +1 -0
- package/lib/esm/utils/block-props-utils.js +114 -0
- package/lib/esm/utils/generate-wrapper-code.js +123 -345
- package/lib/esm/utils/ts-morph-utils.js +217 -0
- package/lib/esm/utils/zod-utils.js +269 -0
- package/lib/types/constants/new-block-template/index.d.ts +13 -6
- package/lib/types/plugins/_theme.d.ts +5 -1
- package/lib/types/tsconfig.tsbuildinfo +1 -1
- package/lib/types/types/properties.d.ts +17 -0
- package/lib/types/utils/block-props-utils.d.ts +103 -0
- package/lib/types/utils/generate-wrapper-code.d.ts +24 -0
- package/lib/types/utils/ts-morph-utils.d.ts +24 -0
- package/lib/types/utils/zod-utils.d.ts +67 -0
- package/package.json +20 -4
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.createTsMorphProject = createTsMorphProject;
|
|
37
|
+
exports.extractTypeWithDependencies = extractTypeWithDependencies;
|
|
38
|
+
exports.combineTypeDefinitions = combineTypeDefinitions;
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const ts_morph_1 = require("ts-morph");
|
|
41
|
+
/**
|
|
42
|
+
* 创建 ts-morph 项目
|
|
43
|
+
* @param tsConfigFilePath tsconfig.json 文件路径
|
|
44
|
+
*/
|
|
45
|
+
function createTsMorphProject(tsConfigFilePath = path.resolve(process.cwd(), 'tsconfig.json')) {
|
|
46
|
+
return new ts_morph_1.Project({
|
|
47
|
+
tsConfigFilePath,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 解析类型定义文件,提取指定类型及其依赖
|
|
52
|
+
* @param sourceFilePath 源文件路径
|
|
53
|
+
* @param typeName 要提取的类型名称
|
|
54
|
+
* @param tsConfigFilePath tsconfig.json 文件路径
|
|
55
|
+
*/
|
|
56
|
+
function extractTypeWithDependencies(sourceFilePath, typeName, tsConfigFilePath = path.resolve(process.cwd(), 'tsconfig.json')) {
|
|
57
|
+
// 创建项目
|
|
58
|
+
const project = createTsMorphProject(tsConfigFilePath);
|
|
59
|
+
// 添加源文件
|
|
60
|
+
const sourceFile = project.addSourceFileAtPath(sourceFilePath);
|
|
61
|
+
// 收集所有导入语句
|
|
62
|
+
const imports = sourceFile.getImportDeclarations().map((imp) => imp.getText());
|
|
63
|
+
// 查找指定接口或类型别名
|
|
64
|
+
const targetInterface = sourceFile.getInterface(typeName);
|
|
65
|
+
const targetTypeAlias = sourceFile.getTypeAlias(typeName);
|
|
66
|
+
// 存储所有收集到的类型定义
|
|
67
|
+
const dependencies = new Map();
|
|
68
|
+
if (targetInterface) {
|
|
69
|
+
// 如果是接口,获取其文本
|
|
70
|
+
const typeText = targetInterface.getText();
|
|
71
|
+
// 收集接口依赖
|
|
72
|
+
collectTypeDependencies(targetInterface, dependencies, project, imports);
|
|
73
|
+
return { typeText, dependencies, imports };
|
|
74
|
+
}
|
|
75
|
+
if (targetTypeAlias) {
|
|
76
|
+
// 如果是类型别名,获取其文本
|
|
77
|
+
const typeText = targetTypeAlias.getText();
|
|
78
|
+
// 收集类型别名依赖
|
|
79
|
+
collectTypeDependencies(targetTypeAlias, dependencies, project, imports);
|
|
80
|
+
return { typeText, dependencies, imports };
|
|
81
|
+
}
|
|
82
|
+
throw new Error(`Type '${typeName}' not found in ${sourceFilePath}`);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 收集类型依赖
|
|
86
|
+
* @param node 类型节点
|
|
87
|
+
* @param dependencies 依赖集合
|
|
88
|
+
* @param project ts-morph 项目
|
|
89
|
+
* @param imports 导入语句集合
|
|
90
|
+
*/
|
|
91
|
+
function collectTypeDependencies(node, dependencies, project, imports) {
|
|
92
|
+
// 获取类型引用
|
|
93
|
+
const typeReferences = findTypeReferences(node);
|
|
94
|
+
// 处理每个类型引用
|
|
95
|
+
typeReferences.forEach((referenceName) => {
|
|
96
|
+
// 排除内置类型和已处理类型
|
|
97
|
+
if (isBuiltInType(referenceName) ||
|
|
98
|
+
dependencies.has(referenceName) ||
|
|
99
|
+
referenceName === node.getName() // 避免循环引用
|
|
100
|
+
) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
// 在整个项目中查找类型定义
|
|
105
|
+
const { declaration, definitionText, sourceFileImports } = findTypeDefinitionInProject(referenceName, project);
|
|
106
|
+
if (declaration && definitionText) {
|
|
107
|
+
// 存储找到的类型定义
|
|
108
|
+
dependencies.set(referenceName, definitionText);
|
|
109
|
+
// 添加源文件的导入语句
|
|
110
|
+
if (sourceFileImports && sourceFileImports.length > 0) {
|
|
111
|
+
sourceFileImports.forEach((importText) => {
|
|
112
|
+
if (!imports.includes(importText)) {
|
|
113
|
+
imports.push(importText);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
// 递归收集该类型的依赖
|
|
118
|
+
if (ts_morph_1.Node.isInterfaceDeclaration(declaration) || ts_morph_1.Node.isTypeAliasDeclaration(declaration)) {
|
|
119
|
+
collectTypeDependencies(declaration, dependencies, project, imports);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
console.warn(`Could not find definition for type: ${referenceName}`, error);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* 从节点中查找所有类型引用
|
|
130
|
+
* @param node 类型节点
|
|
131
|
+
*/
|
|
132
|
+
function findTypeReferences(node) {
|
|
133
|
+
const references = [];
|
|
134
|
+
// 递归查找所有类型引用
|
|
135
|
+
function findReferences(currentNode) {
|
|
136
|
+
if (ts_morph_1.Node.isTypeReference(currentNode)) {
|
|
137
|
+
const typeName = currentNode.getTypeName().getText();
|
|
138
|
+
if (!references.includes(typeName)) {
|
|
139
|
+
references.push(typeName);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
currentNode.forEachChild(findReferences);
|
|
143
|
+
}
|
|
144
|
+
findReferences(node);
|
|
145
|
+
return references;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* 判断是否为内置类型
|
|
149
|
+
* @param typeName 类型名称
|
|
150
|
+
*/
|
|
151
|
+
function isBuiltInType(typeName) {
|
|
152
|
+
const builtInTypes = [
|
|
153
|
+
'String',
|
|
154
|
+
'Number',
|
|
155
|
+
'Boolean',
|
|
156
|
+
'Array',
|
|
157
|
+
'Object',
|
|
158
|
+
'Date',
|
|
159
|
+
'RegExp',
|
|
160
|
+
'Function',
|
|
161
|
+
'Promise',
|
|
162
|
+
'any',
|
|
163
|
+
'unknown',
|
|
164
|
+
'never',
|
|
165
|
+
'void',
|
|
166
|
+
'string',
|
|
167
|
+
'number',
|
|
168
|
+
'boolean',
|
|
169
|
+
'symbol',
|
|
170
|
+
'bigint',
|
|
171
|
+
'undefined',
|
|
172
|
+
'null',
|
|
173
|
+
'Record',
|
|
174
|
+
'Partial',
|
|
175
|
+
'Required',
|
|
176
|
+
'Readonly',
|
|
177
|
+
'Pick',
|
|
178
|
+
'Omit',
|
|
179
|
+
'Exclude',
|
|
180
|
+
'Extract',
|
|
181
|
+
'NonNullable',
|
|
182
|
+
'Parameters',
|
|
183
|
+
'ConstructorParameters',
|
|
184
|
+
'ReturnType',
|
|
185
|
+
'InstanceType',
|
|
186
|
+
'React',
|
|
187
|
+
'ReactNode',
|
|
188
|
+
'JSX',
|
|
189
|
+
];
|
|
190
|
+
return builtInTypes.includes(typeName) || typeName.startsWith('React.');
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* 在项目中查找类型定义
|
|
194
|
+
* @param typeName 类型名称
|
|
195
|
+
* @param project ts-morph 项目
|
|
196
|
+
*/
|
|
197
|
+
function findTypeDefinitionInProject(typeName, project) {
|
|
198
|
+
// 在所有源文件中查找
|
|
199
|
+
for (const sourceFile of project.getSourceFiles()) {
|
|
200
|
+
// 尝试查找接口定义
|
|
201
|
+
const interfaceDecl = sourceFile.getInterface(typeName);
|
|
202
|
+
if (interfaceDecl) {
|
|
203
|
+
const imports = sourceFile.getImportDeclarations().map((imp) => imp.getText());
|
|
204
|
+
return {
|
|
205
|
+
declaration: interfaceDecl,
|
|
206
|
+
definitionText: interfaceDecl.getText(),
|
|
207
|
+
sourceFileImports: imports,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
// 尝试查找类型别名定义
|
|
211
|
+
const typeAliasDecl = sourceFile.getTypeAlias(typeName);
|
|
212
|
+
if (typeAliasDecl) {
|
|
213
|
+
const imports = sourceFile.getImportDeclarations().map((imp) => imp.getText());
|
|
214
|
+
return {
|
|
215
|
+
declaration: typeAliasDecl,
|
|
216
|
+
definitionText: typeAliasDecl.getText(),
|
|
217
|
+
sourceFileImports: imports,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
// 尝试查找枚举定义
|
|
221
|
+
const enumDecl = sourceFile.getEnum(typeName);
|
|
222
|
+
if (enumDecl) {
|
|
223
|
+
const imports = sourceFile.getImportDeclarations().map((imp) => imp.getText());
|
|
224
|
+
return {
|
|
225
|
+
declaration: enumDecl,
|
|
226
|
+
definitionText: enumDecl.getText(),
|
|
227
|
+
sourceFileImports: imports,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return { declaration: undefined, definitionText: undefined };
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* 组合类型及其依赖为完整的类型定义文本
|
|
235
|
+
* @param typeText 主类型文本
|
|
236
|
+
* @param dependencies 依赖类型集合
|
|
237
|
+
* @param imports 导入语句集合
|
|
238
|
+
*/
|
|
239
|
+
function combineTypeDefinitions(typeText, dependencies, imports = []) {
|
|
240
|
+
let combinedText = '';
|
|
241
|
+
// 首先添加所有导入语句
|
|
242
|
+
if (imports.length > 0) {
|
|
243
|
+
imports.forEach((importText) => {
|
|
244
|
+
combinedText += `${importText}\n`;
|
|
245
|
+
});
|
|
246
|
+
combinedText += '\n';
|
|
247
|
+
}
|
|
248
|
+
// 添加所有依赖类型
|
|
249
|
+
dependencies.forEach((defText) => {
|
|
250
|
+
combinedText += `${defText}\n\n`;
|
|
251
|
+
});
|
|
252
|
+
// 添加主类型
|
|
253
|
+
combinedText += typeText;
|
|
254
|
+
return combinedText;
|
|
255
|
+
}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.PROPERTIES_TYPE_SCHEMA = void 0;
|
|
40
|
+
exports.propertiesToZodSchema = propertiesToZodSchema;
|
|
41
|
+
exports.jsonSchemaToProperties = jsonSchemaToProperties;
|
|
42
|
+
exports.zodSchemaToTypeString = zodSchemaToTypeString;
|
|
43
|
+
exports.zodSchemaToJsonSchema = zodSchemaToJsonSchema;
|
|
44
|
+
exports.tsFileToZodSchema = tsFileToZodSchema;
|
|
45
|
+
exports.tsFileInterfaceToProperties = tsFileInterfaceToProperties;
|
|
46
|
+
const config_1 = __importDefault(require("@blocklet/sdk/lib/config"));
|
|
47
|
+
const fs_1 = require("fs");
|
|
48
|
+
const lowerFirst_1 = __importDefault(require("lodash/lowerFirst"));
|
|
49
|
+
const nanoid_1 = require("nanoid");
|
|
50
|
+
const path = __importStar(require("path"));
|
|
51
|
+
const ts_to_zod_1 = require("ts-to-zod");
|
|
52
|
+
const zod_1 = require("zod");
|
|
53
|
+
const zod_to_json_schema_1 = require("zod-to-json-schema");
|
|
54
|
+
const zod_to_ts_1 = require("zod-to-ts");
|
|
55
|
+
const helper_1 = require("./helper");
|
|
56
|
+
const ts_morph_utils_1 = require("./ts-morph-utils");
|
|
57
|
+
const PROPERTY_PREFIX = '@description';
|
|
58
|
+
const JOIN_SYMBOL = ' | ';
|
|
59
|
+
// 转换为: type: string | visible: boolean
|
|
60
|
+
const stringifyZodDescribe = (properties, extra = {}) => {
|
|
61
|
+
return `${PROPERTY_PREFIX} ${Object.entries({
|
|
62
|
+
id: properties.id ?? '',
|
|
63
|
+
type: properties.type ?? 'string',
|
|
64
|
+
visible: properties.visible ?? true,
|
|
65
|
+
...extra,
|
|
66
|
+
})
|
|
67
|
+
.map(([key, value]) => {
|
|
68
|
+
return `${key}: ${value}`;
|
|
69
|
+
})
|
|
70
|
+
.join(JOIN_SYMBOL)}`;
|
|
71
|
+
};
|
|
72
|
+
const parseZodDescribe = (describe) => {
|
|
73
|
+
const describeList = describe.split(JOIN_SYMBOL);
|
|
74
|
+
const result = {};
|
|
75
|
+
describeList.forEach((item) => {
|
|
76
|
+
const [key, value] = item.split(':');
|
|
77
|
+
const realKey = key.trim();
|
|
78
|
+
const realValue = value.trim();
|
|
79
|
+
if (['true', 'false'].includes(realValue)) {
|
|
80
|
+
result[realKey] = realValue === 'true';
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
result[realKey] = realValue;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
return result;
|
|
87
|
+
};
|
|
88
|
+
const getJSONSchemaDescribe = (jsonSchema) => {
|
|
89
|
+
let result = {};
|
|
90
|
+
if (jsonSchema.description) {
|
|
91
|
+
result = parseZodDescribe(jsonSchema.description);
|
|
92
|
+
}
|
|
93
|
+
return result;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Properties 类型与 Schema 类型的映射关系
|
|
97
|
+
* blocklets/pages-kit/src/pages/page-maker/custom-component/setting.tsx
|
|
98
|
+
*/
|
|
99
|
+
exports.PROPERTIES_TYPE_SCHEMA = {
|
|
100
|
+
// Properties 类型到 Zod schema 的映射
|
|
101
|
+
propertyToZod: {
|
|
102
|
+
string: () => zod_1.z.string(),
|
|
103
|
+
multiline: () => zod_1.z.string(),
|
|
104
|
+
number: () => zod_1.z.number(),
|
|
105
|
+
decimal: () => zod_1.z.number(),
|
|
106
|
+
boolean: () => zod_1.z.boolean(),
|
|
107
|
+
color: () => zod_1.z.string(),
|
|
108
|
+
url: () => zod_1.z.object({
|
|
109
|
+
url: zod_1.z.string(),
|
|
110
|
+
mediaKitUrl: zod_1.z.string().optional(),
|
|
111
|
+
width: zod_1.z.number().optional(),
|
|
112
|
+
height: zod_1.z.number().optional(),
|
|
113
|
+
}),
|
|
114
|
+
json: (properties, { addZodDescribe, propLLMConfig }) => {
|
|
115
|
+
const { subProperties } = properties;
|
|
116
|
+
if (subProperties && Object.keys(subProperties)?.length > 0) {
|
|
117
|
+
// 递归处理子属性
|
|
118
|
+
return propertiesToZodSchema(subProperties, { addZodDescribe, llmConfig: propLLMConfig?.subProperties });
|
|
119
|
+
}
|
|
120
|
+
return zod_1.z.object({});
|
|
121
|
+
},
|
|
122
|
+
yaml: (properties, { addZodDescribe, propLLMConfig }) => {
|
|
123
|
+
const { subProperties } = properties;
|
|
124
|
+
if (subProperties && Object.keys(subProperties)?.length > 0) {
|
|
125
|
+
// 使用联合类型:字符串或结构化对象
|
|
126
|
+
return zod_1.z.union([
|
|
127
|
+
zod_1.z.string(),
|
|
128
|
+
propertiesToZodSchema(subProperties, { addZodDescribe, llmConfig: propLLMConfig?.subProperties }),
|
|
129
|
+
]);
|
|
130
|
+
}
|
|
131
|
+
return zod_1.z.string();
|
|
132
|
+
},
|
|
133
|
+
array: (properties, { addZodDescribe, propLLMConfig }) => {
|
|
134
|
+
const { subProperties } = properties;
|
|
135
|
+
if (subProperties && Object.keys(subProperties)?.length > 0) {
|
|
136
|
+
// 递归处理子属性
|
|
137
|
+
return zod_1.z.array(propertiesToZodSchema(subProperties, { addZodDescribe, llmConfig: propLLMConfig?.subProperties }));
|
|
138
|
+
}
|
|
139
|
+
return zod_1.z.array(zod_1.z.any());
|
|
140
|
+
},
|
|
141
|
+
component: () => zod_1.z.any(),
|
|
142
|
+
custom: () => zod_1.z.any(),
|
|
143
|
+
default: () => zod_1.z.string(),
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* 从 Properties 对象创建 Zod schema
|
|
148
|
+
*/
|
|
149
|
+
function propertiesToZodSchema(properties, { addZodDescribe = false,
|
|
150
|
+
// 这个目前来说都是可选的,避免用户在使用的过程中疯狂报错,并且目前 setting 中没有必填标识符
|
|
151
|
+
isOptional = true, llmConfig, } = {}) {
|
|
152
|
+
const schemaObj = {};
|
|
153
|
+
Object.entries(properties || {}).forEach(([key, prop]) => {
|
|
154
|
+
if (!prop.data)
|
|
155
|
+
return;
|
|
156
|
+
// 如果key未定义,使用id
|
|
157
|
+
const propKey = prop.data.key || prop.data.id || key;
|
|
158
|
+
// prop llmConfig
|
|
159
|
+
const propLLMConfig = llmConfig?.[prop.data.id];
|
|
160
|
+
// 是否不需要 LLM 处理
|
|
161
|
+
if (propLLMConfig && !propLLMConfig.isNeedGenerate) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const propType = prop.data.type;
|
|
165
|
+
const getSchemaFn = (propType in exports.PROPERTIES_TYPE_SCHEMA.propertyToZod
|
|
166
|
+
? exports.PROPERTIES_TYPE_SCHEMA.propertyToZod[propType]
|
|
167
|
+
: exports.PROPERTIES_TYPE_SCHEMA.propertyToZod.default);
|
|
168
|
+
schemaObj[propKey] = getSchemaFn(prop.data, { addZodDescribe, propLLMConfig });
|
|
169
|
+
if (isOptional) {
|
|
170
|
+
schemaObj[propKey] = schemaObj[propKey].optional();
|
|
171
|
+
}
|
|
172
|
+
let propDescribe = '';
|
|
173
|
+
if (addZodDescribe) {
|
|
174
|
+
propDescribe = stringifyZodDescribe(prop.data, propLLMConfig?.describe ? { llmDescribe: propLLMConfig?.describe } : {});
|
|
175
|
+
}
|
|
176
|
+
else if (propLLMConfig?.describe) {
|
|
177
|
+
propDescribe = propLLMConfig?.describe;
|
|
178
|
+
}
|
|
179
|
+
if (propDescribe) {
|
|
180
|
+
schemaObj[propKey] = schemaObj[propKey].describe(propDescribe);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
return zod_1.z.object(schemaObj);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* 从JSON Schema生成Properties对象
|
|
187
|
+
*/
|
|
188
|
+
function jsonSchemaToProperties(jsonSchema, { existingProperties, key, isRoot, index = 0, } = {}) {
|
|
189
|
+
let properties = {};
|
|
190
|
+
let loopProperties = {};
|
|
191
|
+
if (jsonSchema.type === 'array') {
|
|
192
|
+
loopProperties = jsonSchema.items?.properties;
|
|
193
|
+
}
|
|
194
|
+
else if (jsonSchema.type === 'object') {
|
|
195
|
+
loopProperties = jsonSchema.properties;
|
|
196
|
+
}
|
|
197
|
+
const schemaDescribe = getJSONSchemaDescribe(jsonSchema);
|
|
198
|
+
const { type, visible } = schemaDescribe;
|
|
199
|
+
const id = schemaDescribe.id || (0, nanoid_1.nanoid)(helper_1.NANOID_LENGTH);
|
|
200
|
+
properties[id] = {
|
|
201
|
+
index,
|
|
202
|
+
data: {
|
|
203
|
+
id,
|
|
204
|
+
key,
|
|
205
|
+
type,
|
|
206
|
+
visible,
|
|
207
|
+
locales: (id && existingProperties?.[id]?.data?.locales) || {
|
|
208
|
+
en: { name: key },
|
|
209
|
+
zh: { name: key },
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
if (loopProperties && Object.keys(loopProperties)?.length > 0) {
|
|
214
|
+
let subIndex = 0;
|
|
215
|
+
properties[id].data.subProperties = {};
|
|
216
|
+
// get sub existing properties
|
|
217
|
+
const subExistingProperties = existingProperties?.[id]?.data?.subProperties ?? existingProperties;
|
|
218
|
+
Object.entries(loopProperties).forEach(([key, item]) => {
|
|
219
|
+
properties[id].data.subProperties = {
|
|
220
|
+
...properties[id].data.subProperties,
|
|
221
|
+
...jsonSchemaToProperties(item, { existingProperties: subExistingProperties, key, index: subIndex++ }),
|
|
222
|
+
};
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
if (isRoot) {
|
|
226
|
+
properties = properties[id].data.subProperties;
|
|
227
|
+
}
|
|
228
|
+
return properties;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* 从Zod schema生成TypeScript类型字符串
|
|
232
|
+
*/
|
|
233
|
+
function zodSchemaToTypeString(schema, typeName, removeUndefined = true) {
|
|
234
|
+
const { node } = (0, zod_to_ts_1.zodToTs)(schema, typeName);
|
|
235
|
+
let output = (0, zod_to_ts_1.printNode)(node);
|
|
236
|
+
if (removeUndefined) {
|
|
237
|
+
// 移除所有的 "| undefined"
|
|
238
|
+
output = output.replace(/\s+\|\s+undefined/g, '');
|
|
239
|
+
}
|
|
240
|
+
return output;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* 从Zod schema生成JSON Schema
|
|
244
|
+
*/
|
|
245
|
+
function zodSchemaToJsonSchema(schema) {
|
|
246
|
+
return (0, zod_to_json_schema_1.zodToJsonSchema)(schema, { $refStrategy: 'none' });
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* 从TypeScript文件中的类型声明生成Zod schema
|
|
250
|
+
* 支持处理外部引用类型
|
|
251
|
+
* @param sourceFilePath 源文件路径
|
|
252
|
+
* @param typeName 类型名称
|
|
253
|
+
* @param tsConfigFilePath tsconfig.json 路径,默认为项目根目录下的tsconfig.json
|
|
254
|
+
*/
|
|
255
|
+
async function tsFileToZodSchema(sourceFilePath, typeName) {
|
|
256
|
+
try {
|
|
257
|
+
const { typeText, dependencies, imports } = (0, ts_morph_utils_1.extractTypeWithDependencies)(sourceFilePath, typeName);
|
|
258
|
+
// set imports to empty array
|
|
259
|
+
const combinedTypeDefinition = (0, ts_morph_utils_1.combineTypeDefinitions)(typeText, dependencies, []);
|
|
260
|
+
// 使用 ts-to-zod 生成代码,需要手动添加 imports 和 combinedTypeDefinition,因为 zod-to-ts 对 import 处理存在问题
|
|
261
|
+
const result = (0, ts_to_zod_1.generate)({
|
|
262
|
+
sourceText: combinedTypeDefinition,
|
|
263
|
+
keepComments: true,
|
|
264
|
+
});
|
|
265
|
+
// 提取生成的 Zod schema 代码
|
|
266
|
+
const zodSchemaText = result.getZodSchemasFile('zod');
|
|
267
|
+
// 创建临时目录
|
|
268
|
+
const tmpDir = path.resolve(config_1.default.env.dataDir, 'temp');
|
|
269
|
+
try {
|
|
270
|
+
await fs_1.promises.stat(tmpDir);
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
// 如果目录不存在,stat 会抛出错误
|
|
274
|
+
await fs_1.promises.mkdir(tmpDir, { recursive: true });
|
|
275
|
+
}
|
|
276
|
+
const fileName = `zodSchema_${typeName}_${(0, nanoid_1.nanoid)(helper_1.NANOID_LENGTH)}.ts`;
|
|
277
|
+
const filePath = path.join(tmpDir, fileName);
|
|
278
|
+
// 添加 ts-to-zod 的 imports 和 zodSchemaText
|
|
279
|
+
const zodSchemaTextWithImports = `
|
|
280
|
+
${imports.join('\n')}
|
|
281
|
+
|
|
282
|
+
${zodSchemaText}
|
|
283
|
+
`;
|
|
284
|
+
// 写入临时文件
|
|
285
|
+
await fs_1.promises.writeFile(filePath, zodSchemaTextWithImports);
|
|
286
|
+
// 动态导入生成的模块
|
|
287
|
+
const module = await Promise.resolve(`${`file://${filePath}`}`).then(s => __importStar(require(s)));
|
|
288
|
+
// 首字母转换为小写
|
|
289
|
+
const schemaName = `${(0, lowerFirst_1.default)(typeName)}Schema`;
|
|
290
|
+
// 获取 schema
|
|
291
|
+
const schema = module[schemaName];
|
|
292
|
+
// 清理临时文件
|
|
293
|
+
await fs_1.promises.unlink(filePath);
|
|
294
|
+
return schema;
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
console.error('Failed to generate Zod schema from TypeScript file:', error);
|
|
298
|
+
console.error('Error details:', error);
|
|
299
|
+
return zod_1.z.object({}).passthrough();
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* 从TypeScript文件中的类型生成Properties对象
|
|
304
|
+
* 支持处理外部引用类型
|
|
305
|
+
* @param sourceFilePath 源文件路径
|
|
306
|
+
* @param typeName 类型名称
|
|
307
|
+
* @param existingProperties 已存在的properties对象
|
|
308
|
+
*/
|
|
309
|
+
async function tsFileInterfaceToProperties(sourceFilePath, typeName, existingProperties = {}) {
|
|
310
|
+
const zodSchema = await tsFileToZodSchema(sourceFilePath, typeName);
|
|
311
|
+
const jsonSchema = zodSchemaToJsonSchema(zodSchema);
|
|
312
|
+
// get jsonSchemaToProperties and set isRoot to true
|
|
313
|
+
return jsonSchemaToProperties(jsonSchema, { existingProperties, key: '', isRoot: true });
|
|
314
|
+
}
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
-
export interface
|
|
3
|
+
export interface BlockProps {
|
|
4
|
+
/** @description id: gs1rn5jmxfvpxptx | type: string | visible: true */
|
|
4
5
|
title?: string;
|
|
5
|
-
|
|
6
|
+
/** @description id: 9ajrz12ik7esfk1z | type: string | visible: true */
|
|
6
7
|
description?: string;
|
|
8
|
+
/** @description id: 3ckcfvf6b7zyskk8 | type: url | visible: true */
|
|
9
|
+
logo?: {
|
|
10
|
+
url: string;
|
|
11
|
+
mediaKitUrl?: string;
|
|
12
|
+
width?: number;
|
|
13
|
+
height?: number;
|
|
14
|
+
};
|
|
15
|
+
/** @description id: x3lqht8ikble1itx | type: string | visible: false */
|
|
7
16
|
copyright?: string;
|
|
8
17
|
}
|
|
9
18
|
|
|
10
19
|
// default export
|
|
11
|
-
export default function HelloWorld({ title = 'Hello World', logo, description, copyright }:
|
|
20
|
+
export default function HelloWorld({ title = 'Hello World', logo, description, copyright }: BlockProps) {
|
|
12
21
|
return (
|
|
13
22
|
<div
|
|
14
23
|
style={{
|
|
@@ -52,7 +61,7 @@ export default function HelloWorld({ title = 'Hello World', logo, description, c
|
|
|
52
61
|
}
|
|
53
62
|
|
|
54
63
|
// export edit component
|
|
55
|
-
export const EditComponent: React.FC<
|
|
64
|
+
export const EditComponent: React.FC<BlockProps & { onChange?: (value: BlockProps) => void }> = ({
|
|
56
65
|
onChange,
|
|
57
66
|
...props
|
|
58
67
|
}) => {
|