#!/usr/bin/env node const fs = require('fs'); const path = require('path'); const repoRoot = path.resolve(__dirname, '..'); const sqlFile = path.join(repoRoot, 'sql', 'wwjcloud.sql'); const outDir = path.join(repoRoot, 'temp', 'entities'); if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true }); const sql = fs.readFileSync(sqlFile, 'utf8'); // crude parser: split by CREATE TABLE `table` const tableRegex = /CREATE TABLE\s+`([^`]+)`\s*\(([^;]+)\)\s*ENGINE=[^;]+;/gim; function pascalCase(name) { return name .replace(/^[^a-zA-Z]+/, '') .split(/[_\-\s]+/) .map((s) => s.charAt(0).toUpperCase() + s.slice(1)) .join(''); } function mapColumnType(def) { const d = def.toLowerCase(); if (d.startsWith('int') || d.startsWith('tinyint') || d.startsWith('smallint') || d.startsWith('bigint')) return { type: 'int' }; if (d.startsWith('varchar')) { const m = d.match(/varchar\((\d+)\)/); return { type: 'varchar', length: m ? parseInt(m[1], 10) : 255 }; } if (d.startsWith('text') || d.includes('longtext')) return { type: 'text' }; if (d.startsWith('decimal')) { const m = d.match(/decimal\((\d+)\s*,\s*(\d+)\)/); return { type: 'decimal', precision: m ? parseInt(m[1], 10) : 10, scale: m ? parseInt(m[2], 10) : 2 }; } if (d.startsWith('timestamp')) return { type: 'int' }; if (d.startsWith('datetime')) return { type: 'int' }; if (d.startsWith('enum')) return { type: 'varchar', length: 255 }; return { type: 'varchar', length: 255 }; } function parseDefault(defPart) { const m = defPart.match(/default\s+([^\s]+)/i); if (!m) return undefined; let v = m[1].trim(); v = v.replace(/^'/, '').replace(/'$/, ''); if (v.toLowerCase() === 'null') return undefined; if (/^[0-9.]+$/.test(v)) return Number(v); return `'${v}'`; } function generateEntity(tableName, columnsBlock) { const className = pascalCase(tableName); const lines = columnsBlock.split(/\n/).map((l) => l.trim()).filter(Boolean); const fields = []; for (const line of lines) { if (line.startsWith('PRIMARY KEY') || line.startsWith('UNIQUE') || line.startsWith('KEY') || line.startsWith(')')) continue; const m = line.match(/^`([^`]+)`\s+([^\s,]+)([^,]*),?$/); if (!m) continue; const col = m[1]; const typeDef = m[2]; const rest = m[3] || ''; const isPk = /auto_increment/i.test(rest) || col === 'id'; const { type, length, precision, scale } = mapColumnType(typeDef); const defVal = parseDefault(rest); fields.push({ col, isPk, type, length, precision, scale, defVal }); } const imports = new Set(['Entity', 'Column']); if (fields.some((f) => f.isPk)) imports.add('PrimaryGeneratedColumn'); const importLine = `import { ${Array.from(imports).join(', ')} } from 'typeorm';`; const props = fields.map((f) => { if (f.isPk) { return ` @PrimaryGeneratedColumn({ type: 'int' })\n id: number;`; } const opts = []; opts.push(`name: '${f.col}'`); opts.push(`type: '${f.type}'`); if (f.length) opts.push(`length: ${f.length}`); if (f.precision) opts.push(`precision: ${f.precision}`); if (f.scale !== undefined) opts.push(`scale: ${f.scale}`); if (f.defVal !== undefined) opts.push(`default: ${f.defVal}`); const propName = f.col.replace(/_([a-z])/g, (_, c) => c.toUpperCase()); return ` @Column({ ${opts.join(', ')} })\n ${propName}: ${f.type === 'decimal' ? 'string' : 'any'};`; }).join('\n\n'); return `${importLine}\n\n@Entity('${tableName}')\nexport class ${className} {\n${props}\n}\n`; } let match; let count = 0; while ((match = tableRegex.exec(sql)) !== null) { const table = match[1]; const body = match[2]; const ts = generateEntity(table, body); const outFile = path.join(outDir, `${table}.ts`); fs.writeFileSync(outFile, ts, 'utf8'); count++; } console.log(`Generated ${count} entities into ${path.relative(repoRoot, outDir)}`);