@diagramers/cli 1.0.16 → 1.0.18
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/README.md +431 -32
- package/dist/commands/api.d.ts.map +1 -1
- package/dist/commands/api.js +34 -0
- package/dist/commands/api.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/services/relation-generator.d.ts +3 -0
- package/dist/services/relation-generator.d.ts.map +1 -0
- package/dist/services/relation-generator.js +212 -0
- package/dist/services/relation-generator.js.map +1 -0
- package/dist/services/table-generator.d.ts +2 -0
- package/dist/services/table-generator.d.ts.map +1 -0
- package/dist/services/table-generator.js +137 -0
- package/dist/services/table-generator.js.map +1 -0
- package/package.json +1 -1
- package/src/commands/api.ts +34 -0
- package/src/index.ts +2 -0
- package/src/services/relation-generator.ts +203 -0
- package/src/services/table-generator.ts +114 -0
@@ -0,0 +1,212 @@
|
|
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.generateRelation = generateRelation;
|
37
|
+
const fs = __importStar(require("fs"));
|
38
|
+
const path = __importStar(require("path"));
|
39
|
+
async function generateRelation(table1, table2, relationType = 'one-to-one') {
|
40
|
+
// Validate table names
|
41
|
+
if (!table1 || !table2 || !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(table1) || !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(table2)) {
|
42
|
+
throw new Error('Invalid table names. Use only letters, numbers, hyphens, and underscores. Must start with a letter.');
|
43
|
+
}
|
44
|
+
const table1Capitalized = table1.charAt(0).toUpperCase() + table1.slice(1);
|
45
|
+
const table2Capitalized = table2.charAt(0).toUpperCase() + table2.slice(1);
|
46
|
+
const currentDir = process.cwd();
|
47
|
+
// Check for required API project structure
|
48
|
+
const requiredDirs = ['src/entities', 'src/schemas'];
|
49
|
+
const missingDirs = requiredDirs.filter(dir => !fs.existsSync(path.join(currentDir, dir)));
|
50
|
+
if (missingDirs.length > 0) {
|
51
|
+
throw new Error(`This command should be run from a diagramers API project. Missing directories: ${missingDirs.join(', ')}`);
|
52
|
+
}
|
53
|
+
// Check if both tables exist
|
54
|
+
const table1EntityPath = path.join(currentDir, 'src/entities', `${table1}.ts`);
|
55
|
+
const table2EntityPath = path.join(currentDir, 'src/entities', `${table2}.ts`);
|
56
|
+
const table1SchemaPath = path.join(currentDir, 'src/schemas', `${table1}.ts`);
|
57
|
+
const table2SchemaPath = path.join(currentDir, 'src/schemas', `${table2}.ts`);
|
58
|
+
if (!fs.existsSync(table1EntityPath) || !fs.existsSync(table2EntityPath)) {
|
59
|
+
throw new Error(`Both tables must exist before creating relationships. Missing: ${!fs.existsSync(table1EntityPath) ? table1 : ''} ${!fs.existsSync(table2EntityPath) ? table2 : ''}`);
|
60
|
+
}
|
61
|
+
console.log(`🔗 Creating ${relationType} relationship between ${table1} and ${table2}...`);
|
62
|
+
// Update entities
|
63
|
+
console.log('📝 Updating entities...');
|
64
|
+
updateEntity(table1EntityPath, table1Capitalized, table2Capitalized, relationType, 'forward');
|
65
|
+
updateEntity(table2EntityPath, table2Capitalized, table1Capitalized, relationType, 'reverse');
|
66
|
+
// Update schemas
|
67
|
+
console.log('📋 Updating schemas...');
|
68
|
+
updateSchema(table1SchemaPath, table1Capitalized, table2Capitalized, relationType, 'forward');
|
69
|
+
updateSchema(table2SchemaPath, table2Capitalized, table1Capitalized, relationType, 'reverse');
|
70
|
+
console.log('✅ Relationship created successfully!');
|
71
|
+
console.log(`🔗 ${table1} ↔ ${table2} (${relationType})`);
|
72
|
+
}
|
73
|
+
function updateEntity(entityPath, tableCapitalized, relatedTableCapitalized, relationType, direction) {
|
74
|
+
let entityContent = fs.readFileSync(entityPath, 'utf8');
|
75
|
+
// Add import for the related interface
|
76
|
+
const importStatement = `import { I${relatedTableCapitalized} } from './${direction === 'forward' ? relatedTableCapitalized.toLowerCase() : relatedTableCapitalized.toLowerCase()}';`;
|
77
|
+
if (!entityContent.includes(importStatement)) {
|
78
|
+
// Find the last import statement and add after it
|
79
|
+
const importRegex = /import.*from.*['"];?\s*$/gm;
|
80
|
+
const matches = [...entityContent.matchAll(importRegex)];
|
81
|
+
if (matches.length > 0) {
|
82
|
+
const lastImport = matches[matches.length - 1];
|
83
|
+
const insertIndex = lastImport.index + lastImport[0].length;
|
84
|
+
entityContent = entityContent.slice(0, insertIndex) + '\n' + importStatement + entityContent.slice(insertIndex);
|
85
|
+
}
|
86
|
+
}
|
87
|
+
// Add relationship fields based on type
|
88
|
+
const interfaceRegex = /export interface I\w+ extends mongoose\.Document \{([^}]*)\}/s;
|
89
|
+
const match = entityContent.match(interfaceRegex);
|
90
|
+
if (match) {
|
91
|
+
const interfaceContent = match[1];
|
92
|
+
const newFields = generateEntityFields(tableCapitalized, relatedTableCapitalized, relationType, direction);
|
93
|
+
// Add new fields before the closing brace
|
94
|
+
const updatedInterfaceContent = interfaceContent.trim() + '\n ' + newFields;
|
95
|
+
entityContent = entityContent.replace(interfaceRegex, `export interface I${tableCapitalized} extends mongoose.Document {$1${updatedInterfaceContent}\n}`);
|
96
|
+
}
|
97
|
+
fs.writeFileSync(entityPath, entityContent);
|
98
|
+
}
|
99
|
+
function updateSchema(schemaPath, tableCapitalized, relatedTableCapitalized, relationType, direction) {
|
100
|
+
let schemaContent = fs.readFileSync(schemaPath, 'utf8');
|
101
|
+
// Add import for the related schema
|
102
|
+
const importStatement = `import { ${relatedTableCapitalized}Entity } from './${direction === 'forward' ? relatedTableCapitalized.toLowerCase() : relatedTableCapitalized.toLowerCase()}';`;
|
103
|
+
if (!schemaContent.includes(importStatement)) {
|
104
|
+
// Find the last import statement and add after it
|
105
|
+
const importRegex = /import.*from.*['"];?\s*$/gm;
|
106
|
+
const matches = [...schemaContent.matchAll(importRegex)];
|
107
|
+
if (matches.length > 0) {
|
108
|
+
const lastImport = matches[matches.length - 1];
|
109
|
+
const insertIndex = lastImport.index + lastImport[0].length;
|
110
|
+
schemaContent = schemaContent.slice(0, insertIndex) + '\n' + importStatement + schemaContent.slice(insertIndex);
|
111
|
+
}
|
112
|
+
}
|
113
|
+
// Add schema fields based on relationship type
|
114
|
+
const schemaRegex = /export const \w+Schema = new mongoose\.Schema\(([^)]*),/s;
|
115
|
+
const match = schemaContent.match(schemaRegex);
|
116
|
+
if (match) {
|
117
|
+
const schemaFields = match[1];
|
118
|
+
const newFields = generateSchemaFields(tableCapitalized, relatedTableCapitalized, relationType, direction);
|
119
|
+
// Add new fields before the closing brace
|
120
|
+
const updatedSchemaFields = schemaFields.trim() + '\n ' + newFields;
|
121
|
+
schemaContent = schemaContent.replace(schemaRegex, `export const ${tableCapitalized.toLowerCase()}Schema = new mongoose.Schema(${updatedSchemaFields},`);
|
122
|
+
}
|
123
|
+
// Add virtual fields and population methods
|
124
|
+
const modelRegex = /export const \w+Entity = mongoose\.model<.*>\(.*\);?\s*$/;
|
125
|
+
const virtualFields = generateVirtualFields(tableCapitalized, relatedTableCapitalized, relationType, direction);
|
126
|
+
if (virtualFields) {
|
127
|
+
schemaContent = schemaContent.replace(modelRegex, `${virtualFields}\n\n$&`);
|
128
|
+
}
|
129
|
+
fs.writeFileSync(schemaPath, schemaContent);
|
130
|
+
}
|
131
|
+
function generateEntityFields(tableCapitalized, relatedTableCapitalized, relationType, direction) {
|
132
|
+
switch (relationType) {
|
133
|
+
case 'one-to-one':
|
134
|
+
if (direction === 'forward') {
|
135
|
+
return `${relatedTableCapitalized.toLowerCase()}Id: ObjectId,\n ${relatedTableCapitalized.toLowerCase()}?: I${relatedTableCapitalized}`;
|
136
|
+
}
|
137
|
+
else {
|
138
|
+
return `${tableCapitalized.toLowerCase()}Id: ObjectId,\n ${tableCapitalized.toLowerCase()}?: I${tableCapitalized}`;
|
139
|
+
}
|
140
|
+
case 'one-to-many':
|
141
|
+
if (direction === 'forward') {
|
142
|
+
return `${relatedTableCapitalized.toLowerCase()}Id: ObjectId,\n ${relatedTableCapitalized.toLowerCase()}?: I${relatedTableCapitalized}`;
|
143
|
+
}
|
144
|
+
else {
|
145
|
+
return `${tableCapitalized.toLowerCase()}s?: I${tableCapitalized}[]`;
|
146
|
+
}
|
147
|
+
case 'many-to-many':
|
148
|
+
if (direction === 'forward') {
|
149
|
+
return `${relatedTableCapitalized.toLowerCase()}Ids: ObjectId[],\n ${relatedTableCapitalized.toLowerCase()}s?: I${relatedTableCapitalized}[]`;
|
150
|
+
}
|
151
|
+
else {
|
152
|
+
return `${tableCapitalized.toLowerCase()}Ids: ObjectId[],\n ${tableCapitalized.toLowerCase()}s?: I${tableCapitalized}[]`;
|
153
|
+
}
|
154
|
+
default:
|
155
|
+
return '';
|
156
|
+
}
|
157
|
+
}
|
158
|
+
function generateSchemaFields(tableCapitalized, relatedTableCapitalized, relationType, direction) {
|
159
|
+
switch (relationType) {
|
160
|
+
case 'one-to-one':
|
161
|
+
if (direction === 'forward') {
|
162
|
+
return `${relatedTableCapitalized.toLowerCase()}Id: {\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${relatedTableCapitalized.toLowerCase()}',\n required: false\n }`;
|
163
|
+
}
|
164
|
+
else {
|
165
|
+
return `${tableCapitalized.toLowerCase()}Id: {\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${tableCapitalized.toLowerCase()}',\n required: false\n }`;
|
166
|
+
}
|
167
|
+
case 'one-to-many':
|
168
|
+
if (direction === 'forward') {
|
169
|
+
return `${relatedTableCapitalized.toLowerCase()}Id: {\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${relatedTableCapitalized.toLowerCase()}',\n required: false\n }`;
|
170
|
+
}
|
171
|
+
else {
|
172
|
+
return ''; // No field needed for reverse one-to-many
|
173
|
+
}
|
174
|
+
case 'many-to-many':
|
175
|
+
if (direction === 'forward') {
|
176
|
+
return `${relatedTableCapitalized.toLowerCase()}Ids: [{\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${relatedTableCapitalized.toLowerCase()}'\n }]`;
|
177
|
+
}
|
178
|
+
else {
|
179
|
+
return `${tableCapitalized.toLowerCase()}Ids: [{\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${tableCapitalized.toLowerCase()}'\n }]`;
|
180
|
+
}
|
181
|
+
default:
|
182
|
+
return '';
|
183
|
+
}
|
184
|
+
}
|
185
|
+
function generateVirtualFields(tableCapitalized, relatedTableCapitalized, relationType, direction) {
|
186
|
+
switch (relationType) {
|
187
|
+
case 'one-to-one':
|
188
|
+
if (direction === 'forward') {
|
189
|
+
return `// Virtual populate for ${relatedTableCapitalized.toLowerCase()}\n${tableCapitalized.toLowerCase()}Schema.virtual('${relatedTableCapitalized.toLowerCase()}', {\n ref: '${relatedTableCapitalized.toLowerCase()}',\n localField: '${relatedTableCapitalized.toLowerCase()}Id',\n foreignField: '_id',\n justOne: true\n});`;
|
190
|
+
}
|
191
|
+
else {
|
192
|
+
return `// Virtual populate for ${tableCapitalized.toLowerCase()}\n${tableCapitalized.toLowerCase()}Schema.virtual('${tableCapitalized.toLowerCase()}', {\n ref: '${tableCapitalized.toLowerCase()}',\n localField: '${tableCapitalized.toLowerCase()}Id',\n foreignField: '_id',\n justOne: true\n});`;
|
193
|
+
}
|
194
|
+
case 'one-to-many':
|
195
|
+
if (direction === 'forward') {
|
196
|
+
return `// Virtual populate for ${relatedTableCapitalized.toLowerCase()}\n${tableCapitalized.toLowerCase()}Schema.virtual('${relatedTableCapitalized.toLowerCase()}', {\n ref: '${relatedTableCapitalized.toLowerCase()}',\n localField: '${relatedTableCapitalized.toLowerCase()}Id',\n foreignField: '_id',\n justOne: true\n});`;
|
197
|
+
}
|
198
|
+
else {
|
199
|
+
return `// Virtual populate for ${tableCapitalized.toLowerCase()}s\n${tableCapitalized.toLowerCase()}Schema.virtual('${tableCapitalized.toLowerCase()}s', {\n ref: '${tableCapitalized.toLowerCase()}',\n localField: '_id',\n foreignField: '${tableCapitalized.toLowerCase()}Id'\n});`;
|
200
|
+
}
|
201
|
+
case 'many-to-many':
|
202
|
+
if (direction === 'forward') {
|
203
|
+
return `// Virtual populate for ${relatedTableCapitalized.toLowerCase()}s\n${tableCapitalized.toLowerCase()}Schema.virtual('${relatedTableCapitalized.toLowerCase()}s', {\n ref: '${relatedTableCapitalized.toLowerCase()}',\n localField: '${relatedTableCapitalized.toLowerCase()}Ids',\n foreignField: '_id'\n});`;
|
204
|
+
}
|
205
|
+
else {
|
206
|
+
return `// Virtual populate for ${tableCapitalized.toLowerCase()}s\n${tableCapitalized.toLowerCase()}Schema.virtual('${tableCapitalized.toLowerCase()}s', {\n ref: '${tableCapitalized.toLowerCase()}',\n localField: '${tableCapitalized.toLowerCase()}Ids',\n foreignField: '_id'\n});`;
|
207
|
+
}
|
208
|
+
default:
|
209
|
+
return '';
|
210
|
+
}
|
211
|
+
}
|
212
|
+
//# sourceMappingURL=relation-generator.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"relation-generator.js","sourceRoot":"","sources":["../../src/services/relation-generator.ts"],"names":[],"mappings":"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"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"table-generator.d.ts","sourceRoot":"","sources":["../../src/services/table-generator.ts"],"names":[],"mappings":"AAGA,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkCpE"}
|
@@ -0,0 +1,137 @@
|
|
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.generateTable = generateTable;
|
37
|
+
const fs = __importStar(require("fs"));
|
38
|
+
const path = __importStar(require("path"));
|
39
|
+
async function generateTable(tableName) {
|
40
|
+
// Validate table name
|
41
|
+
if (!tableName || !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(tableName)) {
|
42
|
+
throw new Error('Invalid table name. Use only letters, numbers, hyphens, and underscores. Must start with a letter.');
|
43
|
+
}
|
44
|
+
const tableNameCapitalized = tableName.charAt(0).toUpperCase() + tableName.slice(1);
|
45
|
+
const currentDir = process.cwd();
|
46
|
+
// Check for required API project structure
|
47
|
+
const requiredDirs = [
|
48
|
+
'src/entities',
|
49
|
+
'src/schemas'
|
50
|
+
];
|
51
|
+
const missingDirs = requiredDirs.filter(dir => !fs.existsSync(path.join(currentDir, dir)));
|
52
|
+
if (missingDirs.length > 0) {
|
53
|
+
throw new Error(`This command should be run from a diagramers API project. Missing directories: ${missingDirs.join(', ')}`);
|
54
|
+
}
|
55
|
+
console.log('📝 Creating entity...');
|
56
|
+
const entityContent = generateEntityContent(tableName, tableNameCapitalized);
|
57
|
+
fs.writeFileSync(path.join(currentDir, 'src/entities', `${tableName}.ts`), entityContent);
|
58
|
+
console.log('📋 Creating schema...');
|
59
|
+
const schemaContent = generateSchemaContent(tableName, tableNameCapitalized);
|
60
|
+
fs.writeFileSync(path.join(currentDir, 'src/schemas', `${tableName}.ts`), schemaContent);
|
61
|
+
// Update dbcontext to include the new entity
|
62
|
+
updateDbContext(currentDir, tableName, tableNameCapitalized);
|
63
|
+
console.log('✅ Table generation completed!');
|
64
|
+
console.log(`📊 Entity: src/entities/${tableName}.ts`);
|
65
|
+
console.log(`📋 Schema: src/schemas/${tableName}.ts`);
|
66
|
+
console.log('🔄 dbcontext.ts updated');
|
67
|
+
}
|
68
|
+
function generateEntityContent(tableName, tableNameCapitalized) {
|
69
|
+
return `import * as mongoose from 'mongoose';
|
70
|
+
import { ObjectId } from "bson";
|
71
|
+
|
72
|
+
export interface I${tableNameCapitalized} extends mongoose.Document {
|
73
|
+
_id: ObjectId,
|
74
|
+
name: string,
|
75
|
+
description?: string,
|
76
|
+
status: number,
|
77
|
+
createdAt: Date,
|
78
|
+
updatedAt: Date
|
79
|
+
}`;
|
80
|
+
}
|
81
|
+
function generateSchemaContent(tableName, tableNameCapitalized) {
|
82
|
+
return `import * as mongoose from 'mongoose';
|
83
|
+
import { I${tableNameCapitalized} } from '../entities/${tableName}';
|
84
|
+
|
85
|
+
export const ${tableName}Schema = new mongoose.Schema(
|
86
|
+
{
|
87
|
+
name: {
|
88
|
+
type: mongoose.SchemaTypes.String,
|
89
|
+
required: true,
|
90
|
+
},
|
91
|
+
description: {
|
92
|
+
type: mongoose.SchemaTypes.String,
|
93
|
+
required: false,
|
94
|
+
},
|
95
|
+
status: {
|
96
|
+
type: mongoose.SchemaTypes.Number,
|
97
|
+
default: 1,
|
98
|
+
}
|
99
|
+
},
|
100
|
+
{ timestamps: true, suppressReservedKeysWarning: true },
|
101
|
+
);
|
102
|
+
|
103
|
+
export const ${tableNameCapitalized}Entity = mongoose.model<I${tableNameCapitalized}>('${tableName}', ${tableName}Schema);`;
|
104
|
+
}
|
105
|
+
function updateDbContext(currentDir, tableName, tableNameCapitalized) {
|
106
|
+
const dbcontextPath = path.join(currentDir, 'src/helpers/dbcontext.ts');
|
107
|
+
if (!fs.existsSync(dbcontextPath)) {
|
108
|
+
console.log('⚠️ dbcontext.ts not found, skipping dbcontext update');
|
109
|
+
return;
|
110
|
+
}
|
111
|
+
let dbcontextContent = fs.readFileSync(dbcontextPath, 'utf8');
|
112
|
+
// Add import
|
113
|
+
const importStatement = `import { ${tableNameCapitalized}Entity } from '../schemas/${tableName}';`;
|
114
|
+
if (!dbcontextContent.includes(importStatement)) {
|
115
|
+
// Find the last import statement and add after it
|
116
|
+
const importRegex = /import.*from.*['"];?\s*$/gm;
|
117
|
+
const matches = [...dbcontextContent.matchAll(importRegex)];
|
118
|
+
if (matches.length > 0) {
|
119
|
+
const lastImport = matches[matches.length - 1];
|
120
|
+
const insertIndex = lastImport.index + lastImport[0].length;
|
121
|
+
dbcontextContent = dbcontextContent.slice(0, insertIndex) + '\n' + importStatement + dbcontextContent.slice(insertIndex);
|
122
|
+
}
|
123
|
+
}
|
124
|
+
// Add to dbcontext object
|
125
|
+
const dbcontextObjectRegex = /export\s+default\s*\{([^}]*)\}/s;
|
126
|
+
const match = dbcontextContent.match(dbcontextObjectRegex);
|
127
|
+
if (match) {
|
128
|
+
const objectContent = match[1];
|
129
|
+
if (!objectContent.includes(`${tableNameCapitalized}Entity`)) {
|
130
|
+
const newObjectContent = objectContent.trim() + `,\n ${tableNameCapitalized}Entity`;
|
131
|
+
dbcontextContent = dbcontextContent.replace(dbcontextObjectRegex, `export default {$1${newObjectContent}\n}`);
|
132
|
+
}
|
133
|
+
}
|
134
|
+
fs.writeFileSync(dbcontextPath, dbcontextContent);
|
135
|
+
console.log('📊 Updated dbcontext.ts');
|
136
|
+
}
|
137
|
+
//# sourceMappingURL=table-generator.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"table-generator.js","sourceRoot":"","sources":["../../src/services/table-generator.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/commands/api.ts
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
import { Command } from 'commander';
|
2
2
|
import { generateModule } from '../services/api-generator';
|
3
|
+
import { generateTable } from '../services/table-generator';
|
4
|
+
import { generateRelation } from '../services/relation-generator';
|
3
5
|
import { processTemplate } from '../services/template-processor';
|
4
6
|
import chalk from 'chalk';
|
5
7
|
|
@@ -38,5 +40,37 @@ export function apiCommand(program: Command) {
|
|
38
40
|
}
|
39
41
|
});
|
40
42
|
|
43
|
+
// Generate table command
|
44
|
+
api
|
45
|
+
.command('generate:table <name>')
|
46
|
+
.description('Generate a new database table with entity and schema only')
|
47
|
+
.action(async (name: string) => {
|
48
|
+
try {
|
49
|
+
console.log(chalk.blue(`🚀 Generating table: ${name}`));
|
50
|
+
await generateTable(name);
|
51
|
+
console.log(chalk.green(`✅ Table '${name}' generated successfully!`));
|
52
|
+
} catch (error: any) {
|
53
|
+
console.error(chalk.red(`❌ Error generating table: ${error.message}`));
|
54
|
+
process.exit(1);
|
55
|
+
}
|
56
|
+
});
|
57
|
+
|
58
|
+
// Generate relation command
|
59
|
+
api
|
60
|
+
.command('generate:relation <table1> <table2> [type]')
|
61
|
+
.description('Generate a relationship between two existing tables')
|
62
|
+
.option('-t, --type <type>', 'Relationship type: one-to-one, one-to-many, many-to-many', 'one-to-one')
|
63
|
+
.action(async (table1: string, table2: string, options: any) => {
|
64
|
+
try {
|
65
|
+
const relationType = options.type || 'one-to-one';
|
66
|
+
console.log(chalk.blue(`🔗 Creating ${relationType} relationship between ${table1} and ${table2}...`));
|
67
|
+
await generateRelation(table1, table2, relationType);
|
68
|
+
console.log(chalk.green(`✅ Relationship created successfully!`));
|
69
|
+
} catch (error: any) {
|
70
|
+
console.error(chalk.red(`❌ Error creating relationship: ${error.message}`));
|
71
|
+
process.exit(1);
|
72
|
+
}
|
73
|
+
});
|
74
|
+
|
41
75
|
return api;
|
42
76
|
}
|
package/src/index.ts
CHANGED
@@ -28,6 +28,8 @@ Examples:
|
|
28
28
|
$ diagramers update
|
29
29
|
$ diagramers extend --feature auth
|
30
30
|
$ diagramers api generate:module product
|
31
|
+
$ diagramers api generate:table category
|
32
|
+
$ diagramers api generate:relation product category one-to-many
|
31
33
|
$ diagramers api process:template my-api-project
|
32
34
|
`);
|
33
35
|
|
@@ -0,0 +1,203 @@
|
|
1
|
+
import * as fs from 'fs';
|
2
|
+
import * as path from 'path';
|
3
|
+
|
4
|
+
export type RelationType = 'one-to-one' | 'one-to-many' | 'many-to-many';
|
5
|
+
|
6
|
+
export async function generateRelation(table1: string, table2: string, relationType: RelationType = 'one-to-one'): Promise<void> {
|
7
|
+
// Validate table names
|
8
|
+
if (!table1 || !table2 || !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(table1) || !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(table2)) {
|
9
|
+
throw new Error('Invalid table names. Use only letters, numbers, hyphens, and underscores. Must start with a letter.');
|
10
|
+
}
|
11
|
+
|
12
|
+
const table1Capitalized = table1.charAt(0).toUpperCase() + table1.slice(1);
|
13
|
+
const table2Capitalized = table2.charAt(0).toUpperCase() + table2.slice(1);
|
14
|
+
const currentDir = process.cwd();
|
15
|
+
|
16
|
+
// Check for required API project structure
|
17
|
+
const requiredDirs = ['src/entities', 'src/schemas'];
|
18
|
+
const missingDirs = requiredDirs.filter(dir => !fs.existsSync(path.join(currentDir, dir)));
|
19
|
+
if (missingDirs.length > 0) {
|
20
|
+
throw new Error(`This command should be run from a diagramers API project. Missing directories: ${missingDirs.join(', ')}`);
|
21
|
+
}
|
22
|
+
|
23
|
+
// Check if both tables exist
|
24
|
+
const table1EntityPath = path.join(currentDir, 'src/entities', `${table1}.ts`);
|
25
|
+
const table2EntityPath = path.join(currentDir, 'src/entities', `${table2}.ts`);
|
26
|
+
const table1SchemaPath = path.join(currentDir, 'src/schemas', `${table1}.ts`);
|
27
|
+
const table2SchemaPath = path.join(currentDir, 'src/schemas', `${table2}.ts`);
|
28
|
+
|
29
|
+
if (!fs.existsSync(table1EntityPath) || !fs.existsSync(table2EntityPath)) {
|
30
|
+
throw new Error(`Both tables must exist before creating relationships. Missing: ${!fs.existsSync(table1EntityPath) ? table1 : ''} ${!fs.existsSync(table2EntityPath) ? table2 : ''}`);
|
31
|
+
}
|
32
|
+
|
33
|
+
console.log(`🔗 Creating ${relationType} relationship between ${table1} and ${table2}...`);
|
34
|
+
|
35
|
+
// Update entities
|
36
|
+
console.log('📝 Updating entities...');
|
37
|
+
updateEntity(table1EntityPath, table1Capitalized, table2Capitalized, relationType, 'forward');
|
38
|
+
updateEntity(table2EntityPath, table2Capitalized, table1Capitalized, relationType, 'reverse');
|
39
|
+
|
40
|
+
// Update schemas
|
41
|
+
console.log('📋 Updating schemas...');
|
42
|
+
updateSchema(table1SchemaPath, table1Capitalized, table2Capitalized, relationType, 'forward');
|
43
|
+
updateSchema(table2SchemaPath, table2Capitalized, table1Capitalized, relationType, 'reverse');
|
44
|
+
|
45
|
+
console.log('✅ Relationship created successfully!');
|
46
|
+
console.log(`🔗 ${table1} ↔ ${table2} (${relationType})`);
|
47
|
+
}
|
48
|
+
|
49
|
+
function updateEntity(entityPath: string, tableCapitalized: string, relatedTableCapitalized: string, relationType: RelationType, direction: 'forward' | 'reverse'): void {
|
50
|
+
let entityContent = fs.readFileSync(entityPath, 'utf8');
|
51
|
+
|
52
|
+
// Add import for the related interface
|
53
|
+
const importStatement = `import { I${relatedTableCapitalized} } from './${direction === 'forward' ? relatedTableCapitalized.toLowerCase() : relatedTableCapitalized.toLowerCase()}';`;
|
54
|
+
if (!entityContent.includes(importStatement)) {
|
55
|
+
// Find the last import statement and add after it
|
56
|
+
const importRegex = /import.*from.*['"];?\s*$/gm;
|
57
|
+
const matches = [...entityContent.matchAll(importRegex)];
|
58
|
+
if (matches.length > 0) {
|
59
|
+
const lastImport = matches[matches.length - 1];
|
60
|
+
const insertIndex = lastImport.index! + lastImport[0].length;
|
61
|
+
entityContent = entityContent.slice(0, insertIndex) + '\n' + importStatement + entityContent.slice(insertIndex);
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
// Add relationship fields based on type
|
66
|
+
const interfaceRegex = /export interface I\w+ extends mongoose\.Document \{([^}]*)\}/s;
|
67
|
+
const match = entityContent.match(interfaceRegex);
|
68
|
+
|
69
|
+
if (match) {
|
70
|
+
const interfaceContent = match[1];
|
71
|
+
const newFields = generateEntityFields(tableCapitalized, relatedTableCapitalized, relationType, direction);
|
72
|
+
|
73
|
+
// Add new fields before the closing brace
|
74
|
+
const updatedInterfaceContent = interfaceContent.trim() + '\n ' + newFields;
|
75
|
+
entityContent = entityContent.replace(interfaceRegex, `export interface I${tableCapitalized} extends mongoose.Document {$1${updatedInterfaceContent}\n}`);
|
76
|
+
}
|
77
|
+
|
78
|
+
fs.writeFileSync(entityPath, entityContent);
|
79
|
+
}
|
80
|
+
|
81
|
+
function updateSchema(schemaPath: string, tableCapitalized: string, relatedTableCapitalized: string, relationType: RelationType, direction: 'forward' | 'reverse'): void {
|
82
|
+
let schemaContent = fs.readFileSync(schemaPath, 'utf8');
|
83
|
+
|
84
|
+
// Add import for the related schema
|
85
|
+
const importStatement = `import { ${relatedTableCapitalized}Entity } from './${direction === 'forward' ? relatedTableCapitalized.toLowerCase() : relatedTableCapitalized.toLowerCase()}';`;
|
86
|
+
if (!schemaContent.includes(importStatement)) {
|
87
|
+
// Find the last import statement and add after it
|
88
|
+
const importRegex = /import.*from.*['"];?\s*$/gm;
|
89
|
+
const matches = [...schemaContent.matchAll(importRegex)];
|
90
|
+
if (matches.length > 0) {
|
91
|
+
const lastImport = matches[matches.length - 1];
|
92
|
+
const insertIndex = lastImport.index! + lastImport[0].length;
|
93
|
+
schemaContent = schemaContent.slice(0, insertIndex) + '\n' + importStatement + schemaContent.slice(insertIndex);
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
// Add schema fields based on relationship type
|
98
|
+
const schemaRegex = /export const \w+Schema = new mongoose\.Schema\(([^)]*),/s;
|
99
|
+
const match = schemaContent.match(schemaRegex);
|
100
|
+
|
101
|
+
if (match) {
|
102
|
+
const schemaFields = match[1];
|
103
|
+
const newFields = generateSchemaFields(tableCapitalized, relatedTableCapitalized, relationType, direction);
|
104
|
+
|
105
|
+
// Add new fields before the closing brace
|
106
|
+
const updatedSchemaFields = schemaFields.trim() + '\n ' + newFields;
|
107
|
+
schemaContent = schemaContent.replace(schemaRegex, `export const ${tableCapitalized.toLowerCase()}Schema = new mongoose.Schema(${updatedSchemaFields},`);
|
108
|
+
}
|
109
|
+
|
110
|
+
// Add virtual fields and population methods
|
111
|
+
const modelRegex = /export const \w+Entity = mongoose\.model<.*>\(.*\);?\s*$/;
|
112
|
+
const virtualFields = generateVirtualFields(tableCapitalized, relatedTableCapitalized, relationType, direction);
|
113
|
+
|
114
|
+
if (virtualFields) {
|
115
|
+
schemaContent = schemaContent.replace(modelRegex, `${virtualFields}\n\n$&`);
|
116
|
+
}
|
117
|
+
|
118
|
+
fs.writeFileSync(schemaPath, schemaContent);
|
119
|
+
}
|
120
|
+
|
121
|
+
function generateEntityFields(tableCapitalized: string, relatedTableCapitalized: string, relationType: RelationType, direction: 'forward' | 'reverse'): string {
|
122
|
+
switch (relationType) {
|
123
|
+
case 'one-to-one':
|
124
|
+
if (direction === 'forward') {
|
125
|
+
return `${relatedTableCapitalized.toLowerCase()}Id: ObjectId,\n ${relatedTableCapitalized.toLowerCase()}?: I${relatedTableCapitalized}`;
|
126
|
+
} else {
|
127
|
+
return `${tableCapitalized.toLowerCase()}Id: ObjectId,\n ${tableCapitalized.toLowerCase()}?: I${tableCapitalized}`;
|
128
|
+
}
|
129
|
+
|
130
|
+
case 'one-to-many':
|
131
|
+
if (direction === 'forward') {
|
132
|
+
return `${relatedTableCapitalized.toLowerCase()}Id: ObjectId,\n ${relatedTableCapitalized.toLowerCase()}?: I${relatedTableCapitalized}`;
|
133
|
+
} else {
|
134
|
+
return `${tableCapitalized.toLowerCase()}s?: I${tableCapitalized}[]`;
|
135
|
+
}
|
136
|
+
|
137
|
+
case 'many-to-many':
|
138
|
+
if (direction === 'forward') {
|
139
|
+
return `${relatedTableCapitalized.toLowerCase()}Ids: ObjectId[],\n ${relatedTableCapitalized.toLowerCase()}s?: I${relatedTableCapitalized}[]`;
|
140
|
+
} else {
|
141
|
+
return `${tableCapitalized.toLowerCase()}Ids: ObjectId[],\n ${tableCapitalized.toLowerCase()}s?: I${tableCapitalized}[]`;
|
142
|
+
}
|
143
|
+
|
144
|
+
default:
|
145
|
+
return '';
|
146
|
+
}
|
147
|
+
}
|
148
|
+
|
149
|
+
function generateSchemaFields(tableCapitalized: string, relatedTableCapitalized: string, relationType: RelationType, direction: 'forward' | 'reverse'): string {
|
150
|
+
switch (relationType) {
|
151
|
+
case 'one-to-one':
|
152
|
+
if (direction === 'forward') {
|
153
|
+
return `${relatedTableCapitalized.toLowerCase()}Id: {\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${relatedTableCapitalized.toLowerCase()}',\n required: false\n }`;
|
154
|
+
} else {
|
155
|
+
return `${tableCapitalized.toLowerCase()}Id: {\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${tableCapitalized.toLowerCase()}',\n required: false\n }`;
|
156
|
+
}
|
157
|
+
|
158
|
+
case 'one-to-many':
|
159
|
+
if (direction === 'forward') {
|
160
|
+
return `${relatedTableCapitalized.toLowerCase()}Id: {\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${relatedTableCapitalized.toLowerCase()}',\n required: false\n }`;
|
161
|
+
} else {
|
162
|
+
return ''; // No field needed for reverse one-to-many
|
163
|
+
}
|
164
|
+
|
165
|
+
case 'many-to-many':
|
166
|
+
if (direction === 'forward') {
|
167
|
+
return `${relatedTableCapitalized.toLowerCase()}Ids: [{\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${relatedTableCapitalized.toLowerCase()}'\n }]`;
|
168
|
+
} else {
|
169
|
+
return `${tableCapitalized.toLowerCase()}Ids: [{\n type: mongoose.SchemaTypes.ObjectId,\n ref: '${tableCapitalized.toLowerCase()}'\n }]`;
|
170
|
+
}
|
171
|
+
|
172
|
+
default:
|
173
|
+
return '';
|
174
|
+
}
|
175
|
+
}
|
176
|
+
|
177
|
+
function generateVirtualFields(tableCapitalized: string, relatedTableCapitalized: string, relationType: RelationType, direction: 'forward' | 'reverse'): string {
|
178
|
+
switch (relationType) {
|
179
|
+
case 'one-to-one':
|
180
|
+
if (direction === 'forward') {
|
181
|
+
return `// Virtual populate for ${relatedTableCapitalized.toLowerCase()}\n${tableCapitalized.toLowerCase()}Schema.virtual('${relatedTableCapitalized.toLowerCase()}', {\n ref: '${relatedTableCapitalized.toLowerCase()}',\n localField: '${relatedTableCapitalized.toLowerCase()}Id',\n foreignField: '_id',\n justOne: true\n});`;
|
182
|
+
} else {
|
183
|
+
return `// Virtual populate for ${tableCapitalized.toLowerCase()}\n${tableCapitalized.toLowerCase()}Schema.virtual('${tableCapitalized.toLowerCase()}', {\n ref: '${tableCapitalized.toLowerCase()}',\n localField: '${tableCapitalized.toLowerCase()}Id',\n foreignField: '_id',\n justOne: true\n});`;
|
184
|
+
}
|
185
|
+
|
186
|
+
case 'one-to-many':
|
187
|
+
if (direction === 'forward') {
|
188
|
+
return `// Virtual populate for ${relatedTableCapitalized.toLowerCase()}\n${tableCapitalized.toLowerCase()}Schema.virtual('${relatedTableCapitalized.toLowerCase()}', {\n ref: '${relatedTableCapitalized.toLowerCase()}',\n localField: '${relatedTableCapitalized.toLowerCase()}Id',\n foreignField: '_id',\n justOne: true\n});`;
|
189
|
+
} else {
|
190
|
+
return `// Virtual populate for ${tableCapitalized.toLowerCase()}s\n${tableCapitalized.toLowerCase()}Schema.virtual('${tableCapitalized.toLowerCase()}s', {\n ref: '${tableCapitalized.toLowerCase()}',\n localField: '_id',\n foreignField: '${tableCapitalized.toLowerCase()}Id'\n});`;
|
191
|
+
}
|
192
|
+
|
193
|
+
case 'many-to-many':
|
194
|
+
if (direction === 'forward') {
|
195
|
+
return `// Virtual populate for ${relatedTableCapitalized.toLowerCase()}s\n${tableCapitalized.toLowerCase()}Schema.virtual('${relatedTableCapitalized.toLowerCase()}s', {\n ref: '${relatedTableCapitalized.toLowerCase()}',\n localField: '${relatedTableCapitalized.toLowerCase()}Ids',\n foreignField: '_id'\n});`;
|
196
|
+
} else {
|
197
|
+
return `// Virtual populate for ${tableCapitalized.toLowerCase()}s\n${tableCapitalized.toLowerCase()}Schema.virtual('${tableCapitalized.toLowerCase()}s', {\n ref: '${tableCapitalized.toLowerCase()}',\n localField: '${tableCapitalized.toLowerCase()}Ids',\n foreignField: '_id'\n});`;
|
198
|
+
}
|
199
|
+
|
200
|
+
default:
|
201
|
+
return '';
|
202
|
+
}
|
203
|
+
}
|