@zuzjs/orm 0.3.7 → 0.3.8

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/dist/core/cli.js DELETED
@@ -1,78 +0,0 @@
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
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.Pull = exports.buildEntities = void 0;
40
- const fs_1 = __importStar(require("fs"));
41
- const path_1 = __importDefault(require("path"));
42
- const picocolors_1 = __importDefault(require("picocolors"));
43
- const index_js_1 = require("./index.js");
44
- const mysql_1 = require("../drivers/mysql");
45
- const buildEntities = (connection, dist) => {
46
- dist = dist || path_1.default.join(`src`, `zorm`);
47
- const _checkDist = (0, index_js_1.checkDirectory)(path_1.default.join(process.cwd(), dist), true);
48
- if (!_checkDist.access) {
49
- console.log(picocolors_1.default.red(`○ ${_checkDist.message}`));
50
- process.exit(1);
51
- }
52
- if (connection.startsWith(`mysql`)) {
53
- const driver = new mysql_1.MySqlDriver(decodeURIComponent(connection), dist);
54
- driver.generate();
55
- }
56
- else {
57
- console.log(`○ Only MySQL is supported for now`);
58
- process.exit(1);
59
- }
60
- };
61
- exports.buildEntities = buildEntities;
62
- /**
63
- * Pull using DATABASE_URL from .env in project directory
64
- **/
65
- const Pull = () => {
66
- const env = path_1.default.join(process.cwd(), `.env`);
67
- if (!(0, fs_1.existsSync)(env)) {
68
- console.log(picocolors_1.default.red(`○ ".env" not exists. Create .env and add DATABASE_URL="connection_string"`));
69
- return;
70
- }
71
- const raw = fs_1.default.readFileSync(env, `utf8`).split(`\n`).filter((line) => line.startsWith(`DATABASE_URL`));
72
- if (raw.length == 0) {
73
- console.log(picocolors_1.default.red(`○ DATABASE_URL not found in ".env". Add DATABASE_URL="connection_string"`));
74
- return;
75
- }
76
- (0, exports.buildEntities)(raw[0].trim().replace(/DATABASE_URL=|"|"/g, ``));
77
- };
78
- exports.Pull = Pull;
@@ -1,8 +0,0 @@
1
- export declare const toPascalCase: (str: string) => string;
2
- export declare const mapSqlTypeToTypescript: (sqlType: string) => string;
3
- export declare const checkDirectory: (path: string, create: boolean) => {
4
- access: boolean;
5
- message?: string;
6
- };
7
- export declare const stackTrace: (_error: string, ...more: string[]) => Error;
8
- export declare const isNumber: (val: any) => boolean;
@@ -1,72 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.isNumber = exports.stackTrace = exports.checkDirectory = exports.mapSqlTypeToTypescript = exports.toPascalCase = void 0;
7
- const fs_1 = __importDefault(require("fs"));
8
- const toPascalCase = (str) => {
9
- return str.replace(/(^\w|_\w)/g, (match) => match.replace("_", "").toUpperCase());
10
- };
11
- exports.toPascalCase = toPascalCase;
12
- const mapSqlTypeToTypescript = (sqlType) => {
13
- if (sqlType.startsWith("int") || sqlType.startsWith("tinyint"))
14
- return "number";
15
- if (sqlType.startsWith("varchar") || sqlType.startsWith("text"))
16
- return "string";
17
- if (sqlType.startsWith("datetime") || sqlType.startsWith("timestamp"))
18
- return "Date";
19
- return "any";
20
- };
21
- exports.mapSqlTypeToTypescript = mapSqlTypeToTypescript;
22
- const checkDirectory = (path, create) => {
23
- try {
24
- // Check if the path exists
25
- if (!fs_1.default.existsSync(path)) {
26
- if (create)
27
- fs_1.default.mkdirSync(path, { recursive: true });
28
- else
29
- return { access: false, message: `${path} does not exist.\nRun \`zorm -c [DATABASE_CONNECTION_STRING]\`` };
30
- }
31
- // Check if it's a directory
32
- const stat = fs_1.default.statSync(path);
33
- if (!stat.isDirectory())
34
- return { access: false, message: `${path} is not a folder.` };
35
- // Check if it's writable
36
- try {
37
- fs_1.default.accessSync(path, fs_1.default.constants.W_OK);
38
- }
39
- catch (e) {
40
- return { access: false, message: `${path} has no write permission.` };
41
- }
42
- return { access: true };
43
- }
44
- catch (err) {
45
- return { access: false, message: `Destination folder is not accessible` };
46
- }
47
- };
48
- exports.checkDirectory = checkDirectory;
49
- const stackTrace = (_error, ...more) => {
50
- const error = new Error([_error, ...more].join(` `));
51
- const lines = [error.message];
52
- // const lines : string[] = [`${pc.bgRed(pc.whiteBright(` Zorm `))} ${pc.red(error.message)}`]
53
- // const regex = /\((\/.*\.[a-zA-Z0-9]+):\d+:\d+\)/;
54
- // const regex = /(\/.*\.[a-zA-Z0-9]+):\d+:\d+/;
55
- // error.stack?.split(`\n`)
56
- // .forEach((line, index) => {
57
- // if ( line.includes(process.cwd()) ){
58
- // const match = line.match(regex)
59
- // if ( match ){
60
- // const [f, _line, _column] = match[0].split(`:`)
61
- // const rawLine = fs.readFileSync(match[1], `utf8`).split(`\n`)[Number(_line)-1]
62
- // const ls = f.split(`/`)
63
- // lines.push(`${ls[ls.length-3]}/${ls[ls.length-2]}/${ls[ls.length-1]} - ZormError: ${_error}`)
64
- // lines.push(`${pc.bgBlack(pc.white(_line))}\t${rawLine}`)
65
- // }
66
- // }
67
- // })
68
- return Error(lines.join(`\n`));
69
- };
70
- exports.stackTrace = stackTrace;
71
- const isNumber = (val) => /^[+-]?\d+(\.\d+)?$/.test(val);
72
- exports.isNumber = isNumber;
@@ -1,32 +0,0 @@
1
- import { ObjectLiteral } from "typeorm";
2
- declare class ZormExprBuilder<T extends ObjectLiteral> {
3
- private _parts;
4
- private _params;
5
- private _paramIdx;
6
- private _alias;
7
- private static globalParamIdx;
8
- constructor(alias?: string, parent?: {
9
- params: Record<string, any>;
10
- idx: number;
11
- });
12
- field(col: keyof T | string): this;
13
- equals(value: any): this;
14
- append(extra: string): this;
15
- wrap(wrapper: (expr: string) => string): this;
16
- exists(sub: (q: ZormExprBuilder<T>) => ZormExprBuilder<T>): this;
17
- select(expr: string): this;
18
- from(table: string, alias: string): this;
19
- where(cond: Record<string, any>): this;
20
- or(): this;
21
- group(): this;
22
- fromUnixTime(): this;
23
- date(): this;
24
- substring(column: keyof T | string, delimiter: string, index: number): this;
25
- toExpression(): {
26
- expression: string;
27
- param: Record<string, any>;
28
- };
29
- buildExpression(): string;
30
- buildParams(): Record<string, any>;
31
- }
32
- export default ZormExprBuilder;
@@ -1,100 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- class ZormExprBuilder {
4
- _parts = [];
5
- _params = {};
6
- _paramIdx = 0;
7
- _alias;
8
- static globalParamIdx = 0;
9
- constructor(alias, parent) {
10
- this._alias = alias ? `${alias}.` : ``;
11
- this._paramIdx = parent ? parent.idx : 0;
12
- this._params = parent ? parent.params : {};
13
- }
14
- field(col) {
15
- this._parts.push(`${this._alias}${String(col)}`);
16
- return this;
17
- }
18
- equals(value) {
19
- const key = `p${this._paramIdx++}`;
20
- this.append(` = :${key}`);
21
- // this._parts[this._parts.length - 1] += ` = :${key}`;
22
- this._params[key] = value;
23
- return this;
24
- }
25
- append(extra) {
26
- if (this._parts.length === 0)
27
- throw new Error("Cannot append to empty expression");
28
- this._parts[this._parts.length - 1] += extra;
29
- return this;
30
- }
31
- wrap(wrapper) {
32
- if (this._parts.length === 0)
33
- throw new Error("Cannot wrap empty expression");
34
- this._parts[this._parts.length - 1] = wrapper(this._parts[this._parts.length - 1]);
35
- return this;
36
- }
37
- exists(sub) {
38
- const subQ = new ZormExprBuilder(undefined, { params: this._params, idx: this._paramIdx });
39
- sub(subQ);
40
- // const { expression, param } = subQ.toExpression();
41
- this._parts.push(`EXISTS (${subQ.buildExpression()})`);
42
- // Object.assign(this._params, param);
43
- this._paramIdx = subQ._paramIdx;
44
- return this;
45
- }
46
- select(expr) {
47
- this._parts.push(`SELECT ${expr}`);
48
- return this;
49
- }
50
- from(table, alias) {
51
- this._parts.push(`FROM ${table} ${alias}`);
52
- this._alias = alias;
53
- return this;
54
- }
55
- where(cond) {
56
- const whereParts = [];
57
- for (const [k, v] of Object.entries(cond)) {
58
- const key = `p${this._paramIdx++}`;
59
- whereParts.push(`${k} = :${key}`);
60
- this._params[key] = v;
61
- }
62
- this._parts.push(`WHERE ${whereParts.join(' AND ')}`);
63
- return this;
64
- }
65
- // or(sub: ZormExprBuilder<any> | { expression: string; param: Record<string, any> }): this {
66
- // const { expression, param } = 'toExpression' in sub ? sub.toExpression() : sub;
67
- // this._parts.push(this._parts.length === 0 ? expression : `OR ${expression}`);
68
- // Object.assign(this._params, param);
69
- // return this;
70
- // }
71
- or() {
72
- this._parts.push(`OR`);
73
- return this;
74
- }
75
- group() {
76
- return this.wrap(e => `(${e})`);
77
- }
78
- fromUnixTime() {
79
- this.wrap(expr => `FROM_UNIXTIME(${expr})`);
80
- return this;
81
- }
82
- date() {
83
- this.wrap(expr => `DATE(${expr})`);
84
- return this;
85
- }
86
- substring(column, delimiter, index) {
87
- this._parts = [`SUBSTRING_INDEX(${this._alias}${String(column)}, '${delimiter}', ${index})`];
88
- return this;
89
- }
90
- toExpression() {
91
- return { expression: this._parts.join(' '), param: this._params };
92
- }
93
- buildExpression() {
94
- return this.toExpression().expression;
95
- }
96
- buildParams() {
97
- return this.toExpression().param;
98
- }
99
- }
100
- exports.default = ZormExprBuilder;
@@ -1,39 +0,0 @@
1
- import { Pool } from 'mysql2/promise';
2
- import { ConnectionDetails, ModelGenerator } from '../../types.js';
3
- export declare const parseConnectionString: (connectionString: string) => {
4
- user: string;
5
- password: string;
6
- host: string;
7
- port: number;
8
- database: string;
9
- params: {
10
- [k: string]: string;
11
- };
12
- };
13
- export declare const MySQLErrorMap: Record<string, string>;
14
- export declare class MySqlDriver implements ModelGenerator {
15
- pool: Pool | null;
16
- conn: ConnectionDetails;
17
- dist: string | null;
18
- constructor(connectionString: string, dist?: string);
19
- connection(): ConnectionDetails;
20
- parseConnectionString: (connectionString: string) => {
21
- user: string;
22
- password: string;
23
- host: string;
24
- port: number;
25
- database: string;
26
- params: {
27
- [k: string]: string;
28
- };
29
- };
30
- createPool(): void;
31
- mapColumns(sqlType: string): {
32
- tsType: string;
33
- columnType: string;
34
- length?: number;
35
- enumValues?: string[];
36
- };
37
- formatDefault(value: any, tsType: string): string | number;
38
- generate(): Promise<void>;
39
- }
@@ -1,336 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.MySqlDriver = exports.MySQLErrorMap = exports.parseConnectionString = void 0;
7
- const fs_1 = __importDefault(require("fs"));
8
- const promise_1 = __importDefault(require("mysql2/promise"));
9
- const path_1 = __importDefault(require("path"));
10
- const picocolors_1 = __importDefault(require("picocolors"));
11
- const index_js_1 = require("../../core/index.js");
12
- const parseConnectionString = (connectionString) => {
13
- const regex = /mysql:\/\/(?<user>[^:]+):(?<password>[^@]+)@(?<host>[^:/]+)(?::(?<port>\d+))?\/(?<database>[^?]+)(?:\?(?<params>.+))?/;
14
- const match = connectionString.match(regex);
15
- if (!match || !match.groups) {
16
- throw new Error("Invalid MySQL connection string");
17
- }
18
- const { user, password, host, port, database, params } = match.groups;
19
- const queryParams = params ? Object.fromEntries(new URLSearchParams(params)) : {};
20
- return { user, password, host, port: port ? Number(port) : 3306, database, params: queryParams };
21
- };
22
- exports.parseConnectionString = parseConnectionString;
23
- exports.MySQLErrorMap = {
24
- ER_DUP_ENTRY: "DuplicateEntry",
25
- ER_NO_REFERENCED_ROW: "ForeignKeyConstraintFails",
26
- ER_NO_REFERENCED_ROW_2: "ForeignKeyConstraintFails",
27
- ER_BAD_NULL_ERROR: "NullValueNotAllowed",
28
- ER_PARSE_ERROR: "SQLSyntaxError",
29
- ER_ACCESS_DENIED_ERROR: "AccessDenied",
30
- ER_TABLE_EXISTS_ERROR: "TableAlreadyExists",
31
- ER_NO_SUCH_TABLE: "TableNotFound",
32
- ER_LOCK_WAIT_TIMEOUT: "LockWaitTimeout",
33
- ER_LOCK_DEADLOCK: "DeadlockDetected",
34
- ER_DATA_TOO_LONG: "DataTooLong",
35
- ER_TRUNCATED_WRONG_VALUE: "InvalidDataFormat",
36
- ER_WRONG_VALUE_COUNT_ON_ROW: "WrongValueCount",
37
- ER_CANT_CREATE_TABLE: "TableCreationFailed",
38
- ER_CANT_DROP_FIELD_OR_KEY: "CannotDropKey",
39
- ER_ROW_IS_REFERENCED: "RowReferenced",
40
- ER_ROW_IS_REFERENCED_2: "RowReferenced",
41
- ER_PRIMARY_CANT_HAVE_NULL: "PrimaryKeyCannotBeNull",
42
- ER_KEY_COLUMN_DOES_NOT_EXITS: "KeyColumnNotFound",
43
- ER_UNKNOWN_COLUMN: "UnknownColumn",
44
- ER_WRONG_DB_NAME: "InvalidDatabaseName",
45
- ER_WRONG_TABLE_NAME: "InvalidTableName",
46
- ER_UNKNOWN_PROCEDURE: "ProcedureNotFound",
47
- ER_DUP_UNIQUE: "UniqueConstraintViolation",
48
- ER_SP_DOES_NOT_EXIST: "StoredProcedureNotFound",
49
- ER_BAD_FIELD_ERROR: "InvalidColumn",
50
- };
51
- class MySqlDriver {
52
- pool;
53
- conn;
54
- dist;
55
- constructor(connectionString, dist) {
56
- this.pool = null;
57
- this.dist = dist || null;
58
- this.conn = this.parseConnectionString(connectionString);
59
- }
60
- connection() {
61
- return this.conn;
62
- }
63
- parseConnectionString = (connectionString) => {
64
- const regex = /mysql:\/\/(?<user>[^:]+):(?<password>[^@]+)@(?<host>[^:/]+)(?::(?<port>\d+))?\/(?<database>[^?]+)(?:\?(?<params>.+))?/;
65
- const match = connectionString.match(regex);
66
- if (!match || !match.groups) {
67
- throw new Error("Invalid MySQL connection string");
68
- }
69
- const { user, password, host, port, database, params } = match.groups;
70
- const queryParams = params ? Object.fromEntries(new URLSearchParams(params)) : {};
71
- return { user, password, host, port: port ? Number(port) : 3306, database, params: queryParams };
72
- };
73
- createPool() {
74
- if (!this.pool) {
75
- try {
76
- this.pool = promise_1.default.createPool({
77
- user: this.conn.user,
78
- password: this.conn.password,
79
- host: this.conn.host,
80
- port: this.conn.port,
81
- database: this.conn.database,
82
- ...this.conn.params
83
- });
84
- this.pool.getConnection()
85
- .then((connection) => {
86
- console.log(picocolors_1.default.green("✓ MySQL Connected..."));
87
- connection.release();
88
- })
89
- .catch((err) => {
90
- console.error(picocolors_1.default.red("○ Error while connecting to your MySQL Server with following error:"), err);
91
- process.exit(1); // Exit process if connection fails
92
- });
93
- }
94
- catch (error) {
95
- console.error(picocolors_1.default.red("○ Error while connecting to your MySQL Server with following error:"), error);
96
- process.exit(1);
97
- }
98
- }
99
- }
100
- mapColumns(sqlType) {
101
- const typeMap = {
102
- "int": { tsType: "number", columnType: "int" },
103
- "tinyint": { tsType: "boolean", columnType: "tinyint" },
104
- "smallint": { tsType: "number", columnType: "smallint" },
105
- "mediumint": { tsType: "number", columnType: "mediumint" },
106
- "bigint": { tsType: "number", columnType: "bigint" }, // bigint is safer as string
107
- "decimal": { tsType: "number", columnType: "decimal" },
108
- "float": { tsType: "number", columnType: "float" },
109
- "double": { tsType: "number", columnType: "double" },
110
- "varchar": { tsType: "string", columnType: "varchar", length: 255 },
111
- "text": { tsType: "string", columnType: "text" },
112
- "longtext": { tsType: "string", columnType: "longtext" },
113
- "char": { tsType: "string", columnType: "char", length: 1 },
114
- "datetime": { tsType: "Date", columnType: "datetime" },
115
- "timestamp": { tsType: "Date", columnType: "timestamp" },
116
- "date": { tsType: "Date", columnType: "date" },
117
- "time": { tsType: "string", columnType: "time" },
118
- "json": { tsType: "any", columnType: "json" },
119
- };
120
- const enumMatch = sqlType.match(/^enum\((.*)\)$/i);
121
- if (enumMatch) {
122
- const enumValues = enumMatch[1]
123
- .split(",")
124
- .map((val) => val.trim().replace(/^'|'$/g, "")); // Remove single quotes
125
- return { tsType: `"${enumValues.join('" | "')}"`, columnType: "enum", enumValues };
126
- }
127
- const match = sqlType.match(/^(\w+)(?:\((\d+)\))?/);
128
- if (!match)
129
- return { tsType: "any", columnType: "varchar", length: 255 };
130
- const baseType = match[1].toLowerCase();
131
- const length = match[2] ? parseInt(match[2], 10) : undefined;
132
- return typeMap[baseType] ? { ...typeMap[baseType], length: length || typeMap[baseType].length } : { tsType: "any", columnType: "varchar", length: 255 };
133
- }
134
- formatDefault(value, tsType) {
135
- if (tsType === "number")
136
- return Number(value);
137
- if (tsType === "boolean")
138
- return value === "1" ? "true" : "false";
139
- return `"${value}"`;
140
- }
141
- async generate() {
142
- const self = this;
143
- self.createPool();
144
- const numberTypes = [`int`, `bigint`, `tinyint`, `decimal`];
145
- //Extract Tables
146
- console.log(picocolors_1.default.cyan("○ Extract Tables..."));
147
- const [tables] = await self.pool.execute("SHOW TABLES");
148
- console.log(picocolors_1.default.cyan(`○ ${tables.length} Tables Found.`));
149
- const tableNames = tables.map((row) => Object.values(row)[0]);
150
- console.log(picocolors_1.default.yellow(`○ Generating Models...`));
151
- // Fetch foreign keys
152
- const foreignKeys = {};
153
- for (const tableName of tableNames) {
154
- const [fkResults] = await this.pool.execute(`SELECT COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME
155
- FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
156
- WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? AND REFERENCED_TABLE_NAME IS NOT NULL`, [this.conn.database, tableName]);
157
- foreignKeys[tableName] = fkResults;
158
- }
159
- // Track inverse relations: { tableName: { fkColumn: targetTable } }
160
- const inverseRelations = {};
161
- // Populate inverse map
162
- for (const [tableName, fks] of Object.entries(foreignKeys)) {
163
- for (const fk of fks) {
164
- const targetTable = fk.REFERENCED_TABLE_NAME;
165
- const fkColumn = fk.COLUMN_NAME;
166
- const fkPropName = `fk${(0, index_js_1.toPascalCase)(targetTable)}`;
167
- if (!inverseRelations[targetTable]) {
168
- inverseRelations[targetTable] = [];
169
- }
170
- inverseRelations[targetTable].push({
171
- fkColumn,
172
- fkPropName,
173
- targetTable: tableName,
174
- targetEntity: (0, index_js_1.toPascalCase)(tableName),
175
- });
176
- }
177
- }
178
- // Detect 2 Column Junction Tables for ManyToMany
179
- const junctionTables = {};
180
- for (const tableName of tableNames) {
181
- const [cols] = await this.pool.execute(`SELECT COLUMN_NAME, COLUMN_KEY, EXTRA FROM INFORMATION_SCHEMA.COLUMNS
182
- WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?`, [this.conn.database, tableName]);
183
- const fks = foreignKeys[String(tableName)] || [];
184
- if (fks.length !== 2)
185
- continue;
186
- // Must have exactly 2 columns
187
- if (cols.length !== 2)
188
- continue;
189
- // Both columns must be primary key
190
- if (cols.filter(c => c.COLUMN_KEY !== 'PRI').length > 0)
191
- continue;
192
- const [left, right] = fks;
193
- const [t1, t2] = [left.REFERENCED_TABLE_NAME, right.REFERENCED_TABLE_NAME].sort();
194
- const key = `${t1}_${t2}`;
195
- if (!junctionTables[key]) {
196
- junctionTables[key] = {
197
- left: t1,
198
- right: t2,
199
- leftCol: left.COLUMN_NAME,
200
- rightCol: right.COLUMN_NAME
201
- };
202
- }
203
- }
204
- for (const tableName of tableNames) {
205
- const enums = [];
206
- const imports = [];
207
- const _imports = [`Entity`, `BaseEntity`];
208
- // Get table structure
209
- const [columns] = await this.pool.execute(`SELECT COLUMN_NAME as \`Field\`, COLUMN_TYPE as \`Type\`, COLUMN_KEY as \`Key\`, IS_NULLABLE as \`Null\`, COLUMN_DEFAULT as \`Default\`, EXTRA as \`Extra\`, COLUMN_COMMENT as \`Comment\`
210
- FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? ORDER BY ORDINAL_POSITION ASC`, [this.conn.database, tableName]);
211
- const entityCode = [];
212
- let hasPrimary = false;
213
- for (const column of columns) {
214
- // console.log(tableName, column)
215
- const { Field, Type, Key, Null, Default, Extra, Comment } = column;
216
- const { tsType, columnType, length, enumValues } = this.mapColumns(Type);
217
- let enumName = null;
218
- if (columnType === "enum" && enumValues) {
219
- enumName = (0, index_js_1.toPascalCase)(Field);
220
- enums.push(`export enum ${enumName} { ${enumValues.map(v => {
221
- return `${(0, index_js_1.isNumber)(v) ? `val${v}` : (0, index_js_1.toPascalCase)(v)} = "${v}"`;
222
- }).join(", ")} }`);
223
- }
224
- // Handle primary key
225
- if (Key === "PRI") {
226
- const _priColumn = Extra.includes("auto_increment") ? `PrimaryGeneratedColumn` : `PrimaryColumn`;
227
- if (!_imports.includes(_priColumn))
228
- _imports.push(_priColumn);
229
- entityCode.push(`\t@${_priColumn}()`);
230
- hasPrimary = true;
231
- }
232
- else {
233
- // const hasForeignKey = foreignKeys[tableName as string].find((fk) => fk.COLUMN_NAME === Field);
234
- // let columnDecorator = hasForeignKey ? `\t@JoinColumn({ type: "${columnType}"` : `\t@Column({ type: "${columnType}"`;
235
- let columnDecorator = `\t@Column({ type: "${columnType}"`;
236
- if (!_imports.includes(`Column`))
237
- _imports.push(`Column`);
238
- if (length)
239
- columnDecorator += `, length: ${length}`;
240
- if (Null === "YES")
241
- columnDecorator += `, nullable: true`;
242
- if (enumName)
243
- columnDecorator += `, enum: ${enumName}`;
244
- if (Default !== null)
245
- columnDecorator += `, default: ${this.formatDefault(Default, tsType)}`;
246
- columnDecorator += ` })`;
247
- entityCode.push(columnDecorator);
248
- }
249
- if (Comment && Comment.length > 0) {
250
- entityCode.push(`\t/** @comment ${Comment} */`);
251
- }
252
- entityCode.push(`\t${Field}!: ${enumName ? enumName : Key == `PRI` && numberTypes.includes(Type) ? `number` : numberTypes.includes(Type) ? `number` : tsType};\n`);
253
- }
254
- // Add foreign key relationships
255
- if (foreignKeys[tableName]) {
256
- for (const fk of foreignKeys[tableName] || []) {
257
- const relatedEntity = (0, index_js_1.toPascalCase)(fk.REFERENCED_TABLE_NAME);
258
- if (imports.includes(`import { ${relatedEntity} } from "./${fk.REFERENCED_TABLE_NAME}";`) === false) {
259
- entityCode.push(`\t@OneToOne(() => ${relatedEntity})`);
260
- entityCode.push(`\t@JoinColumn({ name: "${fk.COLUMN_NAME}" })`);
261
- entityCode.push(`\tfk${relatedEntity}!: ${relatedEntity};\n`);
262
- imports.push(`import { ${relatedEntity} } from "./${fk.REFERENCED_TABLE_NAME}";`);
263
- if (!_imports.includes(`OneToOne`))
264
- _imports.push(`OneToOne`);
265
- if (!_imports.includes(`JoinColumn`))
266
- _imports.push(`JoinColumn`);
267
- }
268
- }
269
- }
270
- // Add OneToMany Relations
271
- const inverse = inverseRelations[String(tableName)] || [];
272
- for (const rel of inverse) {
273
- const propName = rel.targetTable.endsWith('s') ? rel.targetTable : `${rel.targetTable}s`;
274
- if (entityCode.some(line => line.includes(` ${propName}!:`)))
275
- continue;
276
- const importLine = `import { ${rel.targetEntity} } from "./${rel.targetTable}";`;
277
- if (!imports.includes(importLine))
278
- imports.push(importLine);
279
- if (!_imports.includes('OneToMany'))
280
- _imports.push('OneToMany');
281
- // CORRECT: Inverse is fk + referenced table (e.g. fkUsers)
282
- entityCode.push(`\t@OneToMany(() => ${rel.targetEntity}, r => r.${rel.fkPropName})`);
283
- entityCode.push(`\tfk${(0, index_js_1.toPascalCase)(propName)}!: ${rel.targetEntity}[];\n`);
284
- // entityCode.push(`\t@OneToMany(() => ${targetEntity}, r => r.${rel.fkColumn})`);
285
- // entityCode.push(`\tfk${toPascalCase(propName)}!: ${targetEntity}[];\n`);
286
- }
287
- // Add Many-to-Many Relations
288
- const junctions = Object.values(junctionTables)
289
- .filter(j => j.left === tableName || j.right === tableName);
290
- for (const j of junctions) {
291
- const targetTable = j.left === tableName ? j.right : j.left;
292
- const targetEntity = (0, index_js_1.toPascalCase)(targetTable);
293
- const propName = targetTable.endsWith('s') ? targetTable : `${targetTable}s`;
294
- if (entityCode.some(line => line.includes(`@${propName}`)))
295
- continue;
296
- const importLine = `import { ${targetEntity} } from "./${targetTable}";`;
297
- if (!imports.includes(importLine)) {
298
- imports.push(importLine);
299
- }
300
- if (!_imports.includes('ManyToMany'))
301
- _imports.push('ManyToMany');
302
- if (!_imports.includes('JoinTable'))
303
- _imports.push('JoinTable');
304
- entityCode.push(`\t@ManyToMany(() => ${targetEntity})`);
305
- entityCode.push(`\t@JoinTable()`);
306
- entityCode.push(`\tom${propName}!: ${targetEntity}[];\n`);
307
- }
308
- const Code = [
309
- `/**`,
310
- `* AutoGenerated by @zuzjs/orm.`,
311
- `* @ ${new Date().toString().split(` GMT`)[0].trim()}`,
312
- `*/`,
313
- `import { ${_imports.join(`, `)} } from "@zuzjs/orm";`,
314
- imports.length > 0 ? imports.join(`\n`) : ``,
315
- enums.length > 0 ? enums.join(`\n`) : ``,
316
- `${enums.length > 0 || imports.length > 0 ? `\n` : ``}@Entity({ name: "${tableName}" })`,
317
- `export class ${(0, index_js_1.toPascalCase)(tableName)} extends BaseEntity {\n`,
318
- ...entityCode,
319
- `}`
320
- ];
321
- if (!hasPrimary) {
322
- console.log(picocolors_1.default.bgRed(picocolors_1.default.whiteBright(` WARNING `)), picocolors_1.default.yellow(`○ "${tableName}" does not have a primary column. Primary column is required.`));
323
- }
324
- // Write entity file
325
- fs_1.default.writeFileSync(path_1.default.join(this.dist, `${tableName}.ts`), Code.join(`\n`));
326
- }
327
- // Write entry file i.e index.ts
328
- const entry = tableNames
329
- .map(tableName => `import { ${(0, index_js_1.toPascalCase)(tableName)} } from "./${tableName}";`);
330
- entry.push(`import Zorm from "@zuzjs/orm";`, `import de from "dotenv";`, `de.config()`, `const zormEntities = [${tableNames.map(t => (0, index_js_1.toPascalCase)(t)).join(`, `)}];`, `const zorm = Zorm.get(`, `\tprocess.env.DATABASE_URL!,`, `\tzormEntities`, `);`, `zorm.connect(zormEntities);`, `export default zorm`, `export { ${tableNames.map(t => (0, index_js_1.toPascalCase)(t)).join(`, `)} }`);
331
- fs_1.default.writeFileSync(path_1.default.join(this.dist, `index.ts`), entry.join(`\n`));
332
- await self.pool.end();
333
- console.log(picocolors_1.default.green(`✓ ${tables.length} Tables Processed.`));
334
- }
335
- }
336
- exports.MySqlDriver = MySqlDriver;