Skip to content

Commit

Permalink
add import prop @ file migration provider.
Browse files Browse the repository at this point in the history
..

add FileMigrationProvider test suite.

..
  • Loading branch information
igalklebanov committed Nov 4, 2024
1 parent ccc2f0a commit ca167d5
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 26 deletions.
58 changes: 37 additions & 21 deletions src/migration/file-migration-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { isFunction, isObject } from '../util/object-utils.js'
import { Migration, MigrationProvider } from './migrator.js'

/**
* Reads all migrations from a folder in node.js.
* Reads all migrations from a folder.
*
* ### Examples
*
Expand All @@ -29,31 +29,45 @@ export class FileMigrationProvider implements MigrationProvider {
const files = await this.#props.fs.readdir(this.#props.migrationFolder)

for (const fileName of files) {
if (
fileName.endsWith('.js') ||
(fileName.endsWith('.ts') && !fileName.endsWith('.d.ts')) ||
fileName.endsWith('.mjs') ||
(fileName.endsWith('.mts') && !fileName.endsWith('.d.mts'))
) {
const migration = await import(
/* webpackIgnore: true */ this.#props.path.join(
this.#props.migrationFolder,
fileName,
)
)
const migrationKey = fileName.substring(0, fileName.lastIndexOf('.'))
if (!this.hasExpectedExtension(fileName)) {
this.#props.onDiscarded?.(fileName, 'Extension')
continue
}

const filePath = this.#props.path.join(
this.#props.migrationFolder,
fileName,
)

const migration = this.#props.import
? await this.#props.import(filePath)
: await import(/* webpackIgnore: true */ filePath)

const migrationKey = fileName.substring(0, fileName.lastIndexOf('.'))

// Handle esModuleInterop export's `default` prop...
if (isMigration(migration?.default)) {
migrations[migrationKey] = migration.default
} else if (isMigration(migration)) {
migrations[migrationKey] = migration
}
// Handle esModuleInterop export's `default` prop...
if (isMigration(migration?.default)) {
migrations[migrationKey] = migration.default
} else if (isMigration(migration)) {
migrations[migrationKey] = migration
} else {
this.#props.onDiscarded?.(fileName, 'NotMigration')
}
}

return migrations
}

protected hasExpectedExtension(fileName: string): boolean {
return (
fileName.endsWith('.js') ||
(fileName.endsWith('.ts') && !fileName.endsWith('.d.ts')) ||
fileName.endsWith('.mjs') ||
(fileName.endsWith('.mts') && !fileName.endsWith('.d.mts')) ||
fileName.endsWith('.cjs') ||
(fileName.endsWith('.cts') && !fileName.endsWith('.d.cts'))
)
}
}

