.auto-generator.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793
  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.AutoGenerator = void 0;
  7. const lodash_1 = __importDefault(require("lodash"));
  8. const types_1 = require("./types");
  9. /** Generates text from each table in TableData */
  10. class AutoGenerator {
  11. constructor(tableData, dialect, options) {
  12. this.tables = tableData.tables;
  13. this.foreignKeys = tableData.foreignKeys;
  14. this.hasTriggerTables = tableData.hasTriggerTables;
  15. this.indexes = tableData.indexes;
  16. this.relations = tableData.relations;
  17. this.dialect = dialect;
  18. this.options = options;
  19. this.options.lang = this.options.lang || 'es5';
  20. this.space = (0, types_1.makeIndent)(this.options.spaces, this.options.indentation);
  21. this.attrbuitesstr = '';
  22. }
  23. makeHeaderTemplate() {
  24. const header = "import { Column, DataType, Table, Model,Sequelize } from 'sequelize-typescript';\n\n";
  25. return header;
  26. }
  27. // 生成文本
  28. generateText() {
  29. const tableNames = lodash_1.default.keys(this.tables);
  30. // 创建通用文件头部内容
  31. const header = this.makeHeaderTemplate();
  32. const text = {};
  33. tableNames.forEach(table => {
  34. let str = header;
  35. const [schemaName, tableNameOrig] = (0, types_1.qNameSplit)(table);
  36. const tableName = (0, types_1.makeTableName)(this.options.caseModel, tableNameOrig, this.options.singularize, this.options.lang);
  37. // 为表的模型创建一个字符串
  38. str += this.addTable(table).trim();
  39. const re = new RegExp('#TABLE#', 'g');
  40. str = str.replace(re, tableName);
  41. text[table] = str;
  42. });
  43. return text;
  44. }
  45. addTable(table) {
  46. const [schemaName, tableNameOrig] = (0, types_1.qNameSplit)(table);
  47. const space = this.space;
  48. let timestamps = (this.options.additional && this.options.additional.timestamps === true) || false;
  49. let paranoid = (this.options.additional && this.options.additional.paranoid === true) || false;
  50. // 为表全部的字段生成字符串
  51. let str = '@Table({\n';
  52. str += space[2] + "tableName: '" + tableNameOrig + "',\n";
  53. if (schemaName && this.dialect.hasSchema) {
  54. str += space[2] + "schema: '" + schemaName + "',\n";
  55. }
  56. if (this.hasTriggerTables[table]) {
  57. str += space[2] + "hasTrigger: true,\n";
  58. }
  59. str += space[2] + "timestamps: " + timestamps + ",\n";
  60. if (paranoid) {
  61. str += space[2] + "paranoid: true,\n";
  62. }
  63. // 有条件地添加额外选项
  64. const hasadditional = lodash_1.default.isObject(this.options.additional) && lodash_1.default.keys(this.options.additional).length > 0;
  65. if (hasadditional) {
  66. lodash_1.default.each(this.options.additional, (value, key) => {
  67. if (key === 'name') {
  68. // name: true - preserve table name always
  69. str += space[2] + "name: {\n";
  70. str += space[3] + "singular: '" + table + "',\n";
  71. str += space[3] + "plural: '" + table + "'\n";
  72. str += space[2] + "},\n";
  73. }
  74. else if (key === "timestamps" || key === "paranoid") {
  75. // handled above
  76. }
  77. else {
  78. value = lodash_1.default.isBoolean(value) ? value : ("'" + value + "'");
  79. str += space[2] + key + ": " + value + ",\n";
  80. }
  81. });
  82. }
  83. if (!this.options.noIndexes) {
  84. // 添加索引
  85. str += this.addIndexes(table);
  86. }
  87. str = space[2] + str.trim();
  88. str = str.substring(0, str.length - 1);
  89. str += "\n})\n";
  90. const characters = [...lodash_1.default.camelCase(tableNameOrig)];
  91. characters[0] = characters[0].toUpperCase();
  92. const entityName = characters.join("");
  93. str += `export class ${entityName}Entity extends Model {\n`;
  94. const fields = lodash_1.default.keys(this.tables[table]);
  95. fields.forEach((field, index) => {
  96. // 判断是否为 created_at 和 updated_at 字段
  97. timestamps || (timestamps = this.isTimestampField(field));
  98. // 判断是否为 deleted_at 字段
  99. paranoid || (paranoid = this.isParanoidField(field));
  100. // 创建一个包含字段属性(类型、defaultValue等)的字符串
  101. str += this.addField(table, field);
  102. });
  103. // 移除最后的 ",\n"
  104. str = str.substring(0, str.length - 2) + "\n}";
  105. str += this.addAttributesHeader(entityName,this.attrbuitesstr);
  106. this.attrbuitesstr = '';
  107. return str;
  108. }
  109. addField(table, field) {
  110. // 忽略 Sequelize 标准字段
  111. const additional = this.options.additional;
  112. if (additional && (additional.timestamps !== false) && (this.isTimestampField(field) || this.isParanoidField(field))) {
  113. return '';
  114. }
  115. // 判断是否为配置的忽略字段
  116. if (this.isIgnoredField(field)) {
  117. return '';
  118. }
  119. // 发现外键
  120. const foreignKey = this.foreignKeys[table] && this.foreignKeys[table][field] ? this.foreignKeys[table][field] : null;
  121. const fieldObj = this.tables[table][field];
  122. if (lodash_1.default.isObject(foreignKey)) {
  123. fieldObj.foreignKey = foreignKey;
  124. }
  125. const fieldName = (0, types_1.recase)(this.options.caseProp, field);
  126. let str = "@Column({\n";
  127. const quoteWrapper = '"';
  128. const unique = fieldObj.unique || fieldObj.foreignKey && fieldObj.foreignKey.isUnique;
  129. const isSerialKey = (fieldObj.foreignKey && fieldObj.foreignKey.isSerialKey) ||
  130. this.dialect.isSerialKey && this.dialect.isSerialKey(fieldObj);
  131. let wroteAutoIncrement = false;
  132. let wroteTimestamp = false;
  133. const space = this.space;
  134. // 列的属性
  135. let typeStr;
  136. const fieldAttrs = lodash_1.default.keys(fieldObj);
  137. fieldAttrs.forEach(attr => {
  138. // 我们不需要来自postgresql的特殊属性; "unique"被单独处理
  139. if (attr === "special" || attr === "elementType" || attr === "unique") {
  140. return true;
  141. }
  142. if (isSerialKey && !wroteAutoIncrement) {
  143. str += space[3] + "autoIncrement: true,\n";
  144. // 默认使用Postgres的GENERATED作为IDENTITY,而不是SERIAL
  145. if (this.dialect.name === "postgres" && fieldObj.foreignKey && fieldObj.foreignKey.isPrimaryKey === true &&
  146. (fieldObj.foreignKey.generation === "ALWAYS" || fieldObj.foreignKey.generation === "BY DEFAULT")) {
  147. str += space[3] + "autoIncrementIdentity: true,\n";
  148. }
  149. wroteAutoIncrement = true;
  150. }
  151. if (attr === "foreignKey") { // 外键属性
  152. if (foreignKey && foreignKey.isForeignKey) {
  153. str += space[3] + "references: {\n";
  154. str += space[4] + "model: \'" + fieldObj[attr].foreignSources.target_table + "\',\n";
  155. str += space[4] + "key: \'" + fieldObj[attr].foreignSources.target_column + "\'\n";
  156. str += space[3] + "}";
  157. }
  158. else {
  159. return true;
  160. }
  161. }
  162. else if (attr === "references") {
  163. // covered by foreignKey
  164. return true;
  165. }
  166. else if (attr === "primaryKey") { // 主键属性
  167. if (fieldObj[attr] === true && (!lodash_1.default.has(fieldObj, 'foreignKey') || !!fieldObj.foreignKey.isPrimaryKey)) {
  168. str += space[3] + "primaryKey: true";
  169. }
  170. else {
  171. return true;
  172. }
  173. }
  174. else if (attr === "autoIncrement") { // 自动增长属性
  175. if (fieldObj[attr] === true && !wroteAutoIncrement) {
  176. str += space[3] + "autoIncrement: true,\n";
  177. // Resort to Postgres' GENERATED BY DEFAULT AS IDENTITY instead of SERIAL
  178. if (this.dialect.name === "postgres" && fieldObj.foreignKey && fieldObj.foreignKey.isPrimaryKey === true && (fieldObj.foreignKey.generation === "ALWAYS" || fieldObj.foreignKey.generation === "BY DEFAULT")) {
  179. str += space[3] + "autoIncrementIdentity: true,\n";
  180. }
  181. wroteAutoIncrement = true;
  182. }
  183. return true;
  184. }
  185. else if (attr === "allowNull") { // 允许为null
  186. str += space[3] + attr + ": " + fieldObj[attr];
  187. }
  188. else if (attr === "defaultValue") { // 有默认值
  189. let defaultVal = fieldObj.defaultValue;
  190. if (this.dialect.name === "mssql" && defaultVal && defaultVal.toLowerCase() === '(newid())') {
  191. defaultVal = null; // disable adding "default value" attribute for UUID fields if generating for MS SQL
  192. }
  193. if (this.dialect.name === "mssql" && (["(NULL)", "NULL"].includes(defaultVal) || typeof defaultVal === "undefined")) {
  194. defaultVal = null; // Override default NULL in MS SQL to javascript null
  195. }
  196. if (defaultVal === null || defaultVal === undefined) {
  197. return true;
  198. }
  199. if (isSerialKey) {
  200. return true; // value generated in the database
  201. }
  202. let val_text = defaultVal;
  203. if (lodash_1.default.isString(defaultVal)) {
  204. const field_type = fieldObj.type.toLowerCase();
  205. defaultVal = this.escapeSpecial(defaultVal);
  206. while (defaultVal.startsWith('(') && defaultVal.endsWith(')')) {
  207. // remove extra parens around mssql defaults
  208. defaultVal = defaultVal.replace(/^[(]/, '').replace(/[)]$/, '');
  209. }
  210. if (field_type === 'bit(1)' || field_type === 'bit' || field_type === 'boolean') {
  211. // convert string to boolean
  212. val_text = /1|true/i.test(defaultVal) ? "true" : "false";
  213. }
  214. else if (this.isArray(field_type)) {
  215. // remove outer {}
  216. val_text = defaultVal.replace(/^{/, '').replace(/}$/, '');
  217. if (val_text && this.isString(fieldObj.elementType)) {
  218. // quote the array elements
  219. val_text = val_text.split(',').map(s => `"${s}"`).join(',');
  220. }
  221. val_text = `[${val_text}]`;
  222. }
  223. else if (field_type.match(/^(json)/)) {
  224. // don't quote json
  225. val_text = defaultVal;
  226. }
  227. else if (field_type === 'uuid' && (defaultVal === 'gen_random_uuid()' || defaultVal === 'uuid_generate_v4()')) {
  228. val_text = "DataType.UUIDV4";
  229. }
  230. else if (defaultVal.match(/\w+\(\)$/)) {
  231. // replace db function with sequelize function
  232. val_text = "Sequelize.fn('" + defaultVal.replace(/\(\)$/g, "") + "')";
  233. }
  234. else if (this.isNumber(field_type)) {
  235. if (defaultVal.match(/\(\)/g)) {
  236. // assume it's a server function if it contains parens
  237. val_text = "Sequelize.literal('" + defaultVal + "')";
  238. }
  239. else {
  240. // don't quote numbers
  241. val_text = defaultVal;
  242. }
  243. }
  244. else if (defaultVal.match(/\(\)/g)) {
  245. // embedded function, pass as literal
  246. val_text = "Sequelize.literal('" + defaultVal + "')";
  247. }
  248. else if (field_type.indexOf('date') === 0 || field_type.indexOf('timestamp') === 0) {
  249. if (lodash_1.default.includes(['current_timestamp', 'current_date', 'current_time', 'localtime', 'localtimestamp'], defaultVal.toLowerCase())) {
  250. val_text = "Sequelize.literal('" + defaultVal + "')";
  251. wroteTimestamp = true;
  252. }
  253. else {
  254. val_text = quoteWrapper + defaultVal + quoteWrapper;
  255. }
  256. }
  257. else {
  258. val_text = quoteWrapper + defaultVal + quoteWrapper;
  259. }
  260. }
  261. str += space[3] + attr + ": " + val_text;
  262. }
  263. else if (attr === "comment" && (!fieldObj[attr] || this.dialect.name === "mssql")) { // 有备注
  264. return true;
  265. }
  266. else {
  267. let val = null;
  268. if (attr === "type") {
  269. val = this.getSqType(fieldObj, attr);
  270. typeStr = this.getFieldType(fieldObj, attr);
  271. }
  272. if (val == null) {
  273. val = fieldObj[attr];
  274. val = lodash_1.default.isString(val) ? quoteWrapper + this.escapeSpecial(val) + quoteWrapper : val;
  275. }
  276. str += space[3] + attr + ": " + val;
  277. }
  278. str += ",\n";
  279. });
  280. if (unique) {
  281. const uniq = lodash_1.default.isString(unique) ? quoteWrapper + unique.replace(/\"/g, '\\"') + quoteWrapper : unique;
  282. str += space[3] + "unique: " + uniq + ",\n";
  283. }
  284. if (field !== fieldName) {
  285. str += space[3] + "field: '" + field + "',\n";
  286. }
  287. // 删除属性选项中的最后一个“,”
  288. str = str.trim().replace(/,+$/, '') + "\n";
  289. str = space[2] + str + space[2] + "})\n";
  290. str += space[2] + this.quoteName(fieldName) + `: ${typeStr};\n\n`;
  291. this.attrbuitesstr += this.addAttributesMain(this.quoteName(fieldName),typeStr,space[2],wroteAutoIncrement,wroteTimestamp);
  292. return str;
  293. }
  294. /**
  295. * 添加属性
  296. * @param {*} table
  297. * @param {*} field
  298. */
  299. addAttributesHeader(entityName,mainStr) {
  300. let str = `\nexport interface ${entityName}Attributes {\n`;
  301. str += (mainStr + "}\n");
  302. return str;
  303. }
  304. /**
  305. * 生成字段属性
  306. * @param {*} entityName
  307. * @param {*} fileName
  308. * @param {*} typeStr
  309. * @param {*} indexspace
  310. */
  311. addAttributesMain(fileName,typeStr,indexspace,iswroteAutoIncrement = false,wroteTimestamp = false) {
  312. let str = indexspace + fileName +"?:" + typeStr +"\n";
  313. return str;
  314. }
  315. // 添加索引
  316. addIndexes(table) {
  317. const indexes = this.indexes[table];
  318. const space = this.space;
  319. let str = "";
  320. if (indexes && indexes.length) {
  321. str += space[2] + "indexes: [\n";
  322. indexes.forEach(idx => {
  323. str += space[3] + "{\n";
  324. if (idx.name) {
  325. str += space[4] + `name: "${idx.name}",\n`;
  326. }
  327. if (idx.unique) {
  328. str += space[4] + "unique: true,\n";
  329. }
  330. if (idx.type) {
  331. if (['UNIQUE', 'FULLTEXT', 'SPATIAL'].includes(idx.type)) {
  332. str += space[4] + `type: "${idx.type}",\n`;
  333. }
  334. else {
  335. str += space[4] + `using: "${idx.type}",\n`;
  336. }
  337. }
  338. str += space[4] + `fields: [\n`;
  339. idx.fields.forEach(ff => {
  340. str += space[5] + `{ name: "${ff.attribute}"`;
  341. if (ff.collate) {
  342. str += `, collate: "${ff.collate}"`;
  343. }
  344. if (ff.length) {
  345. str += `, length: ${ff.length}`;
  346. }
  347. if (ff.order && ff.order !== "ASC") {
  348. str += `, order: "${ff.order}"`;
  349. }
  350. str += " },\n";
  351. });
  352. str += space[4] + "]\n";
  353. str += space[3] + "},\n";
  354. });
  355. str += space[2] + "],\n";
  356. }
  357. return str;
  358. }
  359. getFieldType(fieldObj, attr) {
  360. let typeStr = 'number';
  361. const attrValue = fieldObj[attr];
  362. if (!attrValue.toLowerCase) {
  363. console.log("attrValue", attr, attrValue);
  364. return attrValue;
  365. }
  366. const type = attrValue.toLowerCase();
  367. let typematch = null;
  368. if (type === "boolean" || type === "bit(1)" || type === "bit" || type === "tinyint(1)") {
  369. typeStr = 'boolean';
  370. }
  371. else if (typematch = type.match(/^(bigint|smallint|mediumint|tinyint|int)/)) {
  372. typeStr = 'number';
  373. }
  374. else if (type === 'nvarchar(max)' || type === 'varchar(max)') {
  375. typeStr = 'string';
  376. }
  377. else if (type.match(/n?varchar|string|varying/)) {
  378. typeStr = 'string';
  379. }
  380. else if (type.match(/^n?char/)) {
  381. typeStr = 'string';
  382. }
  383. else if (type.match(/text$/)) {
  384. typeStr = 'string';
  385. }
  386. else if (type === "date") {
  387. typeStr = 'string';
  388. }
  389. else if (type.match(/^(date|timestamp|year)/)) {
  390. typeStr = 'string';
  391. }
  392. else if (type.match(/^(time)/)) {
  393. typeStr = 'string';
  394. }
  395. else if (type.match(/^(float|float4)/)) {
  396. typeStr = 'number';
  397. }
  398. else if (type.match(/^(decimal|numeric)/)) {
  399. typeStr = 'number';
  400. }
  401. else if (type.match(/^money/)) {
  402. typeStr = 'number';
  403. }
  404. else if (type.match(/^smallmoney/)) {
  405. typeStr = 'number';
  406. }
  407. else if (type.match(/^(float8|double)/)) {
  408. typeStr = 'number';
  409. }
  410. else if (type.match(/^uuid|uniqueidentifier/)) {
  411. typeStr = 'string';
  412. }
  413. else if (type.match(/^jsonb/)) {
  414. typeStr = 'string';
  415. }
  416. else if (type.match(/^json/)) {
  417. typeStr = 'string';
  418. }
  419. return typeStr;
  420. }
  421. // 从字段中获取sequelize类型
  422. getSqType(fieldObj, attr) {
  423. let typeStr = '';
  424. const attrValue = fieldObj[attr];
  425. if (!attrValue.toLowerCase) {
  426. console.log("attrValue", attr, attrValue);
  427. return attrValue;
  428. }
  429. const type = attrValue.toLowerCase();
  430. const length = type.match(/\(\d+\)/);
  431. const precision = type.match(/\(\d+,\d+\)/);
  432. let val = null;
  433. let typematch = null;
  434. if (type === "boolean" || type === "bit(1)" || type === "bit" || type === "tinyint(1)") {
  435. val = 'DataType.BOOLEAN';
  436. // postgres range types
  437. }
  438. else if (type === "numrange") {
  439. val = 'DataType.RANGE(DataType.DECIMAL)';
  440. }
  441. else if (type === "int4range") {
  442. val = 'DataType.RANGE(DataType.INTEGER)';
  443. }
  444. else if (type === "int8range") {
  445. val = 'DataType.RANGE(DataType.BIGINT)';
  446. }
  447. else if (type === "daterange") {
  448. val = 'DataType.RANGE(DataType.DATEONLY)';
  449. }
  450. else if (type === "tsrange" || type === "tstzrange") {
  451. val = 'DataType.RANGE(DataType.DATE)';
  452. }
  453. else if (typematch = type.match(/^(bigint|smallint|mediumint|tinyint|int)/)) {
  454. // integer subtypes
  455. val = 'DataType.' + (typematch[0] === 'int' ? 'INTEGER' : typematch[0].toUpperCase());
  456. if (/unsigned/i.test(type)) {
  457. val += '.UNSIGNED';
  458. }
  459. if (/zerofill/i.test(type)) {
  460. val += '.ZEROFILL';
  461. }
  462. }
  463. else if (type === 'nvarchar(max)' || type === 'varchar(max)') {
  464. val = 'DataType.TEXT';
  465. }
  466. else if (type.match(/n?varchar|string|varying/)) {
  467. val = 'DataType.STRING' + (!lodash_1.default.isNull(length) ? length : '');
  468. }
  469. else if (type.match(/^n?char/)) {
  470. val = 'DataType.CHAR' + (!lodash_1.default.isNull(length) ? length : '');
  471. }
  472. else if (type.match(/^real/)) {
  473. val = 'DataType.REAL';
  474. }
  475. else if (type.match(/text$/)) {
  476. val = 'DataType.TEXT' + (!lodash_1.default.isNull(length) ? length : '');
  477. }
  478. else if (type === "date") {
  479. val = 'DataType.DATEONLY';
  480. }
  481. else if (type.match(/^(date|timestamp|year)/)) {
  482. val = 'DataType.DATE' + (!lodash_1.default.isNull(length) ? length : '');
  483. }
  484. else if (type.match(/^(time)/)) {
  485. val = 'DataType.TIME';
  486. }
  487. else if (type.match(/^(float|float4)/)) {
  488. val = 'DataType.FLOAT' + (!lodash_1.default.isNull(precision) ? precision : '');
  489. }
  490. else if (type.match(/^(decimal|numeric)/)) {
  491. val = 'DataType.DECIMAL' + (!lodash_1.default.isNull(precision) ? precision : '');
  492. }
  493. else if (type.match(/^money/)) {
  494. val = 'DataType.DECIMAL(19,4)';
  495. }
  496. else if (type.match(/^smallmoney/)) {
  497. val = 'DataType.DECIMAL(10,4)';
  498. }
  499. else if (type.match(/^(float8|double)/)) {
  500. val = 'DataType.DOUBLE' + (!lodash_1.default.isNull(precision) ? precision : '');
  501. }
  502. else if (type.match(/^uuid|uniqueidentifier/)) {
  503. val = 'DataType.UUID';
  504. }
  505. else if (type.match(/^jsonb/)) {
  506. val = 'DataType.JSONB';
  507. }
  508. else if (type.match(/^json/)) {
  509. val = 'DataType.JSON';
  510. }
  511. else if (type.match(/^geometry/)) {
  512. const gtype = fieldObj.elementType ? `(${fieldObj.elementType})` : '';
  513. val = `DataType.GEOMETRY${gtype}`;
  514. }
  515. else if (type.match(/^geography/)) {
  516. const gtype = fieldObj.elementType ? `(${fieldObj.elementType})` : '';
  517. val = `DataType.GEOGRAPHY${gtype}`;
  518. }
  519. else if (type.match(/^array/)) {
  520. const eltype = this.getSqType(fieldObj, "elementType");
  521. val = `DataType.ARRAY(${eltype})`;
  522. }
  523. else if (type.match(/(binary|image|blob|bytea)/)) {
  524. val = 'DataType.BLOB';
  525. }
  526. else if (type.match(/^hstore/)) {
  527. val = 'DataType.HSTORE';
  528. }
  529. else if (type.match(/^inet/)) {
  530. val = 'DataType.INET';
  531. }
  532. else if (type.match(/^cidr/)) {
  533. val = 'DataType.CIDR';
  534. }
  535. else if (type.match(/^oid/)) {
  536. val = 'DataType.INTEGER';
  537. }
  538. else if (type.match(/^macaddr/)) {
  539. val = 'DataType.MACADDR';
  540. }
  541. else if (type.match(/^enum(\(.*\))?$/)) {
  542. const enumValues = this.getEnumValues(fieldObj);
  543. val = `DataType.ENUM(${enumValues})`;
  544. }
  545. return val;
  546. }
  547. getTypeScriptPrimaryKeys(table) {
  548. const fields = lodash_1.default.keys(this.tables[table]);
  549. return fields.filter((field) => {
  550. const fieldObj = this.tables[table][field];
  551. return fieldObj['primaryKey'];
  552. });
  553. }
  554. getTypeScriptCreationOptionalFields(table) {
  555. const fields = lodash_1.default.keys(this.tables[table]);
  556. return fields.filter((field) => {
  557. const fieldObj = this.tables[table][field];
  558. return fieldObj.allowNull || (!!fieldObj.defaultValue || fieldObj.defaultValue === "") || fieldObj.autoIncrement
  559. || this.isTimestampField(field);
  560. });
  561. }
  562. /** Add schema to table so it will match the relation data. Fixes mysql problem. */
  563. addSchemaForRelations(table) {
  564. if (!table.includes('.') && !this.relations.some(rel => rel.childTable === table)) {
  565. // if no tables match the given table, then assume we need to fix the schema
  566. const first = this.relations.find(rel => !!rel.childTable);
  567. if (first) {
  568. const [schemaName, tableName] = (0, types_1.qNameSplit)(first.childTable);
  569. if (schemaName) {
  570. table = (0, types_1.qNameJoin)(schemaName, table);
  571. }
  572. }
  573. }
  574. return table;
  575. }
  576. addTypeScriptAssociationMixins(table) {
  577. const sp = this.space[1];
  578. const needed = {};
  579. let str = '';
  580. table = this.addSchemaForRelations(table);
  581. this.relations.forEach(rel => {
  582. var _a, _b, _c;
  583. var _d, _e;
  584. if (!rel.isM2M) {
  585. if (rel.childTable === table) {
  586. // current table is a child that belongsTo parent
  587. const pparent = lodash_1.default.upperFirst(rel.parentProp);
  588. str += `${sp}// ${rel.childModel} belongsTo ${rel.parentModel} via ${rel.parentId}\n`;
  589. str += `${sp}${rel.parentProp}!: ${rel.parentModel};\n`;
  590. str += `${sp}get${pparent}!: Sequelize.BelongsToGetAssociationMixin<${rel.parentModel}>;\n`;
  591. str += `${sp}set${pparent}!: Sequelize.BelongsToSetAssociationMixin<${rel.parentModel}, ${rel.parentModel}Id>;\n`;
  592. str += `${sp}create${pparent}!: Sequelize.BelongsToCreateAssociationMixin<${rel.parentModel}>;\n`;
  593. (_a = needed[_d = rel.parentTable]) !== null && _a !== void 0 ? _a : (needed[_d] = new Set());
  594. needed[rel.parentTable].add(rel.parentModel);
  595. needed[rel.parentTable].add(rel.parentModel + 'Id');
  596. }
  597. else if (rel.parentTable === table) {
  598. (_b = needed[_e = rel.childTable]) !== null && _b !== void 0 ? _b : (needed[_e] = new Set());
  599. const pchild = lodash_1.default.upperFirst(rel.childProp);
  600. if (rel.isOne) {
  601. // const hasModelSingular = singularize(hasModel);
  602. str += `${sp}// ${rel.parentModel} hasOne ${rel.childModel} via ${rel.parentId}\n`;
  603. str += `${sp}${rel.childProp}!: ${rel.childModel};\n`;
  604. str += `${sp}get${pchild}!: Sequelize.HasOneGetAssociationMixin<${rel.childModel}>;\n`;
  605. str += `${sp}set${pchild}!: Sequelize.HasOneSetAssociationMixin<${rel.childModel}, ${rel.childModel}Id>;\n`;
  606. str += `${sp}create${pchild}!: Sequelize.HasOneCreateAssociationMixin<${rel.childModel}>;\n`;
  607. needed[rel.childTable].add(rel.childModel);
  608. needed[rel.childTable].add(`${rel.childModel}Id`);
  609. needed[rel.childTable].add(`${rel.childModel}CreationAttributes`);
  610. }
  611. else {
  612. const hasModel = rel.childModel;
  613. const sing = lodash_1.default.upperFirst((0, types_1.singularize)(rel.childProp));
  614. const lur = (0, types_1.pluralize)(rel.childProp);
  615. const plur = lodash_1.default.upperFirst(lur);
  616. str += `${sp}// ${rel.parentModel} hasMany ${rel.childModel} via ${rel.parentId}\n`;
  617. str += `${sp}${lur}!: ${rel.childModel}[];\n`;
  618. str += `${sp}get${plur}!: Sequelize.HasManyGetAssociationsMixin<${hasModel}>;\n`;
  619. str += `${sp}set${plur}!: Sequelize.HasManySetAssociationsMixin<${hasModel}, ${hasModel}Id>;\n`;
  620. str += `${sp}add${sing}!: Sequelize.HasManyAddAssociationMixin<${hasModel}, ${hasModel}Id>;\n`;
  621. str += `${sp}add${plur}!: Sequelize.HasManyAddAssociationsMixin<${hasModel}, ${hasModel}Id>;\n`;
  622. str += `${sp}create${sing}!: Sequelize.HasManyCreateAssociationMixin<${hasModel}>;\n`;
  623. str += `${sp}remove${sing}!: Sequelize.HasManyRemoveAssociationMixin<${hasModel}, ${hasModel}Id>;\n`;
  624. str += `${sp}remove${plur}!: Sequelize.HasManyRemoveAssociationsMixin<${hasModel}, ${hasModel}Id>;\n`;
  625. str += `${sp}has${sing}!: Sequelize.HasManyHasAssociationMixin<${hasModel}, ${hasModel}Id>;\n`;
  626. str += `${sp}has${plur}!: Sequelize.HasManyHasAssociationsMixin<${hasModel}, ${hasModel}Id>;\n`;
  627. str += `${sp}count${plur}!: Sequelize.HasManyCountAssociationsMixin;\n`;
  628. needed[rel.childTable].add(hasModel);
  629. needed[rel.childTable].add(`${hasModel}Id`);
  630. }
  631. }
  632. }
  633. else {
  634. // rel.isM2M
  635. if (rel.parentTable === table) {
  636. // many-to-many
  637. const isParent = (rel.parentTable === table);
  638. const thisModel = isParent ? rel.parentModel : rel.childModel;
  639. const otherModel = isParent ? rel.childModel : rel.parentModel;
  640. const otherModelSingular = lodash_1.default.upperFirst((0, types_1.singularize)(isParent ? rel.childProp : rel.parentProp));
  641. const lotherModelPlural = (0, types_1.pluralize)(isParent ? rel.childProp : rel.parentProp);
  642. const otherModelPlural = lodash_1.default.upperFirst(lotherModelPlural);
  643. const otherTable = isParent ? rel.childTable : rel.parentTable;
  644. str += `${sp}// ${thisModel} belongsToMany ${otherModel} via ${rel.parentId} and ${rel.childId}\n`;
  645. str += `${sp}${lotherModelPlural}!: ${otherModel}[];\n`;
  646. str += `${sp}get${otherModelPlural}!: Sequelize.BelongsToManyGetAssociationsMixin<${otherModel}>;\n`;
  647. str += `${sp}set${otherModelPlural}!: Sequelize.BelongsToManySetAssociationsMixin<${otherModel}, ${otherModel}Id>;\n`;
  648. str += `${sp}add${otherModelSingular}!: Sequelize.BelongsToManyAddAssociationMixin<${otherModel}, ${otherModel}Id>;\n`;
  649. str += `${sp}add${otherModelPlural}!: Sequelize.BelongsToManyAddAssociationsMixin<${otherModel}, ${otherModel}Id>;\n`;
  650. str += `${sp}create${otherModelSingular}!: Sequelize.BelongsToManyCreateAssociationMixin<${otherModel}>;\n`;
  651. str += `${sp}remove${otherModelSingular}!: Sequelize.BelongsToManyRemoveAssociationMixin<${otherModel}, ${otherModel}Id>;\n`;
  652. str += `${sp}remove${otherModelPlural}!: Sequelize.BelongsToManyRemoveAssociationsMixin<${otherModel}, ${otherModel}Id>;\n`;
  653. str += `${sp}has${otherModelSingular}!: Sequelize.BelongsToManyHasAssociationMixin<${otherModel}, ${otherModel}Id>;\n`;
  654. str += `${sp}has${otherModelPlural}!: Sequelize.BelongsToManyHasAssociationsMixin<${otherModel}, ${otherModel}Id>;\n`;
  655. str += `${sp}count${otherModelPlural}!: Sequelize.BelongsToManyCountAssociationsMixin;\n`;
  656. (_c = needed[otherTable]) !== null && _c !== void 0 ? _c : (needed[otherTable] = new Set());
  657. needed[otherTable].add(otherModel);
  658. needed[otherTable].add(`${otherModel}Id`);
  659. }
  660. }
  661. });
  662. if (needed[table]) {
  663. delete needed[table]; // don't add import for self
  664. }
  665. return { needed, str };
  666. }
  667. addTypeScriptFields(table, isInterface) {
  668. const sp = this.space[1];
  669. const fields = lodash_1.default.keys(this.tables[table]);
  670. const notNull = isInterface ? '' : '!';
  671. let str = '';
  672. fields.forEach(field => {
  673. if (!this.options.skipFields || !this.options.skipFields.includes(field)) {
  674. const name = this.quoteName((0, types_1.recase)(this.options.caseProp, field));
  675. const isOptional = this.getTypeScriptFieldOptional(table, field);
  676. str += `${sp}${name}${isOptional ? '?' : notNull}: ${this.getTypeScriptType(table, field)};\n`;
  677. }
  678. });
  679. return str;
  680. }
  681. getTypeScriptFieldOptional(table, field) {
  682. const fieldObj = this.tables[table][field];
  683. return fieldObj.allowNull;
  684. }
  685. getTypeScriptType(table, field) {
  686. const fieldObj = this.tables[table][field];
  687. return this.getTypeScriptFieldType(fieldObj, "type");
  688. }
  689. getTypeScriptFieldType(fieldObj, attr) {
  690. const rawFieldType = fieldObj[attr] || '';
  691. const fieldType = String(rawFieldType).toLowerCase();
  692. let jsType;
  693. if (this.isArray(fieldType)) {
  694. const eltype = this.getTypeScriptFieldType(fieldObj, "elementType");
  695. jsType = eltype + '[]';
  696. }
  697. else if (this.isNumber(fieldType)) {
  698. jsType = 'number';
  699. }
  700. else if (this.isBoolean(fieldType)) {
  701. jsType = 'boolean';
  702. }
  703. else if (this.isDate(fieldType)) {
  704. jsType = 'Date';
  705. }
  706. else if (this.isString(fieldType)) {
  707. jsType = 'string';
  708. }
  709. else if (this.isEnum(fieldType)) {
  710. const values = this.getEnumValues(fieldObj);
  711. jsType = values.join(' | ');
  712. }
  713. else if (this.isJSON(fieldType)) {
  714. jsType = 'object';
  715. }
  716. else {
  717. console.log(`Missing TypeScript type: ${fieldType || fieldObj['type']}`);
  718. jsType = 'any';
  719. }
  720. return jsType;
  721. }
  722. getEnumValues(fieldObj) {
  723. if (fieldObj.special) {
  724. // postgres
  725. return fieldObj.special.map((v) => `"${v}"`);
  726. }
  727. else {
  728. // mysql
  729. return fieldObj.type.substring(5, fieldObj.type.length - 1).split(',');
  730. }
  731. }
  732. isTimestampField(field) {
  733. const additional = this.options.additional;
  734. if (additional.timestamps === false) {
  735. return false;
  736. }
  737. return ((!additional.createdAt && (0, types_1.recase)('c', field) === 'createdAt') || additional.createdAt === field)
  738. || ((!additional.updatedAt && (0, types_1.recase)('c', field) === 'updatedAt') || additional.updatedAt === field);
  739. }
  740. isParanoidField(field) {
  741. const additional = this.options.additional;
  742. if (additional.timestamps === false || additional.paranoid === false) {
  743. return false;
  744. }
  745. return ((!additional.deletedAt && (0, types_1.recase)('c', field) === 'deletedAt') || additional.deletedAt === field);
  746. }
  747. isIgnoredField(field) {
  748. return (this.options.skipFields && this.options.skipFields.includes(field));
  749. }
  750. escapeSpecial(val) {
  751. if (typeof (val) !== "string") {
  752. return val;
  753. }
  754. return val
  755. .replace(/[\\]/g, '\\\\')
  756. .replace(/[\"]/g, '\\"')
  757. .replace(/[\/]/g, '\\/')
  758. .replace(/[\b]/g, '\\b')
  759. .replace(/[\f]/g, '\\f')
  760. .replace(/[\n]/g, '\\n')
  761. .replace(/[\r]/g, '\\r')
  762. .replace(/[\t]/g, '\\t');
  763. }
  764. /** Quote the name if it is not a valid identifier */
  765. quoteName(name) {
  766. return (/^[$A-Z_][0-9A-Z_$]*$/i.test(name) ? name : "'" + name + "'");
  767. }
  768. isNumber(fieldType) {
  769. return /^(smallint|mediumint|tinyint|int|bigint|float|money|smallmoney|double|decimal|numeric|real|oid)/.test(fieldType);
  770. }
  771. isBoolean(fieldType) {
  772. return /^(boolean|bit)/.test(fieldType);
  773. }
  774. isDate(fieldType) {
  775. return /^(datetime|timestamp)/.test(fieldType);
  776. }
  777. isString(fieldType) {
  778. return /^(char|nchar|string|varying|varchar|nvarchar|text|longtext|mediumtext|tinytext|ntext|uuid|uniqueidentifier|date|time|inet|cidr|macaddr)/.test(fieldType);
  779. }
  780. isArray(fieldType) {
  781. return /(^array)|(range$)/.test(fieldType);
  782. }
  783. isEnum(fieldType) {
  784. return /^(enum)/.test(fieldType);
  785. }
  786. isJSON(fieldType) {
  787. return /^(json|jsonb)/.test(fieldType);
  788. }
  789. }
  790. exports.AutoGenerator = AutoGenerator;
  791. //# sourceMappingURL=auto-generator.js.map