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.
Files changed (2) hide show
  1. package/mcp_server.js +36 -36
  2. 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.10';
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 閻犱焦宕橀鍝ユ嫚鐎涙ɑ顫?md');
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 閻犱焦宕橀鍝ユ嫚鐎涙ɑ顫?md when omitted.' },
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(/(?:閻庢稒顨呴崥鈧瑋dict)[闁?\s]*([a-zA-Z0-9_]+)/i);
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*[\(锛圿\s*(?:瀛楀吀|dict)(?:[_\s-]*type)?\s*[:锛歖?\s*[a-zA-Z0-9_-]+\s*[\)锛塢\s*/gi, '')
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
- /(?:瀛楀吀|dict)(?:[_\s-]*type)?\s*[:锛歖\s*([a-zA-Z0-9_-]+)/i,
604
- /[\(锛圿\s*(?:瀛楀吀|dict)(?:[_\s-]*type)?\s*[:锛歖?\s*([a-zA-Z0-9_-]+)\s*[\)锛塢/i,
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 ['闁?, 'y', 'yes', '1', 'true', '闊洤鎳庨敐?].includes(normalized);
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) => /濞戞挸顭烽弫?i.test(field.comment));
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, [/閻庢稒顨嗛宀勫触?, /闁告帗顨呴幃?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]);
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, [/閻庢稒顨呴崥鈧?, /dict/i, /dict_?type/i]);
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*[\(闁挎稑婧?[^)\n闁挎稑銆?)[\)闁挎稑銆?);
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) => /(濞戞挻妲掗妴鍎勯柣鏍ㄥ劶閵?\s*[:闁挎稒鐡?.test(line));
818
- const childLine = lines.find((line) => /(濞寸姴姘﹂妴鍎勯悗娑欏姌閵?\s*[:闁挎稒鐡?.test(line));
819
- const fieldLine = lines.find((line) => /闁稿繐鐤囨禒鍫⑩偓娑欘殕椤斿s*[:闁挎稒鐡?.test(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) => /闁稿繐纾柈瀵哥尵鐠囪尙鈧﹥s*[:闁挎稒鐡?.test(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(/[:闁挎稒鐡?).slice(1).join(':').trim() : '',
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 濞戞捁顔婄划鐘垫偘閵娿儱褰犻柤杈ㄦ椤曗晠寮?section in the design file.',
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="閻犲洨鍏橀埀顒€顦扮€?{label}" style="width: 100%">`,
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('婵絾鏌х欢?) ? ' :max="100"' : '';
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="閻犲洨鏌夌欢顓㈠礂?{label}" style="width: 100%" />`,
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="閻犲洨鍏橀埀顒€顦扮€?{label}" v-model="form.${prop}" :value-format="${formatName}" style="width: 100%"></el-date-picker>`,
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="閻犲洨鏌夌欢顓㈠礂?{label}" />`,
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="閻犲洨鏌夌欢顓㈠礂?{label}" />`,
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="閻犲洨鍏橀埀顒€顦扮€?{label}" style="width: 100%">`,
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('婵絾鏌х欢?) ? ' :max="100"' : '';
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('濠殿噯绲鹃弻褏娆?) ? ' :max="100"' : '';
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('濠殿噯绲鹃弻褏娆?) ? ' :max="100"' : '';
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}濞戞挸绉烽崗妯荤▔閾忓厜鏁?, trigger: 'blur' }],`;
1532
+ return ` ${field.attrName}: [{ required: true, message: '${label}婵炴垶鎸哥粔鐑藉礂濡崵鈻旈柧蹇撳帨閺?, trigger: 'blur' }],`;
1533
1533
  }
1534
1534
 
1535
1535
  function renderFormRules(visibleFields) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "worsoft-frontend-codegen-local-mcp",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "Worsoft frontend local-template code generation MCP server.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "worsoft <sw@worsoft.vip>",