/* eslint-disable no-magic-numbers */ 'use strict'; const fs = require ('fs-extra'); /** * convert short type notation to knex notation * * @param {string} short short type * @returns {string} type */ function get_type (short) { switch (short) { case '\'\'': return 'string'; case '#': return 'integer'; case '#.#': return 'double'; case '✓': return 'boolean'; case '🖹': return 'text'; default: return ''; } } /** * returns columns and attributes for a table * * @param {string} str table definition * @returns {object} table info */ function get_table_info (str) { const lines = str.split (/\n/ug); lines.splice (0, 2); lines.splice (lines.length - 2, 2); const name_line = lines.shift (); const { name } = (/(?\S+)<\/b>/u).exec (name_line).groups; const columns = []; while (lines.length > 0) { const col = {}; const l = lines.shift (); const regex = /(?.*?)<\/td><\/tr>/u; const data = regex.exec (l).groups.props.split (/\s+/gu); if (data.length === 3 || data[0] === '🔑') { const opt = data.shift (); if (opt === '★') col.unique = true; if (opt === '🔑') col.type = 'increments'; } col.name = data.shift (); if (data.length > 0) col.type = get_type (data.shift ()); columns.push (col); } return { name, columns, foreign_keys: [] }; } /** * get all tables from a structure file * * @param {string} file path to the structure file * @returns {Promise} tables and foreign keys */ async function get_tables (file) { const lines = (await fs.readFile (file, 'utf-8')).split (/\n/gu); const curr = []; const tables = []; const foreign = []; for (const l of lines) { if (curr.length > 0 || (/\S+ \[label=.*?\]/u).test (l)) { const val = curr.join ('\n'); if (val) tables.push (get_table_info (val)); curr.splice (0, curr.length); } get_foreign_key (l, foreign); } for (const fk of foreign) { for (let i = 0; i < tables.length; i++) { if (tables[i].name === fk.table) { tables[i].foreign_keys.push (fk); break; } } } return tables; } /** * gets foreign keys from a line * * @param {string} line line to check * @param {Array} foreign_keys array to add to */ function get_foreign_key (line, foreign_keys) { const fk = (/(?\S+) -> (?\S+)/u).exec (line); if (fk) { const col = fk.groups.col.split (':'); const ref = fk.groups.ref.split (':'); const foreign_key = { table: col[0], column: col[1], ref_table: ref[0], ref_column: ref[1] }; foreign_keys.push (foreign_key); } } /** * creates a function for creating a table * * @param {object} table table to create a function for * @returns {string} function */ function create_table_function (table) { let func = `/** * create table ${table.name} * * @param {any} knex database connection * @returns {Promise} result */ function create_${table.name} (knex) { return knex.schema.createTable ('${table.name}', (table) => { ${table.columns .map ((col) => `table.${col.type} ('${col.name}');`) .join ('\n ')} `; const unique = table.columns.filter ((val) => val.unique); if (unique.length > 0) { func += `\n table.unique (${unique .map ((val) => `'${val.name}'`) .join (', ')});\n`; } if (table.foreign_keys.length > 0) { func += '\n'; for (const fk of table.foreign_keys) { func += ` table.foreign ('${fk.column}') .references ('${fk.ref_column}') .inTable ('${fk.ref_table}');\n`; } } func += ` }); }`; return func; } /** * creates the migration function * * @param {Array} tables table array * @returns {string} function */ function create_up_function (tables) { const func = `async function up (knex) { ${tables.map ((val) => ` await create_${val.name} (knex);`) .join ('\n')} }`; return func; } /** * creates the complete migration file for a graph * * @param {string} file_path file to scan * @returns {Promise} file */ async function create_migration (file_path) { const tables = await get_tables (file_path); const functions = tables.map ((tab) => create_table_function (tab)); const file = `${functions.join ('\n\n')} /* * run migration * * @param {any} knex db connection */ ${create_up_function (tables)} /* * revert migration * * @param {any} knex db connection */ function down () {} module.exports = { up, down } `; return file; } module.exports = { get_tables, create_table_function, create_migration };