import { execCommand } from '@joplin/utils'; import { insertContentIntoFile, rootDir } from './tool-utils'; const sqlts = require('@rmp135/sql-ts').default; const fs = require('fs-extra'); function createRuntimeObject(table: any) { const colStrings = []; for (const col of table.columns) { const name = col.propertyName; const type = col.propertyType; colStrings.push(`\t\t${name}: { type: '${type}' },`); } return `\t${table.name}: {\n${colStrings.join('\n')}\n\t},`; } const stringToSingular = (word: string) => { if (word.endsWith('s')) return word.substring(0, word.length - 1); return word; }; const generateListRenderDependencyType = (tables: any[]) => { const output: string[] = []; for (const table of tables) { if (!['notes', 'folders'].includes(table.name)) continue; for (const col of table.columns) { const name = col.propertyName; output.push(`'${stringToSingular(table.name)}.${name}'`); } } return output.join(' | '); }; async function main() { // Run the CLI app once so as to generate the database file process.chdir(`${rootDir}/packages/app-cli`); await execCommand('yarn start version'); const sqlTsConfig = { 'client': 'sqlite3', 'connection': { 'filename': `${require('os').homedir()}/.config/joplindev/database.sqlite`, }, 'tableNameCasing': 'pascal', 'singularTableNames': true, 'useNullAsDefault': true, // To disable warning "sqlite does not support inserting default values" 'excludedTables': [ 'main.notes_fts', 'main.notes_fts_segments', 'main.notes_fts_segdir', 'main.notes_fts_docsize', 'main.notes_fts_stat', 'main.master_keys', ], }; const definitions = await sqlts.toObject(sqlTsConfig); definitions.tables = definitions.tables.map((t: any) => { t.columns.push({ nullable: false, name: 'type_', type: 'int', optional: true, isEnum: false, propertyName: 'type_', propertyType: 'number', }); return t; }); definitions.tables = definitions.tables.map((table: any) => { table.columns = table.columns.map((column: any) => { return { ...column, optional: true, }; }); return table; }); const tableStrings = []; for (const table of definitions.tables) { tableStrings.push(createRuntimeObject(table)); } const tsString = sqlts.fromObject(definitions, sqlTsConfig) .replace(/": /g, '"?: '); const header = `// AUTO-GENERATED BY ${__filename.substr(rootDir.length + 1)}`; const targetFile = `${rootDir}/packages/lib/services/database/types.ts`; console.info(`Writing type definitions to ${targetFile}...`); const existingContent = (await fs.pathExists(targetFile)) ? await fs.readFile(targetFile, 'utf8') : ''; const splitted = existingContent.split('// AUTO-GENERATED BY'); const staticContent = splitted[0]; const runtimeContent = `export const databaseSchema: DatabaseTables = {\n${tableStrings.join('\n')}\n};`; const listRendererDependency = `type ListRendererDatabaseDependency = ${generateListRenderDependencyType(definitions.tables)};`; const noteListTypeFilePath = `${rootDir}/packages/lib/services/plugins/api/noteListType.ts`; await fs.writeFile(targetFile, `${staticContent}\n\n${header}\n\n${tsString}\n\n${runtimeContent}`, 'utf8'); console.info(`Writing ListRendererDatabaseDependency type to ${noteListTypeFilePath}...`); await insertContentIntoFile( noteListTypeFilePath, '// AUTO-GENERATED by generate-database-type\n', '\n// AUTO-GENERATED by generate-database-type', listRendererDependency, ); } main().catch((error) => { console.error(error); process.exit(1); });