worsoft-frontend-codegen-local-mcp 0.1.10 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/mcp_server.js +36 -36
- package/package.json +1 -1
package/mcp_server.js
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
|
|
7
|
-
const SERVER_NAME = 'worsoft-codegen-local';
|
|
8
|
-
const SERVER_VERSION = '0.1.
|
|
7
|
+
const SERVER_NAME = 'worsoft-codegen-local';
|
|
8
|
+
const SERVER_VERSION = '0.1.11';
|
|
9
9
|
const PROTOCOL_VERSION = '2024-11-05';
|
|
10
10
|
const TOOL_NAME = 'worsoft_codegen_local_generate_frontend';
|
|
11
11
|
const TEMPLATE_LIBRARY_ROOT = path.resolve(__dirname, '..', 'template');
|
|
12
12
|
const STYLE_CATALOG_PATH = path.join(__dirname, 'assets', 'style-catalog.json');
|
|
13
|
-
const DEFAULT_DESIGN_FILE = path.resolve(__dirname, '..', 'sql', 'SQL
|
|
13
|
+
const DEFAULT_DESIGN_FILE = path.resolve(__dirname, '..', 'sql', 'SQL 闁荤姳鐒﹀畷姗€顢橀崫銉﹀珰閻庢稒蓱椤?md');
|
|
14
14
|
const STYLE_CATALOG = loadStyleCatalog();
|
|
15
15
|
const DEFAULT_DICT_REGISTRY_KEYS = {
|
|
16
16
|
add_start_stop: 'COMMON_STATUS',
|
|
@@ -127,7 +127,7 @@ export function createCrudSchema(
|
|
|
127
127
|
const TOOL_SCHEMA = {
|
|
128
128
|
type: 'object',
|
|
129
129
|
properties: {
|
|
130
|
-
designFile: { type: 'string', description: 'Absolute or relative Markdown design file path. Defaults to ../sql/SQL
|
|
130
|
+
designFile: { type: 'string', description: 'Absolute or relative Markdown design file path. Defaults to ../sql/SQL 闁荤姳鐒﹀畷姗€顢橀崫銉﹀珰閻庢稒蓱椤?md when omitted.' },
|
|
131
131
|
tableName: { type: 'string', description: 'Target main table name from the design file.' },
|
|
132
132
|
style: { type: 'string', enum: Object.keys(STYLE_CATALOG), description: 'Style id from assets/style-catalog.json.' },
|
|
133
133
|
children: {
|
|
@@ -549,7 +549,7 @@ function isAuditField(fieldName) {
|
|
|
549
549
|
|
|
550
550
|
function findDictType(comment) {
|
|
551
551
|
return extractDictType(comment);
|
|
552
|
-
const match = comment.match(/(
|
|
552
|
+
const match = comment.match(/(?:闁诲孩绋掗〃鍛村触閳х憢dict)[闂?\s]*([a-zA-Z0-9_]+)/i);
|
|
553
553
|
}
|
|
554
554
|
|
|
555
555
|
function mapFieldType(field) {
|
|
@@ -588,7 +588,7 @@ function stripDictAnnotation(label) {
|
|
|
588
588
|
return '';
|
|
589
589
|
}
|
|
590
590
|
return text
|
|
591
|
-
.replace(/\s*[\(
|
|
591
|
+
.replace(/\s*[\((][^()()]*?(?:字典|dict)(?:[_\s-]*type)?[^()()]*?[\))]\s*/gi, '')
|
|
592
592
|
.replace(/\s+/g, ' ')
|
|
593
593
|
.trim();
|
|
594
594
|
}
|
|
@@ -600,8 +600,8 @@ function extractDictType(text) {
|
|
|
600
600
|
}
|
|
601
601
|
|
|
602
602
|
const patterns = [
|
|
603
|
-
/(
|
|
604
|
-
/[\(
|
|
603
|
+
/(?:鐎涙鍚€|dict)(?:[_\s-]*type)?\s*[:閿涙瓥\s*([a-zA-Z0-9_-]+)/i,
|
|
604
|
+
/[\(閿涘溈\s*(?:鐎涙鍚€|dict)(?:[_\s-]*type)?\s*[:閿涙瓥?\s*([a-zA-Z0-9_-]+)\s*[\)閿涘、/i,
|
|
605
605
|
];
|
|
606
606
|
|
|
607
607
|
for (const pattern of patterns) {
|
|
@@ -641,7 +641,7 @@ function normalizeDefaultValue(value) {
|
|
|
641
641
|
|
|
642
642
|
function parseRequiredFlag(value) {
|
|
643
643
|
const normalized = String(value || '').trim().toLowerCase();
|
|
644
|
-
return ['
|
|
644
|
+
return ['闂?, 'y', 'yes', '1', 'true', '闂婎偄娲ら幊搴ㄦ晲?].includes(normalized);
|
|
645
645
|
}
|
|
646
646
|
|
|
647
647
|
function parseMarkdownRow(line) {
|
|
@@ -667,7 +667,7 @@ function findPrimaryKeyFromText(text, fields) {
|
|
|
667
667
|
return quotedMatch[1];
|
|
668
668
|
}
|
|
669
669
|
|
|
670
|
-
const commentPk = fields.find((field) =>
|
|
670
|
+
const commentPk = fields.find((field) => /婵炴垶鎸搁…鐑藉极?i.test(field.comment));
|
|
671
671
|
if (commentPk) {
|
|
672
672
|
return commentPk.fieldName;
|
|
673
673
|
}
|
|
@@ -700,14 +700,14 @@ function parseMarkdownTableSection(tableName, tableComment, sectionLines) {
|
|
|
700
700
|
|
|
701
701
|
const headers = rows[0];
|
|
702
702
|
const dataRows = rows.slice(1).filter((cells) => !isMarkdownSeparatorRow(cells));
|
|
703
|
-
const fieldNameIndex = findHeaderIndex(headers, [
|
|
704
|
-
const sqlTypeIndex = findHeaderIndex(headers, [
|
|
705
|
-
const lengthIndex = findHeaderIndex(headers, [
|
|
706
|
-
const requiredIndex = findHeaderIndex(headers, [
|
|
707
|
-
const defaultIndex = findHeaderIndex(headers, [
|
|
708
|
-
const commentIndex = findHeaderIndex(headers, [
|
|
703
|
+
const fieldNameIndex = findHeaderIndex(headers, [/闁诲孩绋掗〃鍡涱敊瀹€鍕Е?, /闂佸憡甯楅〃鍛村箖?i, /field/i]);
|
|
704
|
+
const sqlTypeIndex = findHeaderIndex(headers, [/缂備緡鍋夐褔鎮?, /闂佽桨鑳舵晶妤€鐣垫担铏瑰暗閻犲洩灏欓埀?, /type/i]);
|
|
705
|
+
const lengthIndex = findHeaderIndex(headers, [/闂傚倵鍋撻柛顭戝枛椤?, /缂備緡鍠楅崕宕団偓?, /length/i]);
|
|
706
|
+
const requiredIndex = findHeaderIndex(headers, [/闂婎偄娲ら幊搴ㄦ晲?, /闂婎偄娲ら幊鎾舵?, /required/i]);
|
|
707
|
+
const defaultIndex = findHeaderIndex(headers, [/婵帗绋掗…鍫ヮ敇?, /default/i]);
|
|
708
|
+
const commentIndex = findHeaderIndex(headers, [/闁荤姴娲ら悺銊ノ?, /婵犮垼娉涘ú锕傚极?, /comment/i]);
|
|
709
709
|
|
|
710
|
-
const dictTypeIndex = findHeaderIndex(headers, [
|
|
710
|
+
const dictTypeIndex = findHeaderIndex(headers, [/闁诲孩绋掗〃鍛村触閳?, /dict/i, /dict_?type/i]);
|
|
711
711
|
|
|
712
712
|
if (fieldNameIndex < 0 || sqlTypeIndex < 0) {
|
|
713
713
|
throw new Error('Markdown field table headers are missing required columns for ' + tableName);
|
|
@@ -755,7 +755,7 @@ function parseMarkdownDesignTables(markdownText) {
|
|
|
755
755
|
|
|
756
756
|
for (let index = 0; index < lines.length; index += 1) {
|
|
757
757
|
const heading = lines[index].trim();
|
|
758
|
-
const headingMatch = heading.match(/^###\s+\S+\s+([a-zA-Z0-9_]+)\s*[\(
|
|
758
|
+
const headingMatch = heading.match(/^###\s+\S+\s+([a-zA-Z0-9_]+)\s*[\(闂佹寧绋戝┃?[^)\n闂佹寧绋戦妴?)[\)闂佹寧绋戦妴?);
|
|
759
759
|
if (!headingMatch) continue;
|
|
760
760
|
|
|
761
761
|
const tableName = headingMatch[1];
|
|
@@ -814,9 +814,9 @@ function parseMarkdownRelations(markdownText) {
|
|
|
814
814
|
const relations = [];
|
|
815
815
|
for (const block of blocks) {
|
|
816
816
|
const lines = block.body.split('\n').map((line) => line.trim()).filter(Boolean);
|
|
817
|
-
const mainLine = lines.find((line) => /(
|
|
818
|
-
const childLine = lines.find((line) => /(
|
|
819
|
-
const fieldLine = lines.find((line) =>
|
|
817
|
+
const mainLine = lines.find((line) => /(婵炴垶鎸诲Σ鎺楀Υ閸庡嫰鏌i弽銊ュ姸闁?\s*[:闂佹寧绋掗悺?.test(line));
|
|
818
|
+
const childLine = lines.find((line) => /(婵炲濮村锕傚Υ閸庡嫰鎮楀☉娆忓闁?\s*[:闂佹寧绋掗悺?.test(line));
|
|
819
|
+
const fieldLine = lines.find((line) => /闂佺绻愰悿鍥ㄧ閸懇鍋撳☉娆樻畷妞ゆ柨顒竤*[:闂佹寧绋掗悺?.test(line));
|
|
820
820
|
if (!mainLine || !childLine || !fieldLine) continue;
|
|
821
821
|
|
|
822
822
|
const mainIdentifiers = extractIdentifiersFromLine(mainLine);
|
|
@@ -824,14 +824,14 @@ function parseMarkdownRelations(markdownText) {
|
|
|
824
824
|
const fieldIdentifiers = extractIdentifiersFromLine(fieldLine);
|
|
825
825
|
if (!mainIdentifiers.length || !childIdentifiers.length || fieldIdentifiers.length < 2) continue;
|
|
826
826
|
|
|
827
|
-
const relationTypeLine = lines.find((line) =>
|
|
827
|
+
const relationTypeLine = lines.find((line) => /闂佺绻愮壕顓㈡焾鐎靛摜灏甸悹鍥皺閳ь剛锕*[:闂佹寧绋掗悺?.test(line));
|
|
828
828
|
relations.push({
|
|
829
829
|
title: block.title,
|
|
830
830
|
mainTableName: mainIdentifiers[0],
|
|
831
831
|
childTableName: childIdentifiers[0],
|
|
832
832
|
childField: fieldIdentifiers[0],
|
|
833
833
|
mainField: fieldIdentifiers[1],
|
|
834
|
-
relationType: relationTypeLine ? relationTypeLine.split(/[
|
|
834
|
+
relationType: relationTypeLine ? relationTypeLine.split(/[:闂佹寧绋掗悺?).slice(1).join(':').trim() : '',
|
|
835
835
|
});
|
|
836
836
|
}
|
|
837
837
|
|
|
@@ -968,13 +968,13 @@ function buildRetryArguments(safeArgs, sourceFile, childTableName) {
|
|
|
968
968
|
function buildRelationCorrectionEntry(safeArgs, sourceFile, candidates, selectedChildTableName) {
|
|
969
969
|
return {
|
|
970
970
|
field: 'childTableName',
|
|
971
|
-
title: '
|
|
971
|
+
title: '婵炴垶鎹侀褔鎮哄▎鎴炲仒闁靛鍎辫ぐ鐘绘煠鏉堛劍鐓i柟渚垮妼椤垽鏁愰崨顓炴辈闂?,
|
|
972
972
|
tableName: safeArgs.tableName,
|
|
973
973
|
style: safeArgs.style,
|
|
974
974
|
designFile: sourceFile,
|
|
975
975
|
description: candidates.length
|
|
976
976
|
? 'If the current child relation is not the one you want, pass childTableName to choose a candidate from the design file.'
|
|
977
|
-
: 'No child relation was found. Check the
|
|
977
|
+
: 'No child relation was found. Check the 婵炴垶鎹侀濠勫垝閻樺灚鍋橀柕濞垮劚瑜扮娀鏌ゆ潏銊︻梿妞ゆ洍鏅犲?section in the design file.',
|
|
978
978
|
currentValue: selectedChildTableName || '',
|
|
979
979
|
options: candidates.map((item) => item.childTableName),
|
|
980
980
|
candidates,
|
|
@@ -1187,7 +1187,7 @@ function renderFormField(field) {
|
|
|
1187
1187
|
return [
|
|
1188
1188
|
' <el-col :span="12" class="mb20">',
|
|
1189
1189
|
` <el-form-item label="${label}" prop="${prop}">`,
|
|
1190
|
-
` <el-select v-model="form.${prop}" placeholder="
|
|
1190
|
+
` <el-select v-model="form.${prop}" placeholder="闁荤姴娲ㄩ崗姗€鍩€椤掆偓椤︽壆鈧?{label}" style="width: 100%">`,
|
|
1191
1191
|
` <el-option v-for="item in ${field.dictType}" :key="item.value" :label="item.label" :value="Number(item.value)" />`,
|
|
1192
1192
|
' </el-select>',
|
|
1193
1193
|
' </el-form-item>',
|
|
@@ -1196,12 +1196,12 @@ function renderFormField(field) {
|
|
|
1196
1196
|
}
|
|
1197
1197
|
|
|
1198
1198
|
if (field.formType === 'number') {
|
|
1199
|
-
const max = field.comment.includes('%') || field.comment.includes('
|
|
1199
|
+
const max = field.comment.includes('%') || field.comment.includes('濠殿噯绲鹃弻褏娆?) ? ' :max="100"' : '';
|
|
1200
1200
|
const precision = field.sqlType === 'DECIMAL' && field.scale ? ` :precision="${field.scale}" :step="0.01"` : '';
|
|
1201
1201
|
return [
|
|
1202
1202
|
' <el-col :span="12" class="mb20">',
|
|
1203
1203
|
` <el-form-item label="${label}" prop="${prop}">`,
|
|
1204
|
-
` <el-input-number v-model="form.${prop}" :min="0"${max}${precision} placeholder="
|
|
1204
|
+
` <el-input-number v-model="form.${prop}" :min="0"${max}${precision} placeholder="闁荤姴娲ㄩ弻澶屾椤撱垹绀?{label}" style="width: 100%" />`,
|
|
1205
1205
|
' </el-form-item>',
|
|
1206
1206
|
' </el-col>',
|
|
1207
1207
|
].join('\n');
|
|
@@ -1213,7 +1213,7 @@ function renderFormField(field) {
|
|
|
1213
1213
|
return [
|
|
1214
1214
|
' <el-col :span="12" class="mb20">',
|
|
1215
1215
|
` <el-form-item label="${label}" prop="${prop}">`,
|
|
1216
|
-
` <el-date-picker type="${pickerType}" placeholder="
|
|
1216
|
+
` <el-date-picker type="${pickerType}" placeholder="闁荤姴娲ㄩ崗姗€鍩€椤掆偓椤︽壆鈧?{label}" v-model="form.${prop}" :value-format="${formatName}" style="width: 100%"></el-date-picker>`,
|
|
1217
1217
|
' </el-form-item>',
|
|
1218
1218
|
' </el-col>',
|
|
1219
1219
|
].join('\n');
|
|
@@ -1223,7 +1223,7 @@ function renderFormField(field) {
|
|
|
1223
1223
|
return [
|
|
1224
1224
|
' <el-col :span="24" class="mb20">',
|
|
1225
1225
|
` <el-form-item label="${label}" prop="${prop}">`,
|
|
1226
|
-
` <el-input type="textarea" v-model="form.${prop}" placeholder="
|
|
1226
|
+
` <el-input type="textarea" v-model="form.${prop}" placeholder="闁荤姴娲ㄩ弻澶屾椤撱垹绀?{label}" />`,
|
|
1227
1227
|
' </el-form-item>',
|
|
1228
1228
|
' </el-col>',
|
|
1229
1229
|
].join('\n');
|
|
@@ -1232,7 +1232,7 @@ function renderFormField(field) {
|
|
|
1232
1232
|
return [
|
|
1233
1233
|
' <el-col :span="12" class="mb20">',
|
|
1234
1234
|
` <el-form-item label="${label}" prop="${prop}">`,
|
|
1235
|
-
` <el-input v-model="form.${prop}" placeholder="
|
|
1235
|
+
` <el-input v-model="form.${prop}" placeholder="闁荤姴娲ㄩ弻澶屾椤撱垹绀?{label}" />`,
|
|
1236
1236
|
' </el-form-item>',
|
|
1237
1237
|
' </el-col>',
|
|
1238
1238
|
].join('\n');
|
|
@@ -1259,12 +1259,12 @@ function renderChildTableColumn(field, childListName) {
|
|
|
1259
1259
|
let control = ` <el-input v-model="row.${field.attrName}" />`;
|
|
1260
1260
|
if (field.formType === 'select' && field.dictType) {
|
|
1261
1261
|
control = [
|
|
1262
|
-
` <el-select v-model="row.${field.attrName}" placeholder="
|
|
1262
|
+
` <el-select v-model="row.${field.attrName}" placeholder="闁荤姴娲ㄩ崗姗€鍩€椤掆偓椤︽壆鈧?{label}" style="width: 100%">`,
|
|
1263
1263
|
` <el-option v-for="item in ${field.dictType}" :key="item.value" :label="item.label" :value="Number(item.value)" />`,
|
|
1264
1264
|
' </el-select>',
|
|
1265
1265
|
].join('\n');
|
|
1266
1266
|
} else if (field.formType === 'number') {
|
|
1267
|
-
const max = field.comment.includes('%') || field.comment.includes('
|
|
1267
|
+
const max = field.comment.includes('%') || field.comment.includes('濠殿噯绲鹃弻褏娆?) ? ' :max="100"' : '';
|
|
1268
1268
|
const precision = field.sqlType === 'DECIMAL' && field.scale ? ` :precision="${field.scale}" :step="0.01"` : '';
|
|
1269
1269
|
control = ` <el-input-number v-model="row.${field.attrName}" :min="0"${max}${precision} style="width: 100%" />`;
|
|
1270
1270
|
}
|
|
@@ -1417,7 +1417,7 @@ function renderFormFieldV2(field) {
|
|
|
1417
1417
|
}
|
|
1418
1418
|
|
|
1419
1419
|
if (field.formType === 'number') {
|
|
1420
|
-
const max = field.comment.includes('%') || field.comment.includes('
|
|
1420
|
+
const max = field.comment.includes('%') || field.comment.includes('婵犳鍣徊楣冨蓟瑜忓▎?) ? ' :max="100"' : '';
|
|
1421
1421
|
const precision = field.sqlType === 'DECIMAL' && field.scale ? ` :precision="${field.scale}" :step="0.01"` : '';
|
|
1422
1422
|
return [
|
|
1423
1423
|
` <el-col v-if="${visibilityExpr}" :span="12" class="mb20">`,
|
|
@@ -1488,7 +1488,7 @@ function renderChildTableColumnV2(field, childListName) {
|
|
|
1488
1488
|
' </el-select>',
|
|
1489
1489
|
].join('\n');
|
|
1490
1490
|
} else if (field.formType === 'number') {
|
|
1491
|
-
const max = field.comment.includes('%') || field.comment.includes('
|
|
1491
|
+
const max = field.comment.includes('%') || field.comment.includes('婵犳鍣徊楣冨蓟瑜忓▎?) ? ' :max="100"' : '';
|
|
1492
1492
|
const precision = field.sqlType === 'DECIMAL' && field.scale ? ` :precision="${field.scale}" :step="0.01"` : '';
|
|
1493
1493
|
control = ` <el-input-number v-model="row.${field.attrName}" :min="0"${max}${precision} style="width: 100%" />`;
|
|
1494
1494
|
} else if (field.formType === 'datetime' || field.formType === 'date') {
|
|
@@ -1529,7 +1529,7 @@ function renderChildSectionV2(childModel, childCount) {
|
|
|
1529
1529
|
function renderValidationRule(field) {
|
|
1530
1530
|
if (!field.notNull) return null;
|
|
1531
1531
|
const label = stripDictAnnotation(field.comment).replace(/'/g, "\\'");
|
|
1532
|
-
return ` ${field.attrName}: [{ required: true, message: '${label}
|
|
1532
|
+
return ` ${field.attrName}: [{ required: true, message: '${label}婵炴垶鎸哥粔鐑藉礂濡崵鈻旈柧蹇撳帨閺?, trigger: 'blur' }],`;
|
|
1533
1533
|
}
|
|
1534
1534
|
|
|
1535
1535
|
function renderFormRules(visibleFields) {
|
package/package.json
CHANGED