diff --git a/README.md b/README.md index f7d2e8e7..ef36d60c 100644 --- a/README.md +++ b/README.md @@ -757,6 +757,25 @@ const umzug = new Umzug({ }) ``` +You can also customize the format of the prefix added to migration file names: + +```js +const umzug = new Umzug({ + migrations: ..., + create: { + prefix: (prefix) => { + const base = new Date().toISOString().replace(/[-:]/g, ""); + const prefixes = { + TIMESTAMP: base.split(".")[0], + DATE: base.split("T")[0], + NONE: '', + }; + return prefixes[prefix]; + } + } +}) +``` + The create command includes some safety checks to make sure migrations aren't created with ambiguous ordering, and that they will be picked up by umzug when applying migrations. The first pair is expected to be the "up" migration file, and to be picked up by the `pending` command. Use `node migrator create --help` for more options: diff --git a/src/types.ts b/src/types.ts index e99fed33..b032ee2d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -11,6 +11,8 @@ export type Promisable = T | PromiseLike; export type LogFn = (message: Record) => void; +export type MigrationPrefix = 'TIMESTAMP' | 'DATE' | 'NONE'; + /** Constructor options for the Umzug class */ export type UmzugOptions> = { /** The migrations that the Umzug instance should perform */ @@ -34,6 +36,11 @@ export type UmzugOptions> = { * in the same folder as the last existing migration. The value here can be overriden by passing `folder` when calling `create`. */ folder?: string; + /** + * A function for generating custom prefixes for migration files when using `create`. If this is not specified the default date formats will + * be used ("1970.01.01T00.00.00" for TIMESTAMP, "1970.01.01" for DATE and "" for NONE) + */ + prefix?: (prefix: MigrationPrefix) => string; }; }; diff --git a/src/umzug.ts b/src/umzug.ts index c7921a18..4616683a 100644 --- a/src/umzug.ts +++ b/src/umzug.ts @@ -20,6 +20,7 @@ import type { RunnableMigration, UmzugEvents, UmzugOptions, + MigrationPrefix, } from './types'; import { RerunBehavior } from './types'; @@ -332,23 +333,28 @@ export class Umzug extends emittery { await this.runCommand('create', async ({ context }) => { - const isoDate = new Date().toISOString(); - const prefixes = { - TIMESTAMP: isoDate.replace(/\.\d{3}Z$/, '').replace(/\W/g, '.'), - DATE: isoDate.split('T')[0].replace(/\W/g, '.'), - NONE: '', - }; - const prefixType = options.prefix ?? 'TIMESTAMP'; - const fileBasename = [prefixes[prefixType], options.name].filter(Boolean).join('.'); + const getPrefix = this.options.create?.prefix ?? this.getDefaultPrefix; + const prefix = getPrefix(options.prefix ?? 'TIMESTAMP'); + const fileBasename = [prefix, options.name].filter(Boolean).join('.'); const allowedExtensions = options.allowExtension ? [options.allowExtension] diff --git a/test/umzug.test.ts b/test/umzug.test.ts index bcef3dfe..4bc7f313 100644 --- a/test/umzug.test.ts +++ b/test/umzug.test.ts @@ -17,6 +17,7 @@ jest.mock('../src/storage', () => { }); const names = (migrations: Array<{ name: string }>) => migrations.map(m => m.name); +const paths = (migrations: Array<{ path?: string }>) => migrations.map(m => m.path); describe('basic usage', () => { test('requires script files', async () => { @@ -114,7 +115,7 @@ describe('custom context', () => { expect(pending[0]).toContain('test.x.js'); }); - test(`create doesn't cause "confusing oredering" warning when migrations are nested in folders`, async () => { + test(`create doesn't cause "confusing ordering" warning when migrations are nested in folders`, async () => { const syncer = fsSyncer(path.join(__dirname, 'generated/create-nested-folders'), {}); syncer.sync(); @@ -141,6 +142,28 @@ describe('custom context', () => { expect(pending[1]).toContain('test2'); }); + test(`create can override default migration prefix`, async () => { + const syncer = fsSyncer(path.join(__dirname, 'generated/create-custom-prefix'), {}); + syncer.sync(); + + const umzug = new Umzug({ + migrations: { + glob: ['*.js', { cwd: syncer.baseDir }], + }, + logger: undefined, + create: { + folder: syncer.baseDir, + template: filepath => [[`${filepath}.js`, `/* custom template */`]], + prefix: () => 'a-custom-prefix', + }, + }); + + await umzug.create({ name: 'test' }); + const pending = paths(await umzug.pending()); + expect(pending).toHaveLength(1); + expect(path.basename(pending[0] ?? '')).toContain('a-custom-prefix.test.js'); + }); + describe(`resolve asynchronous context getter before the migrations run`, () => { const sleep = async (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); const getContext = async () => {