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 9 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
@@ -48,6 +48,7 @@
"ts-jest": "^29.0.3",
"ts-node": "^10.9.1",
"ts-node-dev": "^2.0.0",
"jiti": "^2.4.2",
"typedoc": "^0.23.17",
"typescript": "~5.7.3"
},
4 changes: 4 additions & 0 deletions packages/core/src/core.ts
Original file line number Diff line number Diff line change
@@ -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
@@ -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",
5 changes: 5 additions & 0 deletions packages/sql/src/cli/base-command.ts
Original file line number Diff line number Diff line change
@@ -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
@@ -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');
@@ -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;
1 change: 1 addition & 0 deletions packages/sql/src/cli/migration-down-command.ts
Original file line number Diff line number Diff line change
@@ -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);

1 change: 1 addition & 0 deletions packages/sql/src/cli/migration-pending-command.ts
Original file line number Diff line number Diff line change
@@ -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);

1 change: 1 addition & 0 deletions packages/sql/src/cli/migration-up-command.ts
Original file line number Diff line number Diff line change
@@ -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);

66 changes: 53 additions & 13 deletions packages/sql/src/migration/migration-provider.ts
Original file line number Diff line number Diff line change
@@ -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';
@@ -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;
@@ -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);
}
}
11 changes: 7 additions & 4 deletions website/src/pages/documentation/orm/migrations.md
Original file line number Diff line number Diff line change
@@ -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
26 changes: 26 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
@@ -14213,6 +14213,15 @@ __metadata:
languageName: node
linkType: hard

"get-tsconfig@npm:^4.7.5":
version: 4.8.1
resolution: "get-tsconfig@npm:4.8.1"
dependencies:
resolve-pkg-maps: "npm:^1.0.0"
checksum: 536ee85d202f604f4b5fb6be81bcd6e6d9a96846811e83e9acc6de4a04fb49506edea0e1b8cf1d5ee7af33e469916ec2809d4c5445ab8ae015a7a51fbd1572f9
languageName: node
linkType: hard

"getpass@npm:^0.1.1":
version: 0.1.7
resolution: "getpass@npm:0.1.7"
@@ -22307,6 +22316,7 @@ __metadata:
ts-jest: "npm:^29.0.3"
ts-node: "npm:^10.9.1"
ts-node-dev: "npm:^2.0.0"
tsx: "npm:^4.19.1"
typedoc: "npm:^0.23.17"
typescript: "npm:~5.7.3"
languageName: unknown
@@ -24391,6 +24401,22 @@ __metadata:
languageName: node
linkType: hard

"tsx@npm:^4.19.1":
version: 4.19.1
resolution: "tsx@npm:4.19.1"
dependencies:
esbuild: "npm:~0.23.0"
fsevents: "npm:~2.3.3"
get-tsconfig: "npm:^4.7.5"
dependenciesMeta:
fsevents:
optional: true
bin:
tsx: dist/cli.mjs
checksum: cbea9baf57e7406fa0ecc2c03b9bb2501ee740dc28c938f949180a646a28e5d65e7cccbfba340508923bfd45e90320ef9eef7f815cae4515b6ef2ee429edc7ee
languageName: node
linkType: hard

"ttf2woff@npm:^2.0.1":
version: 2.0.2
resolution: "ttf2woff@npm:2.0.2"
Loading