Skip to content

feat(sql): add database when path is provided to migration commands #613

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"commitlint": "^18.4.3",
"coveralls": "^3.0.3",
"jest": "^29.2.1",
"jiti": "^2.4.2",
"lefthook": "^1.5.5",
"lerna": "^7.4.2",
"madge": "^4.0.0",
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -901,3 +901,7 @@ export function assertDefined<T>(value: T): asserts value is NonNullable<T> {
throw new Error(`Value is not defined`);
}
}

export function isEsm(): boolean {
return typeof require === 'undefined';
}
3 changes: 2 additions & 1 deletion packages/sql/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
"@deepkit/logger": "^1.0.1",
"@deepkit/orm": "^1.0.1",
"@deepkit/stopwatch": "^1.0.1",
"@deepkit/type": "^1.0.1"
"@deepkit/type": "^1.0.1",
"jiti": "^2.4.2"
},
"dependencies": {
"@types/sqlstring": "^2.2.1",
Expand Down
5 changes: 5 additions & 0 deletions packages/sql/src/cli/base-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,9 @@ export class BaseCommand {
* @description Sets the migration directory.
*/
protected migrationDir: string & Flag = '';

/**
* @description Sets the database path
*/
protected path?: string & Flag;
}
3 changes: 2 additions & 1 deletion packages/sql/src/cli/migration-create-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export class MigrationCreateController extends BaseCommand implements Command {
empty: boolean & Flag = false,
): Promise<void> {
if (this.migrationDir) this.provider.setMigrationDir(this.migrationDir);
if (this.path) await this.provider.addDatabase(this.path);

if (!this.provider.databases.getDatabases().length) {
this.logger.error('No databases detected. Use --path path/to/database.ts');
Expand Down Expand Up @@ -105,7 +106,7 @@ export class MigrationCreateController extends BaseCommand implements Command {
let migrationName = '';
const date = new Date;

const { format } = require('date-fns');
const { format } = await import('date-fns');
for (let i = 1; i < 100; i++) {
migrationName = format(date, 'yyyyMMdd-HHmm');
if (i > 1) migrationName += '_' + i;
Expand Down
1 change: 1 addition & 0 deletions packages/sql/src/cli/migration-down-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export class MigrationDownCommand extends BaseCommand {
fake: boolean & Flag = false,
): Promise<void> {
if (this.migrationDir) this.provider.setMigrationDir(this.migrationDir);
if (this.path) await this.provider.addDatabase(this.path);

const migrationsPerDatabase = await this.provider.getMigrationsPerDatabase(database);

Expand Down
1 change: 1 addition & 0 deletions packages/sql/src/cli/migration-pending-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export class MigrationPendingCommand extends BaseCommand {
database?: string & Flag<{ char: 'db' }>,
): Promise<void> {
if (this.migrationDir) this.provider.setMigrationDir(this.migrationDir);
if (this.path) await this.provider.addDatabase(this.path);

const migrationsPerDatabase = await this.provider.getMigrationsPerDatabase(database);

Expand Down
1 change: 1 addition & 0 deletions packages/sql/src/cli/migration-up-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export class MigrationUpCommand extends BaseCommand {
all: boolean & Flag = false,
): Promise<void> {
if (this.migrationDir) this.provider.setMigrationDir(this.migrationDir);
if (this.path) await this.provider.addDatabase(this.path);

const migrationsPerDatabase = await this.provider.getMigrationsPerDatabase(database);

Expand Down
66 changes: 53 additions & 13 deletions packages/sql/src/migration/migration-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
*
* You should have received a copy of the MIT License along with this program.
*/

import { ClassType } from '@deepkit/core';
import { ClassType, isEsm } from '@deepkit/core';
import { Database, DatabaseRegistry } from '@deepkit/orm';
import glob from 'fast-glob';
import { basename, join } from 'path';
Expand All @@ -18,10 +17,7 @@ export class MigrationProvider {
protected databaseMap = new Map<string, Database<any>>();
protected migrationDir: string = 'migrations/';

constructor(
public databases: DatabaseRegistry,
) {
}
constructor(public databases: DatabaseRegistry) {}

getMigrationDir(): string {
return this.migrationDir;
Expand Down Expand Up @@ -51,22 +47,66 @@ export class MigrationProvider {
return migrationsPerDatabase;
}

private async createJiti() {
const esm = isEsm();
const { createJiti } = await import('jiti');
return createJiti(
esm
? // @ts-expect-error esm only
import.meta.url
: __filename,
);
}

async addDatabase(path: string): Promise<void> {
const jiti = await this.createJiti();
const exports = Object.values((await jiti.import(join(process.cwd(), path))) || {});
if (!exports.length) {
throw new Error(`No database found in path ${path}`);
}

let databaseInstance: Database | undefined;
let foundDatabaseClass: ClassType<Database> | undefined;

for (const value of exports) {
if (value instanceof Database) {
databaseInstance = value;
break;
}
if (Object.getPrototypeOf(value) instanceof Database) {
foundDatabaseClass = value as ClassType<Database>;
}
}

if (!databaseInstance) {
if (foundDatabaseClass) {
throw new Error(
`Found database class ${foundDatabaseClass.name} in path ${path} but it has to be instantiated an exported. export const database = new ${foundDatabaseClass.name}(/* ... */);`,
);
}
throw new Error(`No database found in path ${path}`);
}

this.databases.addDatabaseInstance(databaseInstance);
}

async getMigrations(migrationDir: string): Promise<Migration[]> {
let migrations: Migration[] = [];
const jiti = await this.createJiti();

const files = await glob('**/*.ts', { cwd: migrationDir });
const files = await glob('**/!(*.d).+(ts|js)', { cwd: migrationDir });
let migrations: Migration[] = [];

for (const file of files) {
const path = join(process.cwd(), migrationDir, file);
const name = basename(file.replace('.ts', ''));
const migration = await import(path);
if (migration && migration.SchemaMigration) {
const jo = new class extends (migration.SchemaMigration as ClassType<Migration>) {
const name = basename(file.replace('.ts', '').replace('.js', ''));
const { SchemaMigration } = (await jiti.import<{ SchemaMigration?: ClassType<Migration> }>(path)) || {};
if (SchemaMigration) {
const jo = new (class extends (SchemaMigration as ClassType<Migration>) {
constructor() {
super();
if (!this.name) this.name = name;
}
};
})();
migrations.push(jo);
}
}
Expand Down
11 changes: 7 additions & 4 deletions website/src/pages/documentation/orm/migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ import { SQLiteDatabaseAdapter } from '@deepkit/sqlite';
import { User } from './models';

export class SQLiteDatabase extends Database {
name = 'default';
constructor() {
super(new SQLiteDatabaseAdapter('/tmp/myapp.sqlite'), [User]);
}
name = 'default';

constructor() {
super(new SQLiteDatabaseAdapter('/tmp/myapp.sqlite'), [User]);
}
}

export const database = new SQLiteDatabase();
```

```sh
Expand Down
11 changes: 11 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3774,6 +3774,7 @@ __metadata:
"@deepkit/orm": ^1.0.1
"@deepkit/stopwatch": ^1.0.1
"@deepkit/type": ^1.0.1
jiti: ^2.4.2
bin:
deepkit-sql: ./bin/deepkit-sql.js
languageName: unknown
Expand Down Expand Up @@ -16603,6 +16604,15 @@ __metadata:
languageName: node
linkType: hard

"jiti@npm:^2.4.2":
version: 2.4.2
resolution: "jiti@npm:2.4.2"
bin:
jiti: lib/jiti-cli.mjs
checksum: 4ceac133a08c8faff7eac84aabb917e85e8257f5ad659e843004ce76e981c457c390a220881748ac67ba1b940b9b729b30fb85cbaf6e7989f04b6002c94da331
languageName: node
linkType: hard

"jpeg-js@npm:^0.4.4":
version: 0.4.4
resolution: "jpeg-js@npm:0.4.4"
Expand Down Expand Up @@ -22300,6 +22310,7 @@ __metadata:
commitlint: "npm:^18.4.3"
coveralls: "npm:^3.0.3"
jest: "npm:^29.2.1"
jiti: "npm:^2.4.2"
lefthook: "npm:^1.5.5"
lerna: "npm:^7.4.2"
madge: "npm:^4.0.0"
Expand Down
Loading