function isMigration(obj: unknown): obj is Migration {
Expand All @@ -70,6 +84,8 @@ export interface FileMigrationProviderPath {

export interface FileMigrationProviderProps {
fs: FileMigrationProviderFS
path: FileMigrationProviderPath
import?(module: string): Promise<any>
migrationFolder: string
onDiscarded?(fileName: string, reason: 'Extension' | 'NotMigration'): void
path: FileMigrationProviderPath
}
89 changes: 89 additions & 0 deletions test/node/src/file-migration-provider.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { mkdir, readdir, rm, writeFile } from 'node:fs/promises'
import { join } from 'node:path'
import { require as tsxRequire } from 'tsx/cjs/api'
import { FileMigrationProvider } from '../../..'
import { expect } from './test-setup.js'

describe('FileMigrationProvider', () => {
;['js', 'ts', 'mjs', 'cjs', 'mts', 'cts'].forEach((extension) => {
describe(`${extension} files`, () => {
const migrationFolder = `${extension}-migrations`
const migrationFolderPath = join(__dirname, migrationFolder)
let provider: FileMigrationProvider
const migrationName = '123_noop'

before(async () => {
await mkdir(migrationFolderPath)
await writeFile(
join(migrationFolderPath, `${migrationName}.${extension}`),
extension.endsWith('js')
? 'exports.up = () => {}'
: 'export const up = () => {}',
)

provider = new FileMigrationProvider({
fs: { readdir },
import:
extension.endsWith('ts') || extension.startsWith('m')
? (module: string) => tsxRequire(module, __filename)
: undefined,
migrationFolder: migrationFolderPath,
path: { join },
})
})

after(async () => {
await rm(migrationFolderPath, { recursive: true })
})

it('should get migration files with this extension', async () => {
const migrations = await provider.getMigrations()

expect(migrations).to.have.property(migrationName)
})
})
})
//
;['zip', 'd.ts', 'd.mts', 'd.cts'].forEach((extension) => {
describe(`${extension} files`, () => {
const migrationFolder = `${extension}-migrations`
const migrationFolderPath = join(__dirname, migrationFolder)
let provider: FileMigrationProvider
const migrationFileName = `123_noop.${extension}`
let discarded: { fileName: string; reason: string }[]

before(async () => {
await mkdir(migrationFolderPath)
await writeFile(
join(migrationFolderPath, migrationFileName),
extension.endsWith('ts') ? 'export {}' : '==asdhjgbaudg1827dg127',
)
discarded = []

provider = new FileMigrationProvider({
fs: { readdir },
migrationFolder: migrationFolderPath,
onDiscarded: (fileName, reason) => {
discarded.push({ fileName, reason })
},
path: { join },
})
})

after(async () => {
await rm(migrationFolderPath, { recursive: true })
})

it('should discard files with this extension', async () => {
const migrations = await provider.getMigrations()

expect(migrations).to.deep.equal({})
expect(discarded).to.have.length(1)
expect(discarded[0]).to.deep.equal({
fileName: migrationFileName,
reason: 'Extension',
})
})
})
})
})
8 changes: 4 additions & 4 deletions test/node/src/test-setup.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import * as chai from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
import * as chaiSubset from 'chai-subset'
import * as Cursor from 'pg-cursor'
import chaiAsPromised from 'chai-as-promised'
import chaiSubset from 'chai-subset'
import Cursor from 'pg-cursor'
import { Pool, PoolConfig } from 'pg'
import { createPool } from 'mysql2'
import * as Database from 'better-sqlite3'
import Database from 'better-sqlite3'
import * as Tarn from 'tarn'
import * as Tedious from 'tedious'
import { PoolOptions } from 'mysql2'
Expand Down
10 changes: 9 additions & 1 deletion test/node/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
{"compilerOptions":{"target":"ES2022","lib":["ESNext"],"module":"CommonJS","outDir":"dist","skipLibCheck":true},"default":{"compilerOptions":{"target":"ES2022","lib":["ESNext"],"module":"CommonJS","outDir":"dist","skipLibCheck":true},"default":{"compilerOptions":{"target":"ES2022","lib":["ESNext"],"module":"CommonJS","outDir":"dist","skipLibCheck":true},"default":{"extends":"../../tsconfig-base.json","include":["src/**/*"],"compilerOptions":{"target":"ES2022","lib":["ESNext"],"module":"CommonJS","outDir":"dist","skipLibCheck":true}},"extends":"../../tsconfig-base.json","include":["src/**/*"],"exclude":["src/async-dispose.test.ts"]},"exclude":["src/async-dispose.test.ts"],"extends":"../../tsconfig-base.json","include":["src/**/*"]},"exclude":["src/async-dispose.test.ts","src/async-dispose.test.ts"],"extends":"../../tsconfig-base.json","include":["src/**/*"]}
{
"extends": "../../tsconfig-base.json",
"include": ["src/**/*"],
"compilerOptions": {
"module": "NodeNext",
"outDir": "dist",
"moduleResolution": "NodeNext"
}
}

0 comments on commit ca167d5

Please sign in to comment.