worsoft-frontend-codegen-local-mcp 0.1.52 → 0.1.54
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 +138 -60
- package/package.json +1 -1
package/mcp_server.js
CHANGED
|
@@ -5,7 +5,7 @@ const fs = require('fs');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
|
|
7
7
|
const SERVER_NAME = 'worsoft-codegen-local';
|
|
8
|
-
const SERVER_VERSION = '0.1.
|
|
8
|
+
const SERVER_VERSION = '0.1.54';
|
|
9
9
|
const PROTOCOL_VERSION = '2024-11-05';
|
|
10
10
|
const TOOL_NAME = 'worsoft_codegen_local_generate_frontend';
|
|
11
11
|
const STYLE_CATALOG_PATH = path.join(__dirname, 'assets', 'style-catalog.json');
|
|
@@ -462,9 +462,9 @@ function toConstantCase(value) {
|
|
|
462
462
|
.toUpperCase();
|
|
463
463
|
}
|
|
464
464
|
|
|
465
|
-
function parseDictRegistryEntries(fileContent) {
|
|
466
|
-
const blockMatch = String(fileContent || '').match(/export const DictRegistry\s*=\s*{([\s\S]*?)}\s*as const;/);
|
|
467
|
-
if (!blockMatch) return [];
|
|
465
|
+
function parseDictRegistryEntries(fileContent) {
|
|
466
|
+
const blockMatch = String(fileContent || '').match(/export const DictRegistry\s*=\s*{([\s\S]*?)}\s*as const;/);
|
|
467
|
+
if (!blockMatch) return [];
|
|
468
468
|
|
|
469
469
|
const entries = [];
|
|
470
470
|
const entryRegex = /^\s*([A-Z0-9_]+)\s*:\s*['"]([^'"]+)['"]\s*,?\s*$/gm;
|
|
@@ -473,23 +473,78 @@ function parseDictRegistryEntries(fileContent) {
|
|
|
473
473
|
entries.push({ key: match[1], value: match[2] });
|
|
474
474
|
match = entryRegex.exec(blockMatch[1]);
|
|
475
475
|
}
|
|
476
|
-
return entries;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
function renderDictRegistryContent(entries) {
|
|
480
|
-
const lines = entries.map((entry) => ` ${entry.key}: '${entry.value}',`);
|
|
481
|
-
return [
|
|
482
|
-
'export const DictRegistry = {',
|
|
483
|
-
...lines,
|
|
484
|
-
'} as const;',
|
|
485
|
-
'',
|
|
486
|
-
'export type DictRegistryKey = keyof typeof DictRegistry;',
|
|
487
|
-
'export type DictType = (typeof DictRegistry)[DictRegistryKey];',
|
|
488
|
-
'',
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
476
|
+
return entries;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function renderDictRegistryContent(entries) {
|
|
480
|
+
const lines = entries.map((entry) => ` ${entry.key}: '${entry.value}',`);
|
|
481
|
+
return [
|
|
482
|
+
'export const DictRegistry = {',
|
|
483
|
+
...lines,
|
|
484
|
+
'} as const;',
|
|
485
|
+
'',
|
|
486
|
+
'export type DictRegistryKey = keyof typeof DictRegistry;',
|
|
487
|
+
'export type DictType = (typeof DictRegistry)[DictRegistryKey];',
|
|
488
|
+
'',
|
|
489
|
+
'export const DictSemanticValues = {',
|
|
490
|
+
' billState: {',
|
|
491
|
+
" editing: '0',",
|
|
492
|
+
" processing: '1',",
|
|
493
|
+
" paused: '2',",
|
|
494
|
+
" terminated: '3',",
|
|
495
|
+
" completed: '4',",
|
|
496
|
+
' },',
|
|
497
|
+
'} as const;',
|
|
498
|
+
'',
|
|
499
|
+
'export const isBillStateEditing = (value: unknown): boolean =>',
|
|
500
|
+
" String(value ?? '') === DictSemanticValues.billState.editing;",
|
|
501
|
+
'',
|
|
502
|
+
].join('\n');
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function patchDictRegistryContent(fileContent, entriesToAdd) {
|
|
506
|
+
const source = String(fileContent || '');
|
|
507
|
+
const blockMatch = source.match(/export const DictRegistry\s*=\s*{([\s\S]*?)}\s*as const;/);
|
|
508
|
+
if (!blockMatch) return null;
|
|
509
|
+
|
|
510
|
+
const block = blockMatch[0];
|
|
511
|
+
const insertAt = block.lastIndexOf('}');
|
|
512
|
+
if (insertAt < 0) return null;
|
|
513
|
+
|
|
514
|
+
const linesToAdd = entriesToAdd.map((entry) => ` ${entry.key}: '${entry.value}',`).join('\n');
|
|
515
|
+
if (!linesToAdd) return source;
|
|
516
|
+
|
|
517
|
+
const prefix = block.slice(0, insertAt).replace(/\s*$/, '');
|
|
518
|
+
const suffix = block.slice(insertAt);
|
|
519
|
+
const nextBlock = `${prefix}\n${linesToAdd}\n${suffix}`;
|
|
520
|
+
|
|
521
|
+
return source.replace(block, nextBlock);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const DICT_REGISTRY_SEMANTIC_HELPERS = [
|
|
525
|
+
'',
|
|
526
|
+
'export const DictSemanticValues = {',
|
|
527
|
+
' billState: {',
|
|
528
|
+
" editing: '0',",
|
|
529
|
+
" processing: '1',",
|
|
530
|
+
" paused: '2',",
|
|
531
|
+
" terminated: '3',",
|
|
532
|
+
" completed: '4',",
|
|
533
|
+
' },',
|
|
534
|
+
'} as const;',
|
|
535
|
+
'',
|
|
536
|
+
'export const isBillStateEditing = (value: unknown): boolean =>',
|
|
537
|
+
" String(value ?? '') === DictSemanticValues.billState.editing;",
|
|
538
|
+
'',
|
|
539
|
+
].join('\n');
|
|
540
|
+
|
|
541
|
+
function ensureDictRegistrySemanticHelpers(fileContent) {
|
|
542
|
+
const source = String(fileContent || '').replace(/\s*$/, '\n');
|
|
543
|
+
if (source.includes('export const isBillStateEditing')) return source;
|
|
544
|
+
return `${source}${DICT_REGISTRY_SEMANTIC_HELPERS}`;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function isPlainObject(value) {
|
|
493
548
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
494
549
|
}
|
|
495
550
|
|
|
@@ -720,33 +775,43 @@ function prepareZhCnLocaleFile(model, mergeExisting) {
|
|
|
720
775
|
};
|
|
721
776
|
}
|
|
722
777
|
|
|
723
|
-
function prepareDictRegistry(frontendPath, dictTypes) {
|
|
724
|
-
const registryPath = path.join(frontendPath, 'src', 'enums', 'dict-registry.ts');
|
|
725
|
-
const exists = fs.existsSync(registryPath);
|
|
726
|
-
const
|
|
727
|
-
const
|
|
728
|
-
const
|
|
729
|
-
const
|
|
730
|
-
const
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
778
|
+
function prepareDictRegistry(frontendPath, dictTypes) {
|
|
779
|
+
const registryPath = path.join(frontendPath, 'src', 'enums', 'dict-registry.ts');
|
|
780
|
+
const exists = fs.existsSync(registryPath);
|
|
781
|
+
const currentContent = exists ? readUtf8File(registryPath) : '';
|
|
782
|
+
const existingEntries = exists ? parseDictRegistryEntries(currentContent) : [];
|
|
783
|
+
const entries = existingEntries.map((entry) => ({ ...entry }));
|
|
784
|
+
const keyByValue = new Map(entries.map((entry) => [entry.value, entry.key]));
|
|
785
|
+
const existingByKey = new Map(entries.map((entry) => [entry.key, entry.value]));
|
|
786
|
+
const usedKeys = new Set(entries.map((entry) => entry.key));
|
|
787
|
+
const entriesToAdd = [];
|
|
788
|
+
let changed = !exists;
|
|
789
|
+
|
|
790
|
+
for (const dictType of dictTypes) {
|
|
791
|
+
if (!dictType || keyByValue.has(dictType)) continue;
|
|
792
|
+
const key = buildUniqueDictRegistryKey(dictType, usedKeys, existingByKey);
|
|
793
|
+
const entry = { key, value: dictType };
|
|
794
|
+
entries.push(entry);
|
|
795
|
+
entriesToAdd.push(entry);
|
|
796
|
+
keyByValue.set(dictType, key);
|
|
797
|
+
existingByKey.set(key, dictType);
|
|
798
|
+
usedKeys.add(key);
|
|
799
|
+
changed = true;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
const patchedContent = exists ? patchDictRegistryContent(currentContent, entriesToAdd) : renderDictRegistryContent(entries);
|
|
803
|
+
const content = patchedContent ? ensureDictRegistrySemanticHelpers(patchedContent) : null;
|
|
804
|
+
const needsSemanticHelpers = exists && Boolean(content) && content !== currentContent;
|
|
805
|
+
|
|
806
|
+
return {
|
|
807
|
+
path: registryPath,
|
|
808
|
+
entries,
|
|
809
|
+
keyByValue,
|
|
810
|
+
content,
|
|
811
|
+
isCompatible: !exists || Boolean(content),
|
|
812
|
+
needsWrite: changed || needsSemanticHelpers,
|
|
813
|
+
};
|
|
814
|
+
}
|
|
750
815
|
|
|
751
816
|
function ensureCrudSchemaSupportFile(frontendPath) {
|
|
752
817
|
const schemaPath = path.join(frontendPath, 'src', 'utils', 'crudSchema.ts');
|
|
@@ -817,10 +882,16 @@ function maybeWriteSharedSupport(sharedSupport, writeToDisk) {
|
|
|
817
882
|
writeSupportFile(sharedSupport.crudSchema.path, sharedSupport.crudSchema.content);
|
|
818
883
|
}
|
|
819
884
|
|
|
820
|
-
if (sharedSupport.dictRegistry.needsWrite) {
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
885
|
+
if (sharedSupport.dictRegistry.needsWrite) {
|
|
886
|
+
if (!sharedSupport.dictRegistry.isCompatible) {
|
|
887
|
+
throw new Error(
|
|
888
|
+
'Detected an existing src/enums/dict-registry.ts that MCP could not merge safely. ' +
|
|
889
|
+
'Please align the DictRegistry export shape manually before enabling writeSupportFiles.'
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
writeSupportFile(sharedSupport.dictRegistry.path, sharedSupport.dictRegistry.content);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
824
895
|
|
|
825
896
|
function buildSupportNote(sharedSupport, localeZhSupport) {
|
|
826
897
|
const notes = [];
|
|
@@ -831,14 +902,21 @@ function buildSupportNote(sharedSupport, localeZhSupport) {
|
|
|
831
902
|
);
|
|
832
903
|
}
|
|
833
904
|
|
|
834
|
-
if (sharedSupport.crudSchema.exists && !sharedSupport.crudSchema.isCompatible) {
|
|
835
|
-
notes.push(
|
|
836
|
-
'Detected an existing src/utils/crudSchema.ts that does not match the expected helper signature. ' +
|
|
837
|
-
'MCP preserved the existing file and did not overwrite it. Generated pages now depend on that file being manually aligned.'
|
|
838
|
-
);
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
if (
|
|
905
|
+
if (sharedSupport.crudSchema.exists && !sharedSupport.crudSchema.isCompatible) {
|
|
906
|
+
notes.push(
|
|
907
|
+
'Detected an existing src/utils/crudSchema.ts that does not match the expected helper signature. ' +
|
|
908
|
+
'MCP preserved the existing file and did not overwrite it. Generated pages now depend on that file being manually aligned.'
|
|
909
|
+
);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
if (sharedSupport.dictRegistry.exists && !sharedSupport.dictRegistry.isCompatible) {
|
|
913
|
+
notes.push(
|
|
914
|
+
'Detected an existing src/enums/dict-registry.ts that MCP could not merge safely. ' +
|
|
915
|
+
'MCP preserved the existing file and did not overwrite it.'
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
if (localeZhSupport.exists && !localeZhSupport.isCompatible) {
|
|
842
920
|
notes.push(
|
|
843
921
|
`Detected an existing ${path.relative(localeZhSupport.frontendPath, localeZhSupport.path).replace(/\\/g, '/')} that MCP could not parse. ` +
|
|
844
922
|
'The file was preserved and not updated, so new Chinese i18n keys may need to be merged manually.'
|
package/package.json
CHANGED