1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00
joplin/packages/tools/generate-database-types.ts

136 lines
4.2 KiB
TypeScript

import { execCommand } from '@joplin/utils';
import { insertContentIntoFile, rootDir } from './tool-utils';
import { remove } from 'fs-extra';
const sqlts = require('@rmp135/sql-ts').default;
const fs = require('fs-extra');
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
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;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
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`);
const profileDir = `${__dirname}/__generateTypesProfile`;
await execCommand(['yarn', 'start', '--profile', profileDir, 'version']);
try {
const sqlTsConfig = {
'client': 'sqlite3',
'connection': {
'filename': `${profileDir}/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);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
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;
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
definitions.tables = definitions.tables.map((table: any) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
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.trim()}\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,
);
} finally {
await remove(profileDir);
}
}
main().catch((error) => {
console.error(error);
process.exit(1);
});