diff --git a/bin/cli.js b/bin/cli.js index 856dfb55..ab7e6032 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -93,7 +93,11 @@ function invoke(env) { 'environment, default: process.env.NODE_ENV || development' ) .option('--esm', 'Enable ESM interop.') - .option('--specific [path]', 'Specify one seed file to execute.'); + .option('--specific [path]', 'Specify one seed file to execute.') + .option( + '--timestamp-filename-prefix', + 'Enable a timestamp prefix on name of generated seed files.' + ); commander .command('init') @@ -300,6 +304,11 @@ function invoke(env) { `--stub [|]`, 'Specify the seed stub to use. If using the file must be located in config.seeds.directory' ) + .option( + '--timestamp-filename-prefix', + 'Enable a timestamp prefix on name of generated seed files.', + false + ) .action(async (name) => { const opts = commander.opts(); opts.client = opts.client || 'sqlite3'; // We don't really care about client when creating seeds @@ -311,6 +320,10 @@ function invoke(env) { configOverrides.stub = stub; } + if (opts.timestampFilenamePrefix) { + configOverrides.timestampFilenamePrefix = opts.timestampFilenamePrefix; + } + instance.seed .make(name, configOverrides) .then((name) => { diff --git a/lib/migrate/MigrationGenerator.js b/lib/migrate/MigrationGenerator.js index 24769a0e..bc2577be 100644 --- a/lib/migrate/MigrationGenerator.js +++ b/lib/migrate/MigrationGenerator.js @@ -2,6 +2,7 @@ const path = require('path'); const { writeJsFileUsingTemplate } = require('../util/template'); const { getMergedConfig } = require('./configuration-merger'); const { ensureDirectoryExists } = require('../util/fs'); +const { yyyymmddhhmmss } = require('../util/timestamp'); class MigrationGenerator { constructor(migrationConfig, logger) { @@ -79,24 +80,4 @@ class MigrationGenerator { } } -// Ensure that we have 2 places for each of the date segments. -function padDate(segment) { - segment = segment.toString(); - return segment[1] ? segment : `0${segment}`; -} - -// Get a date object in the correct format, without requiring a full out library -// like "moment.js". -function yyyymmddhhmmss() { - const d = new Date(); - return ( - d.getFullYear().toString() + - padDate(d.getMonth() + 1) + - padDate(d.getDate()) + - padDate(d.getHours()) + - padDate(d.getMinutes()) + - padDate(d.getSeconds()) - ); -} - module.exports = MigrationGenerator; diff --git a/lib/seed/Seeder.js b/lib/seed/Seeder.js index 45a1e7d0..a86bb292 100644 --- a/lib/seed/Seeder.js +++ b/lib/seed/Seeder.js @@ -7,6 +7,7 @@ const extend = require('lodash/extend'); const includes = require('lodash/includes'); const { ensureDirectoryExists, getFilepathsInFolder } = require('../util/fs'); const { writeJsFileUsingTemplate } = require('../util/template'); +const { yyyymmddhhmmss } = require('../util/timestamp'); const filterByLoadExtensions = (extensions) => (value) => { const extension = path.extname(value); @@ -103,7 +104,12 @@ class Seeder { _getNewStubFileName(name) { if (name[0] === '-') name = name.slice(1); - return name + '.' + this.config.extension; + + if (this.config.timestampFilenamePrefix === true) { + name = `${yyyymmddhhmmss()}_${name}`; + } + + return `${name}.${this.config.extension}`; } _getNewStubFilePath(name) { @@ -185,6 +191,7 @@ class Seeder { '.ls', '.ts', ], + timestampFilenamePrefix: false, sortDirsSeparately: false, recursive: false, }, diff --git a/lib/util/timestamp.js b/lib/util/timestamp.js new file mode 100644 index 00000000..637086aa --- /dev/null +++ b/lib/util/timestamp.js @@ -0,0 +1,16 @@ +// Get a date object in the correct format, without requiring a full out library +// like "moment.js". +function yyyymmddhhmmss() { + const d = new Date(); + + return ( + d.getFullYear().toString() + + (d.getMonth() + 1).toString().padStart(2, '0') + + d.getDate().toString().padStart(2, '0') + + d.getHours().toString().padStart(2, '0') + + d.getMinutes().toString().padStart(2, '0') + + d.getSeconds().toString().padStart(2, '0') + ); +} + +module.exports = { yyyymmddhhmmss }; diff --git a/test/cli/seed-make.spec.js b/test/cli/seed-make.spec.js index d93cad49..562af5e7 100644 --- a/test/cli/seed-make.spec.js +++ b/test/cli/seed-make.spec.js @@ -378,4 +378,70 @@ development: { ); }); }); + + describe('--timestamp-filename-prefix option: make seed with timestamp filename prefix', () => { + /** + * @type FileTestHelper + */ + let fileHelper; + + beforeEach(() => { + fileHelper = setupFileHelper(); + }); + + afterEach(() => { + fileHelper.cleanup(); + }); + + it('Creates a new seed using --timestamp-filename-prefix CLI flag', async () => { + const seedGlobPath = `${process.cwd()}/seeds/*_somename.js`; + fileHelper.registerGlobForCleanup(seedGlobPath); + + await execCommand( + `${NODE} ${KNEX} seed:make somename --timestamp-filename-prefix --knexpath=../knex.js`, + { + expectedOutput: 'Created seed file', + } + ); + + const fileCount = fileHelper.fileGlobExists(seedGlobPath); + + expect(fileCount).to.equal(1); + }); + + it('Creates a new seed using timestampFilenamePrefix parameter in knexfile', async () => { + const seedsDirectory = `${process.cwd()}/seeds`; + const seedGlobPath = `${seedsDirectory}/*_somename.js`; + fileHelper.registerGlobForCleanup(seedGlobPath); + + const knexfileContents = ` + module.exports = { + client: 'sqlite3', + connection: { + filename: __dirname + '/test/jake-util/test.sqlite3', + }, + seeds: { + directory: '${seedsDirectory}', + timestampFilenamePrefix: true + }, + };`; + + fileHelper.createFile( + path.join(process.cwd(), 'knexfile.js'), + knexfileContents, + { isPathAbsolute: true } + ); + + await execCommand( + `${NODE} ${KNEX} seed:make somename --knexpath=../knex.js`, + { + expectedOutput: 'Created seed file', + } + ); + + const fileCount = fileHelper.fileGlobExists(seedGlobPath); + + expect(fileCount).to.equal(1); + }); + }); }); diff --git a/types/index.d.ts b/types/index.d.ts index f61a4aa7..7234a7ac 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1968,6 +1968,7 @@ declare namespace Knex { directory?: string | string[]; loadExtensions?: readonly string[]; specific?: string; + timestampFilenamePrefix?: boolean; recursive?: boolean; sortDirsSeparately?: boolean; }